GXS.ShadowPlane.pas 11 KB

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