2
// This unit is part of the GLScene Engine https://github.com/glscene
5
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.
11
10/11/12 - PW - Added CPP compatibility: changed vector arrays to records
12
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
13
22/06/10 - Yar - Fixes after GLState revision
14
05/03/10 - DanB - More state added to TGLStateCache
15
22/02/10 - Yar - Optimization of switching states
16
12/03/09 - DanB - Bug-fix for scissor test on recent NVidia drivers
17
06/06/07 - DaStr - Added GLColor to uses (BugtrackerID = 1732211)
18
30/03/07 - DaStr - Added $I GLScene.inc
19
28/03/07 - DaStr - Renamed parameters in some methods
20
(thanks Burkhard Carstens) (Bugtracker ID = 1678658)
21
02/08/04 - LR, YHC - BCB corrections: use record instead array
22
23/03/04 - EG - Added spoTransparent
23
29/11/03 - EG - Scissors turned of if camera is withing bounding volume
24
30/10/02 - EG - Added OnBegin/EndRenderingShadows
25
25/10/02 - EG - Fixed Stencil cleanup and shadow projection bug
26
02/10/02 - EG - Added spoScissor
27
23/09/02 - EG - Creation (from GLMirror and Mattias FagerLund ShadowPlane.pas)
49
// TShadowPlaneOptions
51
TShadowPlaneOption = (spoUseStencil, spoScissor, spoTransparent, spoIgnoreZ);
52
TShadowPlaneOptions = set of TShadowPlaneOption;
55
cDefaultShadowPlaneOptions = [spoUseStencil, spoScissor];
61
{ A simple shadow plane.
62
This mirror requires a stencil buffer for optimal rendering!
63
The object is a mix between a plane and a proxy object, in that the plane
64
defines where the shadows are cast, while the proxy part is used to reference
65
the objects that should be shadowing (it is legal to self-shadow, but no
66
self-shadow visuals will be rendered).
67
If stenciling isn't used, the shadow will 'paint' the ShadowColor instead
68
of blending it transparently.
69
You can have lower quality shadow geometry: add a dummycube, set it to
70
invisible (so it won't be rendered in the "regular" pass), and under
71
it place another visible dummycube under which you have all your
72
low quality objects, use it as shadowing object. Apply the same movements
73
to the low-quality objects that you apply to the visible, high-quality ones.
75
TGLShadowPlane = class(TGLPlane)
79
FShadowingObject: TGLBaseSceneObject;
80
FShadowedLight: TGLLightSource;
81
FShadowColor: TGLColor;
82
FShadowOptions: TShadowPlaneOptions;
83
FOnBeginRenderingShadows, FOnEndRenderingShadows: TNotifyEvent;
87
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
88
procedure SetShadowingObject(const val: TGLBaseSceneObject);
89
procedure SetShadowedLight(const val: TGLLightSource);
90
procedure SetShadowColor(const val: TGLColor);
91
procedure SetShadowOptions(const val: TShadowPlaneOptions);
95
constructor Create(AOwner: TComponent); override;
96
destructor Destroy; override;
98
procedure DoRender(var ARci: TGLRenderContextInfo;
99
ARenderSelf, ARenderChildren: Boolean); override;
101
procedure Assign(Source: TPersistent); override;
105
{ Selects the object to mirror.
106
If nil, the whole scene is mirrored. }
107
property ShadowingObject: TGLBaseSceneObject read FShadowingObject write SetShadowingObject;
108
{ The light which casts shadows.
109
The light must be enabled otherwise shadows won't be cast. }
110
property ShadowedLight: TGLLightSource read FShadowedLight write SetShadowedLight;
111
{ The shadow's color.
112
This color is transparently blended to make shadowed area darker. }
113
property ShadowColor: TGLColor read FShadowColor write SetShadowColor;
115
{ Controls rendering options.
117
spoUseStencil: plane area is stenciled, prevents shadowing
118
objects to be visible on the sides of the mirror (stencil buffer
119
must be active in the viewer too). It also allows shadows to
120
be partial (blended).
121
spoScissor: plane area is 'scissored', this should improve
122
rendering speed by limiting rendering operations and fill rate,
123
may have adverse effects on old hardware in rare cases
124
spoTransparent: does not render the plane's material, may help
125
improve performance if you're fillrate limited, are using the
126
stencil, and your hardware has optimized stencil-only writes
129
property ShadowOptions: TShadowPlaneOptions read FShadowOptions write SetShadowOptions default cDefaultShadowPlaneOptions;
131
{ Fired before the shadows are rendered. }
132
property OnBeginRenderingShadows: TNotifyEvent read FOnBeginRenderingShadows write FOnBeginRenderingShadows;
133
{ Fired after the shadows are rendered. }
134
property OnEndRenderingShadows: TNotifyEvent read FOnEndRenderingShadows write FOnEndRenderingShadows;
137
//-------------------------------------------------------------
138
//-------------------------------------------------------------
139
//-------------------------------------------------------------
141
//-------------------------------------------------------------
142
//-------------------------------------------------------------
143
//-------------------------------------------------------------
144
uses OpenGLTokens, GLContext;
146
// ------------------ TGLShadowPlane ------------------
152
constructor TGLShadowPlane.Create(AOwner: Tcomponent);
154
cDefaultShadowColor: TColorVector = (X:0; Y:0; Z:0; W:0.5);
156
inherited Create(AOwner);
157
FShadowOptions := cDefaultShadowPlaneOptions;
158
ObjectStyle := ObjectStyle + [osDirectDraw];
159
FShadowColor := TGLColor.CreateInitialized(Self, cDefaultShadowColor);
165
destructor TGLShadowPlane.Destroy;
174
procedure TGLShadowPlane.DoRender(var ARci: TGLRenderContextInfo;
175
ARenderSelf, ARenderChildren: Boolean);
177
oldProxySubObject, oldIgnoreMaterials: Boolean;
180
CurrentBuffer: TGLSceneBuffer;
187
with ARci.GLStates do
189
oldProxySubObject := ARci.proxySubObject;
190
ARci.proxySubObject := True;
191
CurrentBuffer := TGLSceneBuffer(ARci.buffer);
193
if ARenderSelf and (VectorDotProduct(VectorSubtract(ARci.cameraPosition, AbsolutePosition), AbsoluteDirection) > 0) then
196
if (spoScissor in ShadowOptions)
197
and (PointDistance(ARci.cameraPosition) > BoundingSphereRadius) then
199
sr := ScreenRect(CurrentBuffer);
200
InflateGLRect(sr, 1, 1);
201
IntersectGLRect(sr, GLRect(0, 0, ARci.viewPortSize.cx, ARci.viewPortSize.cy));
202
GL.Scissor(sr.Left, sr.Top, sr.Right - sr.Left, sr.Bottom - sr.Top);
203
Enable(stScissorTest);
206
if (spoUseStencil in ShadowOptions) then
208
StencilClearValue := 0;
209
GL.Clear(GL_STENCIL_BUFFER_BIT);
210
Enable(stStencilTest);
211
SetStencilFunc(cfAlways, 1, 1);
212
SetStencilOp(soReplace, soReplace, soReplace);
215
// "Render" plane and stencil mask
216
if (spoTransparent in ShadowOptions) then
218
SetGLColorWriting(False);
219
DepthWriteMask := False;
221
SetGLColorWriting(True);
225
Material.Apply(ARci);
228
until not Material.UnApply(ARci);
231
// Setup depth options
232
if spoIgnoreZ in ShadowOptions then
236
DepthFunc := cfLEqual;
238
if Assigned(FShadowedLight) then
241
ARci.PipelineTransformation.Push;
243
case ShadowedLight.LightStyle of
246
shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
247
VectorScale(ShadowedLight.SpotDirection.AsVector, 1e10));
250
shadowMat := MakeShadowMatrix(AbsolutePosition, AbsoluteDirection,
251
ShadowedLight.AbsolutePosition);
254
ARci.PipelineTransformation.ViewMatrix := MatrixMultiply(
256
ARci.PipelineTransformation.ViewMatrix);
257
ARci.PipelineTransformation.ModelMatrix := IdentityHmgMatrix;
261
SetPolygonOffset(-1, -1);
262
Enable(stPolygonOffsetFill);
264
oldIgnoreMaterials := ARci.ignoreMaterials;
265
ARci.ignoreMaterials := True;
266
ActiveTextureEnabled[ttTexture2D] := False;
270
GL.Color4fv(ShadowColor.AsAddress);
272
if (spoUseStencil in ShadowOptions) then
275
Disable(stAlphaTest);
276
SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
277
SetStencilFunc(cfEqual, 1, 1);
278
SetStencilOp(soKeep, soKeep, soZero);
283
if Assigned(FOnBeginRenderingShadows) then
284
FOnBeginRenderingShadows(Self);
285
if Assigned(FShadowingObject) then
287
ModelMat := IdentityHmgMatrix;
288
if FShadowingObject.Parent <> nil then
289
MatrixMultiply(ModelMat, FShadowingObject.Parent.AbsoluteMatrix, ModelMat);
290
MatrixMultiply(ModelMat, FShadowingObject.LocalMatrix^, ModelMat);
291
ARci.PipelineTransformation.ModelMatrix := ModelMat;
292
FShadowingObject.DoRender(ARci, True, (FShadowingObject.Count > 0));
296
Scene.Objects.DoRender(ARci, True, True);
298
if Assigned(FOnEndRenderingShadows) then
299
FOnEndRenderingShadows(Self);
301
ARci.ignoreMaterials := oldIgnoreMaterials;
303
// Restore to "normal"
304
ARci.PipelineTransformation.Pop;
307
Disable(stStencilTest);
308
Disable(stScissorTest);
309
Disable(stPolygonOffsetFill);
312
ARci.proxySubObject := oldProxySubObject;
314
if ARenderChildren and (Count > 0) then
315
Self.RenderChildren(0, Count - 1, ARci);
325
procedure TGLShadowPlane.Notification(AComponent: TComponent; Operation: TOperation);
327
if Operation = opRemove then
329
if AComponent = FShadowingObject then
330
ShadowingObject := nil
331
else if AComponent = FShadowedLight then
332
ShadowedLight := nil;
340
procedure TGLShadowPlane.SetShadowingObject(const val: TGLBaseSceneObject);
342
if FShadowingObject <> val then
344
if Assigned(FShadowingObject) then
345
FShadowingObject.RemoveFreeNotification(Self);
346
FShadowingObject := val;
347
if Assigned(FShadowingObject) then
348
FShadowingObject.FreeNotification(Self);
356
procedure TGLShadowPlane.SetShadowedLight(const val: TGLLightSource);
358
if FShadowedLight <> val then
360
if Assigned(FShadowedLight) then
361
FShadowedLight.RemoveFreeNotification(Self);
362
FShadowedLight := val;
363
if Assigned(FShadowedLight) then
364
FShadowedLight.FreeNotification(Self);
372
procedure TGLShadowPlane.SetShadowColor(const val: TGLColor);
374
FShadowColor.Assign(val);
380
procedure TGLShadowPlane.Assign(Source: TPersistent);
382
if Assigned(Source) and (Source is TGLShadowPlane) then
384
FShadowOptions := TGLShadowPlane(Source).FShadowOptions;
385
ShadowingObject := TGLShadowPlane(Source).ShadowingObject;
386
ShadowedLight := TGLShadowPlane(Source).ShadowedLight;
387
ShadowColor := TGLShadowPlane(Source).ShadowColor;
389
inherited Assign(Source);
395
procedure TGLShadowPlane.SetShadowOptions(const val: TShadowPlaneOptions);
397
if FShadowOptions <> val then
399
FShadowOptions := val;
404
//-------------------------------------------------------------
405
//-------------------------------------------------------------
406
//-------------------------------------------------------------
408
//-------------------------------------------------------------
409
//-------------------------------------------------------------
410
//-------------------------------------------------------------
412
RegisterClasses([TGLShadowPlane]);