GLS.ShadowPlane.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.ShadowPlane;
  5. (*
  6. Implements a basic shadow plane.
  7. It is strongly recommended to read and understand the explanations
  8. in the materials/mirror demo before using this component.
  9. *)
  10. interface
  11. {$I GLScene.inc}
  12. uses
  13. Winapi.OpenGL,
  14. System.Classes,
  15. System.Types,
  16. GLS.OpenGLTokens,
  17. GLS.PersistentClasses,
  18. GLS.PipelineTransformation,
  19. GLS.VectorTypes,
  20. GLS.Scene,
  21. GLS.VectorGeometry,
  22. GLS.Objects,
  23. GLS.Color,
  24. GLS.RenderContextInfo,
  25. GLS.State,
  26. GLS.TextureFormat,
  27. GLS.Context,
  28. GLS.Material,
  29. GLS.Texture,
  30. GLS.Utils;
  31. type
  32. TShadowPlaneOption = (spoUseStencil, spoScissor, spoTransparent, spoIgnoreZ);
  33. TShadowPlaneOptions = set of TShadowPlaneOption;
  34. const
  35. cDefaultShadowPlaneOptions = [spoUseStencil, spoScissor];
  36. type
  37. (*A simple shadow plane.
  38. This mirror requires a stencil buffer for optimal rendering!
  39. The object is a mix between a plane and a proxy object, in that the plane
  40. defines where the shadows are cast, while the proxy part is used to reference
  41. the objects that should be shadowing (it is legal to self-shadow, but no
  42. self-shadow visuals will be rendered).
  43. If stenciling isn't used, the shadow will 'paint' the ShadowColor instead
  44. of blending it transparently.
  45. You can have lower quality shadow geometry: add a dummycube, set it to
  46. invisible (so it won't be rendered in the "regular" pass), and under
  47. it place another visible dummycube under which you have all your
  48. low quality objects, use it as shadowing object. Apply the same movements
  49. to the low-quality objects that you apply to the visible, high-quality ones *)
  50. TGLShadowPlane = class(TGLPlane)
  51. private
  52. FRendering: Boolean;
  53. FShadowingObject: TGLBaseSceneObject;
  54. FShadowedLight: TGLLightSource;
  55. FShadowColor: TGLColor;
  56. FShadowOptions: TShadowPlaneOptions;
  57. FOnBeginRenderingShadows, FOnEndRenderingShadows: TNotifyEvent;
  58. protected
  59. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  60. procedure SetShadowingObject(const val: TGLBaseSceneObject);
  61. procedure SetShadowedLight(const val: TGLLightSource);
  62. procedure SetShadowColor(const val: TGLColor);
  63. procedure SetShadowOptions(const val: TShadowPlaneOptions);
  64. public
  65. constructor Create(AOwner: TComponent); override;
  66. destructor Destroy; override;
  67. procedure DoRender(var ARci: TGLRenderContextInfo;
  68. ARenderSelf, ARenderChildren: Boolean); override;
  69. procedure Assign(Source: TPersistent); override;
  70. published
  71. // Selects the object to mirror.If nil, the whole scene is mirrored.
  72. property ShadowingObject: TGLBaseSceneObject read FShadowingObject write SetShadowingObject;
  73. // The light which casts shadows. It must be enabled otherwise shadows won't be cast
  74. property ShadowedLight: TGLLightSource read FShadowedLight write SetShadowedLight;
  75. //The shadow's color. This color is transparently blended to make shadowed area darker
  76. property ShadowColor: TGLColor read FShadowColor write SetShadowColor;
  77. (* Controls rendering options:
  78. spoUseStencil: plane area is stenciled, prevents shadowing
  79. objects to be visible on the sides of the mirror (stencil buffer
  80. must be active in the viewer too). It also allows shadows to
  81. be partial (blended).
  82. spoScissor: plane area is 'scissored', this should improve
  83. rendering speed by limiting rendering operations and fill rate,
  84. may have adverse effects on old hardware in rare cases
  85. spoTransparent: does not render the plane's material, may help
  86. improve performance if you're fillrate limited, are using the
  87. stencil, and your hardware has optimized stencil-only writes *)
  88. property ShadowOptions: TShadowPlaneOptions read FShadowOptions write SetShadowOptions default cDefaultShadowPlaneOptions;
  89. //Fired before the shadows are rendered
  90. property OnBeginRenderingShadows: TNotifyEvent read FOnBeginRenderingShadows write FOnBeginRenderingShadows;
  91. //Fired after the shadows are rendered
  92. property OnEndRenderingShadows: TNotifyEvent read FOnEndRenderingShadows write FOnEndRenderingShadows;
  93. end;
  94. //-------------------------------------------------------------
  95. implementation
  96. //-------------------------------------------------------------
  97. // ------------------
  98. // ------------------ TGLShadowPlane ------------------
  99. // ------------------
  100. constructor TGLShadowPlane.Create(AOwner: Tcomponent);
  101. const
  102. cDefaultShadowColor: TGLColorVector = (X:0; Y:0; Z:0; W:0.5);
  103. begin
  104. inherited Create(AOwner);
  105. FShadowOptions := cDefaultShadowPlaneOptions;
  106. ObjectStyle := ObjectStyle + [osDirectDraw];
  107. FShadowColor := TGLColor.CreateInitialized(Self, cDefaultShadowColor);
  108. end;
  109. destructor TGLShadowPlane.Destroy;
  110. begin
  111. inherited;
  112. FShadowColor.Free;
  113. end;
  114. procedure TGLShadowPlane.DoRender(var ARci: TGLRenderContextInfo;
  115. ARenderSelf, ARenderChildren: Boolean);
  116. var
  117. oldProxySubObject, oldIgnoreMaterials: Boolean;
  118. shadowMat: TGLMatrix;
  119. sr, ds: TRect;
  120. CurrentBuffer: TGLSceneBuffer;
  121. ModelMat: TGLMatrix;
  122. begin
  123. if FRendering then
  124. Exit;
  125. FRendering := True;
  126. try
  127. with ARci.GLStates do
  128. begin
  129. oldProxySubObject := ARci.proxySubObject;
  130. ARci.proxySubObject := True;
  131. CurrentBuffer := TGLSceneBuffer(ARci.buffer);
  132. if ARenderSelf and (VectorDotProduct(VectorSubtract(ARci.cameraPosition, AbsolutePosition), AbsoluteDirection) > 0) then
  133. begin
  134. if (spoScissor in ShadowOptions)
  135. and (PointDistance(ARci.cameraPosition) > BoundingSphereRadius) then
  136. begin
  137. sr := ScreenRect(CurrentBuffer);
  138. InflateGLRect(sr, 1, 1);
  139. ds := GetGLRect(0, 0, ARci.viewPortSize.cx, ARci.viewPortSize.cy);
  140. IntersectGLRect(sr, ds);
  141. gl.Scissor(sr.Left, sr.Top, sr.Right - sr.Left, sr.Bottom - sr.Top);
  142. Enable(stScissorTest);
  143. end;
  144. if (spoUseStencil in ShadowOptions) then
  145. begin
  146. StencilClearValue := 0;
  147. gl.Clear(GL_STENCIL_BUFFER_BIT);
  148. Enable(stStencilTest);
  149. SetStencilFunc(cfAlways, 1, 1);
  150. SetStencilOp(soReplace, soReplace, soReplace);
  151. end;
  152. // "Render" plane and stencil mask
  153. if (spoTransparent in ShadowOptions) then
  154. begin
  155. SetGLColorWriting(False);
  156. DepthWriteMask := False;
  157. BuildList(ARci);
  158. SetGLColorWriting(True);
  159. end
  160. else
  161. begin
  162. Material.Apply(ARci);
  163. repeat
  164. BuildList(ARci);
  165. until not Material.UnApply(ARci);
  166. end;
  167. // Setup depth options
  168. if spoIgnoreZ in ShadowOptions then
  169. Disable(stDepthTest)
  170. else
  171. Enable(stDepthTest);
  172. DepthFunc := cfLEqual;
  173. if Assigned(FShadowedLight) then
  174. begin
  175. ARci.PipelineTransformation.Push;
  176. case ShadowedLight.LightStyle of
  177. lsParallel:
  178. begin
  179. shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
  180. VectorScale(ShadowedLight.SpotDirection.AsVector, 1e10));
  181. end;
  182. else
  183. shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
  184. ShadowedLight.AbsolutePosition);
  185. end;
  186. ARci.PipelineTransformation.SetViewMatrix(MatrixMultiply(
  187. shadowMat,
  188. ARci.PipelineTransformation.ViewMatrix^));
  189. ARci.PipelineTransformation.SetModelMatrix(IdentityHmgMatrix);
  190. Disable(stCullFace);
  191. Enable(stNormalize);
  192. SetPolygonOffset(-1, -1);
  193. Enable(stPolygonOffsetFill);
  194. oldIgnoreMaterials := ARci.ignoreMaterials;
  195. ARci.ignoreMaterials := True;
  196. ActiveTextureEnabled[ttTexture2D] := False;
  197. Disable(stLighting);
  198. Disable(stFog);
  199. gl.Color4fv(ShadowColor.AsAddress);
  200. if (spoUseStencil in ShadowOptions) then
  201. begin
  202. Enable(stBlend);
  203. Disable(stAlphaTest);
  204. SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
  205. SetStencilFunc(cfEqual, 1, 1);
  206. SetStencilOp(soKeep, soKeep, soZero);
  207. end
  208. else
  209. Disable(stBlend);
  210. if Assigned(FOnBeginRenderingShadows) then
  211. FOnBeginRenderingShadows(Self);
  212. if Assigned(FShadowingObject) then
  213. begin
  214. ModelMat := IdentityHmgMatrix;
  215. if FShadowingObject.Parent <> nil then
  216. MatrixMultiply(ModelMat, FShadowingObject.Parent.AbsoluteMatrix, ModelMat);
  217. MatrixMultiply(ModelMat, FShadowingObject.LocalMatrix^, ModelMat);
  218. ARci.PipelineTransformation.SetModelMatrix(ModelMat);
  219. FShadowingObject.DoRender(ARci, True, (FShadowingObject.Count > 0));
  220. end
  221. else
  222. begin
  223. Scene.Objects.DoRender(ARci, True, True);
  224. end;
  225. if Assigned(FOnEndRenderingShadows) then
  226. FOnEndRenderingShadows(Self);
  227. ARci.ignoreMaterials := oldIgnoreMaterials;
  228. // Restore to "normal"
  229. ARci.PipelineTransformation.Pop;
  230. end;
  231. Disable(stStencilTest);
  232. Disable(stScissorTest);
  233. Disable(stPolygonOffsetFill);
  234. end;
  235. ARci.proxySubObject := oldProxySubObject;
  236. if ARenderChildren and (Count > 0) then
  237. Self.RenderChildren(0, Count - 1, ARci);
  238. end;
  239. finally
  240. FRendering := False;
  241. end;
  242. end;
  243. procedure TGLShadowPlane.Notification(AComponent: TComponent; Operation: TOperation);
  244. begin
  245. if Operation = opRemove then
  246. begin
  247. if AComponent = FShadowingObject then
  248. ShadowingObject := nil
  249. else if AComponent = FShadowedLight then
  250. ShadowedLight := nil;
  251. end;
  252. inherited;
  253. end;
  254. procedure TGLShadowPlane.SetShadowingObject(const val: TGLBaseSceneObject);
  255. begin
  256. if FShadowingObject <> val then
  257. begin
  258. if Assigned(FShadowingObject) then
  259. FShadowingObject.RemoveFreeNotification(Self);
  260. FShadowingObject := val;
  261. if Assigned(FShadowingObject) then
  262. FShadowingObject.FreeNotification(Self);
  263. NotifyChange(Self);
  264. end;
  265. end;
  266. procedure TGLShadowPlane.SetShadowedLight(const val: TGLLightSource);
  267. begin
  268. if FShadowedLight <> val then
  269. begin
  270. if Assigned(FShadowedLight) then
  271. FShadowedLight.RemoveFreeNotification(Self);
  272. FShadowedLight := val;
  273. if Assigned(FShadowedLight) then
  274. FShadowedLight.FreeNotification(Self);
  275. NotifyChange(Self);
  276. end;
  277. end;
  278. procedure TGLShadowPlane.SetShadowColor(const val: TGLColor);
  279. begin
  280. FShadowColor.Assign(val);
  281. end;
  282. procedure TGLShadowPlane.Assign(Source: TPersistent);
  283. begin
  284. if Assigned(Source) and (Source is TGLShadowPlane) then
  285. begin
  286. FShadowOptions := TGLShadowPlane(Source).FShadowOptions;
  287. ShadowingObject := TGLShadowPlane(Source).ShadowingObject;
  288. ShadowedLight := TGLShadowPlane(Source).ShadowedLight;
  289. ShadowColor := TGLShadowPlane(Source).ShadowColor;
  290. end;
  291. inherited Assign(Source);
  292. end;
  293. procedure TGLShadowPlane.SetShadowOptions(const val: TShadowPlaneOptions);
  294. begin
  295. if FShadowOptions <> val then
  296. begin
  297. FShadowOptions := val;
  298. NotifyChange(Self);
  299. end;
  300. end;
  301. //-------------------------------------------------------------
  302. initialization
  303. //-------------------------------------------------------------
  304. RegisterClasses([TGLShadowPlane]);
  305. end.