2
// This unit is part of the GLScene Engine https://github.com/glscene
5
Implements basic shadow volumes support.
7
Be aware that only objects that support silhouette determination have a chance
8
to cast correct shadows. Transparent/blended/shader objects among the receivers
9
or the casters will be rendered incorrectly.
12
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
13
31/05/10 - Yar - Fixes forLinux x64
14
01/05/10 - Yar - Moved ignoreBlendingRequests and ignoreDepthRequests behind RenderChildren
15
22/04/10 - Yar - Fixes after GLState revision
16
05/03/10 - DanB - More state added to TGLStateCache
17
31/03/07 - DaStr - Fixed issue with invalid typecasting
18
(thanks Burkhard Carstens) (Bugtracker ID = 1692016)
19
30/03/07 - DaStr - Added $I GLScene.inc
20
28/03/07 - DaStr - Renamed parameters in some methods
21
(thanks Burkhard Carstens) (Bugtracker ID = 1678658)
22
08/12/04 - DB - Fixed bug in TGLShadowVolumeCaster.SetCaster
23
02/12/04 - MF - Added some documentation
24
23/03/04 - EG - Added Active property
25
29/11/03 - MF - Removed a "feature" that would draw the shadow of
26
(hierarchially) invisible objects
27
27/11/03 - MF - TGLShadowVolumeCaster now registers with the FCaster
28
for delete notification
29
11/06/03 - EG - Added silhouette cache
30
04/06/03 - EG - Creation (based on code from Mattias Fagerlund)
41
GLScene, GLVectorGeometry, OpenGLTokens, GLContext, GLSilhouette,
42
GLCrossPlatform, GLPersistentClasses, GLGeometryBB, GLColor,
47
TGLShadowVolume = class;
49
{ Determines when a shadow volume should generate a cap at the beginning and
50
end of the volume. This is ONLY necessary when there's a chance that the
51
camera could end up inside the shadow _or_ between the light source and
52
the camera. If those two situations can't occur then not using capping is
54
Note that if you use the capping, you must either set the depth of view of
55
your camera to something very large (f.i. 1e9), or you could use the infinite
56
mode (csInfinitePerspective) of your camera.
58
svcDefault : Default behaviour
59
svcAlways : Always generates caps
60
svcNever : Never generates caps
63
TGLShadowVolumeCapping = (svcDefault, svcAlways, svcNever);
65
{ Determines when a caster should actually produce a shadow;
67
scmAlways : Caster always produces a shadow, ignoring visibility
68
scmVisible : Caster casts shadow if the object has visible=true
69
scmRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
71
scmParentVisible : Caster produces shadow if parent has visible=true
72
scmParentRecursivelyVisible : Caster casts shadow if ancestors up the hierarchy
73
all have visible=true, starting from the parent (ignoring own visible setting)
76
TGLShadowCastingMode = (scmAlways, scmVisible, scmRecursivelyVisible,
77
scmParentVisible, scmParentRecursivelyVisible);
79
// TGLShadowVolumeCaster
81
{ Specifies an individual shadow caster.
82
Can be a light or an opaque object. }
83
TGLShadowVolumeCaster = class(TCollectionItem)
86
FCaster: TGLBaseSceneObject;
87
FEffectiveRadius: Single;
88
FCapping: TGLShadowVolumeCapping;
89
FCastingMode: TGLShadowCastingMode;
93
procedure SetCaster(const val: TGLBaseSceneObject);
94
function GetGLShadowVolume: TGLShadowVolume;
96
procedure RemoveNotification(aComponent: TComponent);
97
function GetDisplayName: string; override;
101
constructor Create(ACollection: TCollection); override;
102
destructor Destroy; override;
104
procedure Assign(Source: TPersistent); override;
106
{ Shadow casting object.
107
Can be an opaque object or a lightsource. }
108
property Caster: TGLBaseSceneObject read FCaster write SetCaster;
110
property GLShadowVolume: TGLShadowVolume read GetGLShadowVolume;
115
{ Radius beyond which the caster can be ignored.
116
Zero (default value) means the caster can never be ignored. }
117
property EffectiveRadius: Single read FEffectiveRadius write
119
{ Specifies if the shadow volume should be capped.
120
Capping helps solve shadowing artefacts, at the cost of performance. }
121
property Capping: TGLShadowVolumeCapping read FCapping write FCapping default
123
{ Determines when an object should cast a shadow or not. Typically, objects
124
should only cast shadows when recursively visible. But if you're using
125
dummy shadow casters which are less complex than their parent objects,
126
you should use scmParentRecursivelyVisible.}
127
property CastingMode: TGLShadowCastingMode read FCastingMode write
128
FCastingMode default scmRecursivelyVisible;
131
// TGLShadowVolumeOccluder
133
{ Specifies an individual shadow casting occluder. }
134
TGLShadowVolumeOccluder = class(TGLShadowVolumeCaster)
140
// TGLShadowVolumeLight
142
{ Specifies an individual shadow casting light. }
143
TGLShadowVolumeLight = class(TGLShadowVolumeCaster)
146
FSilhouettes: TPersistentObjectList;
150
function GetLightSource: TGLLightSource;
151
procedure SetLightSource(const ls: TGLLightSource);
153
function GetCachedSilhouette(AIndex: Integer): TGLSilhouette;
154
procedure StoreCachedSilhouette(AIndex: Integer; ASil: TGLSilhouette);
156
{ Compute and setup scissor clipping rect for the light.
157
Returns true if a scissor rect was setup }
158
function SetupScissorRect(worldAABB: PAABB; var rci: TGLRenderContextInfo):
163
constructor Create(ACollection: TCollection); override;
164
destructor Destroy; override;
166
procedure FlushSilhouetteCache;
170
{ Shadow casting lightsource. }
171
property LightSource: TGLLightSource read GetLightSource write
176
// TGLShadowVolumeCasters
178
{ Collection of TGLShadowVolumeCaster. }
179
TGLShadowVolumeCasters = class(TOwnedCollection)
185
function GetItems(index: Integer): TGLShadowVolumeCaster;
186
procedure RemoveNotification(aComponent: TComponent);
190
function AddCaster(obj: TGLBaseSceneObject; effectiveRadius: Single = 0;
191
CastingMode: TGLShadowCastingMode = scmRecursivelyVisible):
192
TGLShadowVolumeCaster;
193
procedure RemoveCaster(obj: TGLBaseSceneObject);
194
function IndexOfCaster(obj: TGLBaseSceneObject): Integer;
196
property Items[index: Integer]: TGLShadowVolumeCaster read GetItems;
200
// TGLShadowVolumeOption
202
{ Shadow volume rendering options/optimizations.
204
svoShowVolumes : make the shadow volumes visible
205
svoDesignVisible : the shadow are visible at design-time
206
svoCacheSilhouettes : cache shadow volume silhouettes, beneficial when
207
some objects are static relatively to their light(s)
208
svoScissorClips : use scissor clipping per light, beneficial when
209
lights are attenuated and don't illuminate the whole scene
210
svoWorldScissorClip : use scissor clipping for the world, beneficial
211
when shadow receivers don't cover the whole viewer surface
213
TGLShadowVolumeOption = (svoShowVolumes, svoCacheSilhouettes, svoScissorClips,
214
svoWorldScissorClip, svoDesignVisible);
215
TGLShadowVolumeOptions = set of TGLShadowVolumeOption;
217
// TGLShadowVolumeMode
219
{ Shadow rendering modes.
221
svmAccurate : will render the scene with ambient lighting only, then
222
for each light will make a diffuse+specular pass
223
svmDarkening : renders the scene with lighting on as usual, then darkens
224
shadowed areas (i.e. inaccurate lighting, but will "shadow" objects
225
that don't honour to diffuse or specular lighting)
226
svmOff : no shadowing will take place
228
TGLShadowVolumeMode = (svmAccurate, svmDarkening, svmOff);
232
{ Simple shadow volumes.
233
Shadow receiving objects are the ShadowVolume's children, shadow casters
234
(opaque objects or lights) must be explicitly specified in the Casters
236
Shadow volumes require that the buffer allows stencil buffers,
237
GLSceneViewer.Buffer.ContextOptions contain roStencinBuffer. Without stencil
238
buffers, shadow volumes will not work properly.
239
Another issue to look out for is the fact that shadow volume capping requires
240
that the camera depth of view is either very high (fi 1e9) or that the
241
camera style is csInfinitePerspective.
243
TGLShadowVolume = class(TGLImmaterialSceneObject)
248
FLights: TGLShadowVolumeCasters;
249
FOccluders: TGLShadowVolumeCasters;
250
FCapping: TGLShadowVolumeCapping;
251
FOptions: TGLShadowVolumeOptions;
252
FMode: TGLShadowVolumeMode;
253
FDarkeningColor: TGLColor;
257
procedure Notification(AComponent: TComponent; Operation: TOperation);
260
procedure SetActive(const val: Boolean);
261
procedure SetLights(const val: TGLShadowVolumeCasters);
262
procedure SetOccluders(const val: TGLShadowVolumeCasters);
263
procedure SetOptions(const val: TGLShadowVolumeOptions);
264
procedure SetMode(const val: TGLShadowVolumeMode);
265
procedure SetDarkeningColor(const val: TGLColor);
269
constructor Create(AOwner: TComponent); override;
270
destructor Destroy; override;
272
procedure DoRender(var ARci: TGLRenderContextInfo;
273
ARenderSelf, ARenderChildren: Boolean); override;
275
procedure Assign(Source: TPersistent); override;
277
procedure FlushSilhouetteCache;
281
{ Determines if shadow volume rendering is active.
282
When set to false, children will be rendered without any shadowing
283
or multipass lighting. }
284
property Active: Boolean read FActive write SetActive default True;
285
{ Lights that cast shadow volumes. }
286
property Lights: TGLShadowVolumeCasters read FLights write SetLights;
287
{ Occluders that cast shadow volumes. }
288
property Occluders: TGLShadowVolumeCasters read FOccluders write
291
{ Specifies if the shadow volume should be capped.
292
Capping helps solve shadowing artefacts, at the cost of performance. }
293
property Capping: TGLShadowVolumeCapping read FCapping write FCapping default
295
{ Shadow volume rendering options. }
296
property Options: TGLShadowVolumeOptions read FOptions write SetOptions
297
default [svoCacheSilhouettes, svoScissorClips];
298
{ Shadow rendering mode. }
299
property Mode: TGLShadowVolumeMode read FMode write SetMode default
301
{ Darkening color used in svmDarkening mode. }
302
property DarkeningColor: TGLColor read FDarkeningColor write
306
//-------------------------------------------------------------
307
//-------------------------------------------------------------
308
//-------------------------------------------------------------
310
//-------------------------------------------------------------
311
//-------------------------------------------------------------
312
//-------------------------------------------------------------
321
// ------------------ TGLShadowVolumeCaster ------------------
327
constructor TGLShadowVolumeCaster.Create(ACollection: TCollection);
329
inherited Create(ACollection);
330
FCapping := svcDefault;
331
FCastingMode := scmRecursivelyVisible;
335
// Required for Delphi 5 support.
336
THackOwnedCollection = class(TOwnedCollection);
341
function TGLShadowVolumeCaster.GetGLShadowVolume: TGLShadowVolume;
343
Result := TGLShadowVolume(THackOwnedCollection(Collection).GetOwner);
349
destructor TGLShadowVolumeCaster.Destroy;
351
if Assigned(FCaster) then
352
FCaster.RemoveFreeNotification(GLShadowVolume);
359
procedure TGLShadowVolumeCaster.Assign(Source: TPersistent);
361
if Source is TGLShadowVolumeCaster then
363
FCaster := TGLShadowVolumeCaster(Source).FCaster;
364
FEffectiveRadius := TGLShadowVolumeCaster(Source).FEffectiveRadius;
365
FCapping := TGLShadowVolumeCaster(Source).FCapping;
366
GetGLShadowVolume.StructureChanged;
374
procedure TGLShadowVolumeCaster.SetCaster(const val: TGLBaseSceneObject);
376
if FCaster <> val then
378
if FCaster <> nil then
379
FCaster.RemoveFreeNotification(GLShadowVolume);
381
if FCaster <> nil then
382
FCaster.FreeNotification(GLShadowVolume);
383
GetGLShadowVolume.StructureChanged;
390
procedure TGLShadowVolumeCaster.RemoveNotification(aComponent: TComponent);
392
if aComponent = FCaster then
394
// No point in keeping the TGLShadowVolumeCaster once the FCaster has been
404
function TGLShadowVolumeCaster.GetDisplayName: string;
406
if Assigned(FCaster) then
408
if FCaster is TGLLightSource then
411
Result := '[Object]';
412
Result := Result + ' ' + FCaster.Name;
413
if EffectiveRadius > 0 then
414
Result := Result + Format(' (%.1f)', [EffectiveRadius]);
421
// ------------------ TGLShadowVolumeLight ------------------
427
constructor TGLShadowVolumeLight.Create(ACollection: TCollection);
429
inherited Create(ACollection);
430
FSilhouettes := TPersistentObjectList.Create;
436
destructor TGLShadowVolumeLight.Destroy;
438
FlushSilhouetteCache;
443
// FlushSilhouetteCache
446
procedure TGLShadowVolumeLight.FlushSilhouetteCache;
454
function TGLShadowVolumeLight.GetLightSource: TGLLightSource;
456
Result := TGLLightSource(Caster);
462
procedure TGLShadowVolumeLight.SetLightSource(const ls: TGLLightSource);
467
// GetCachedSilhouette
470
function TGLShadowVolumeLight.GetCachedSilhouette(AIndex: Integer):
473
if AIndex < FSilhouettes.Count then
474
Result := TGLSilhouette(FSilhouettes[AIndex])
479
// StoreCachedSilhouette
482
procedure TGLShadowVolumeLight.StoreCachedSilhouette(AIndex: Integer; ASil:
485
while AIndex >= FSilhouettes.Count do
486
FSilhouettes.Add(nil);
487
if ASil <> FSilhouettes[AIndex] then
489
if assigned(FSilhouettes[AIndex]) then
490
FSilhouettes[AIndex].Free;
491
FSilhouettes[AIndex] := ASil;
495
// TGLShadowVolumeLight
498
function TGLShadowVolumeLight.SetupScissorRect(worldAABB: PAABB; var rci:
499
TGLRenderContextInfo): Boolean;
507
if (EffectiveRadius <= 0) or (not ls.Attenuated) then
509
// non attenuated lights can't be clipped
510
if not Assigned(worldAABB) then
520
aabb := BSphereToAABB(ls.AbsolutePosition, EffectiveRadius);
521
if Assigned(worldAABB) then
522
aabb := AABBIntersection(aabb, worldAABB^);
525
if PointInAABB(rci.cameraPosition, aabb) then
527
// camera inside light volume radius, can't clip
532
// Calculate the window-space bounds of the light's bounding box.
533
mvp := rci.PipelineTransformation.ViewProjectionMatrix;
535
clipRect := AABBToClipRect(aabb, mvp, rci.viewPortSize.cx,
536
rci.viewPortSize.cy);
538
if (clipRect.Right < 0) or (clipRect.Left > rci.viewPortSize.cx)
539
or (clipRect.Top < 0) or (clipRect.Bottom > rci.viewPortSize.cy) then
546
GL.Scissor(Round(Left), Round(Top), Round(Right - Left), Round(Bottom -
552
// ------------------ TGLShadowVolumeCasters ------------------
558
procedure TGLShadowVolumeCasters.RemoveNotification(aComponent: TComponent);
562
for i := Count - 1 downto 0 do
563
Items[i].RemoveNotification(aComponent);
569
function TGLShadowVolumeCasters.GetItems(index: Integer): TGLShadowVolumeCaster;
571
Result := TGLShadowVolumeCaster(inherited Items[index]);
577
function TGLShadowVolumeCasters.AddCaster(obj: TGLBaseSceneObject;
578
effectiveRadius: Single = 0;
579
CastingMode: TGLShadowCastingMode = scmRecursivelyVisible):
580
TGLShadowVolumeCaster;
582
newCaster: TGLShadowVolumeCaster;
584
newCaster := TGLShadowVolumeCaster(Add);
585
newCaster.Caster := obj;
586
newCaster.EffectiveRadius := effectiveRadius;
587
newCaster.CastingMode := CastingMode;
595
procedure TGLShadowVolumeCasters.RemoveCaster(obj: TGLBaseSceneObject);
599
i := IndexOfCaster(obj);
607
function TGLShadowVolumeCasters.IndexOfCaster(obj: TGLBaseSceneObject): Integer;
611
for i := 0 to Count - 1 do
613
if Items[i].Caster = obj then
623
// ------------------ TGLShadowVolume ------------------
629
constructor TGLShadowVolume.Create(AOwner: Tcomponent);
631
inherited Create(AOwner);
632
ObjectStyle := ObjectStyle - [osDirectDraw] + [osNoVisibilityCulling];
634
FLights := TGLShadowVolumeCasters.Create(self, TGLShadowVolumeLight);
635
FOccluders := TGLShadowVolumeCasters.Create(self, TGLShadowVolumeOccluder);
636
FCapping := svcAlways;
637
FMode := svmAccurate;
638
FOptions := [svoCacheSilhouettes, svoScissorClips];
639
FDarkeningColor := TGLColor.CreateInitialized(Self, VectorMake(0, 0, 0, 0.5));
645
destructor TGLShadowVolume.Destroy;
648
FDarkeningColor.Free;
656
procedure TGLShadowVolume.Notification(AComponent: TComponent; Operation:
659
if Operation = opRemove then
661
FLights.RemoveNotification(AComponent);
662
FOccluders.RemoveNotification(AComponent);
670
procedure TGLShadowVolume.Assign(Source: TPersistent);
672
if Assigned(Source) and (Source is TGLShadowVolume) then
674
FLights.Assign(TGLShadowVolume(Source).Lights);
675
FOccluders.Assign(TGLShadowVolume(Source).Occluders);
676
FCapping := TGLShadowVolume(Source).FCapping;
679
inherited Assign(Source);
682
// FlushSilhouetteCache
685
procedure TGLShadowVolume.FlushSilhouetteCache;
689
for i := 0 to Lights.Count - 1 do
690
(Lights[i] as TGLShadowVolumeLight).FlushSilhouetteCache;
696
procedure TGLShadowVolume.SetActive(const val: Boolean);
698
if FActive <> val then
708
procedure TGLShadowVolume.SetLights(const val: TGLShadowVolumeCasters);
710
Assert(val.ItemClass = TGLShadowVolumeLight);
718
procedure TGLShadowVolume.SetOccluders(const val: TGLShadowVolumeCasters);
720
Assert(val.ItemClass = TGLShadowVolumeOccluder);
721
FOccluders.Assign(val);
728
procedure TGLShadowVolume.SetOptions(const val: TGLShadowVolumeOptions);
730
if FOptions <> val then
733
if not (svoCacheSilhouettes in FOptions) then
734
FlushSilhouetteCache;
742
procedure TGLShadowVolume.SetMode(const val: TGLShadowVolumeMode);
754
procedure TGLShadowVolume.SetDarkeningColor(const val: TGLColor);
756
FDarkeningColor.Assign(val);
762
procedure TGLShadowVolume.DoRender(var ARci: TGLRenderContextInfo;
763
ARenderSelf, ARenderChildren: Boolean);
765
// Function that determines if an object is "recursively visible". It halts when
766
// * it finds an invisible ancestor (=> invisible)
767
// * it finds the root (=> visible)
768
// * it finds the shadow volume as an ancestor (=> visible)
770
// This does _not_ mean that the object is actually visible on the screen
772
function DirectHierarchicalVisibility(obj: TGLBaseSceneObject): boolean;
774
p: TGLBaseSceneObject;
776
if not Assigned(obj) then
781
if not obj.Visible then
787
while Assigned(p) and (p <> obj) and (p <> Self) do
789
if not p.Visible then
801
lightSource: TGLLightSource;
802
lightCaster: TGLShadowVolumeLight;
805
obj: TGLBaseSceneObject;
806
caster: TGLShadowVolumeCaster;
807
opaques, opaqueCapping: TList;
808
silParams: TGLSilhouetteParameters;
820
if not (ARenderSelf or ARenderChildren) then
822
ClearStructureChanged;
823
if ((csDesigning in ComponentState) and not (svoDesignVisible in Options))
825
or (ARci.drawState = dsPicking) then
830
if svoWorldScissorClip in Options then
832
// compute shadow receiving world AABB in absolute coordinates
833
worldAABB := Self.AxisAlignedBoundingBox;
834
AABBTransform(worldAABB, AbsoluteMatrix);
835
pWorldAABB := @worldAABB;
839
opaques := TList.Create;
840
opaqueCapping := TList.Create;
843
// collect visible casters
844
for i := 0 to Occluders.Count - 1 do
846
caster := Occluders[i];
847
obj := caster.Caster;
850
// Determine when to render this object or not
852
(Caster.CastingMode = scmAlways) or
853
((Caster.CastingMode = scmVisible) and obj.Visible) or
854
((Caster.CastingMode = scmRecursivelyVisible) and
855
DirectHierarchicalVisibility(obj)) or
856
((Caster.CastingMode = scmParentRecursivelyVisible) and
857
DirectHierarchicalVisibility(obj.Parent)) or
858
((Caster.CastingMode = scmParentVisible) and (not Assigned(obj.Parent)
862
and ((caster.EffectiveRadius <= 0)
863
or (obj.DistanceTo(ARci.cameraPosition) < caster.EffectiveRadius)) then
866
opaqueCapping.Add(Pointer(PtrUInt(ord((caster.Capping = svcAlways)
867
or ((caster.Capping = svcDefault)
868
and (Capping = svcAlways))))));
873
opaqueCapping.Add(nil);
877
// render the shadow volumes
878
with ARci.GLStates do
881
if Mode = svmAccurate then
883
// first turn off all the shadow casting lights diffuse and specular
884
for i := 0 to Lights.Count - 1 do
886
lightCaster := TGLShadowVolumeLight(Lights[i]);
887
lightSource := lightCaster.LightSource;
888
if Assigned(lightSource) and (lightSource.Shining) then
890
lightID := lightSource.LightID;
891
LightDiffuse[lightID] := NullHmgVector;
892
LightSpecular[lightID] := NullHmgVector;
896
// render shadow receivers with ambient lighting
898
// DanB - not sure why this doesn't render properly with these statements
899
// where they were originally (after the RenderChildren call).
901
Self.RenderChildren(0, Count - 1, ARci);
903
ARci.ignoreBlendingRequests := True;
904
ARci.ignoreDepthRequests := True;
905
DepthWriteMask := False;
907
SetBlendFunc(bfSrcAlpha, bfOne);
908
Disable(stAlphaTest);
909
Enable(stStencilTest);
911
// Disable all client states
912
if GL.ARB_vertex_buffer_object then
914
VertexArrayBinding := 0;
915
ArrayBufferBinding := 0;
916
ElementBufferBinding := 0;
919
// turn off *all* lights
920
for i := 0 to TGLScene(ARci.scene).Lights.Count - 1 do
922
lightSource := (TGLScene(ARci.scene).Lights.Items[i]) as TGLLightSource;
923
if Assigned(lightSource) and lightSource.Shining then
924
LightEnabling[lightSource.LightID] := False;
927
GL.LightModelfv(GL_LIGHT_MODEL_AMBIENT, @NullHmgPoint);
928
ARci.PipelineTransformation.Push;
930
// render contribution of all shadow casting lights
931
for i := 0 to Lights.Count - 1 do
933
lightCaster := TGLShadowVolumeLight(lights[i]);
934
lightSource := lightCaster.LightSource;
936
if (not Assigned(lightSource)) or (not lightSource.Shining) then
939
lightID := lightSource.LightID;
941
SetVector(silParams.LightDirection,
942
lightSource.SpotDirection.DirectVector);
943
case lightSource.LightStyle of
944
lsParallel: silParams.Style := ssParallel
946
silParams.Style := ssOmni;
948
silParams.CappingRequired := True;
950
if Assigned(pWorldAABB) or (svoScissorClips in Options) then
952
if lightCaster.SetupScissorRect(pWorldAABB, ARci) then
953
Enable(stScissorTest)
955
Disable(stScissorTest);
958
// clear the stencil and prepare for shadow volume pass
959
GL.Clear(GL_STENCIL_BUFFER_BIT);
960
SetStencilFunc(cfAlways, 0, 255);
963
if svoShowVolumes in Options then
965
GL.Color3f(0.05 * i, 0.1, 0);
970
SetGLColorWriting(False);
976
GL.EnableClientState(GL_VERTEX_ARRAY);
977
SetPolygonOffset(1, 1);
979
// for all opaque shadow casters
980
for k := 0 to opaques.Count - 1 do
982
obj := TGLBaseSceneObject(opaques[k]);
986
SetVector(silParams.SeenFrom,
987
obj.AbsoluteToLocal(lightSource.AbsolutePosition));
989
sil := lightCaster.GetCachedSilhouette(k);
990
if (not Assigned(sil)) or (not CompareMem(@sil.Parameters, @silParams,
991
SizeOf(silParams))) then
993
sil := obj.GenerateSilhouette(silParams);
994
sil.Parameters := silParams;
995
// extrude vertices to infinity
996
sil.ExtrudeVerticesToInfinity(silParams.SeenFrom);
998
if Assigned(sil) then
1000
// render the silhouette
1001
ARci.PipelineTransformation.ModelMatrix := obj.AbsoluteMatrix;
1002
GL.VertexPointer(4, GL_FLOAT, 0, sil.Vertices.List);
1004
if Boolean(PtrUInt(opaqueCapping[k])) then
1007
if GL.EXT_compiled_vertex_array then
1008
GL.LockArrays(0, sil.Vertices.Count);
1010
CullFaceMode := cmFront;
1011
SetStencilOp(soKeep, soIncr, soKeep);
1015
GL.DrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
1017
Enable(stPolygonOffsetFill);
1018
GL.DrawElements(GL_TRIANGLES, CapIndices.Count,
1021
Disable(stPolygonOffsetFill);
1024
CullFaceMode := cmBack;
1025
SetStencilOp(soKeep, soDecr, soKeep);
1029
GL.DrawElements(GL_QUADS, Indices.Count, GL_UNSIGNED_INT,
1031
Enable(stPolygonOffsetFill);
1032
GL.DrawElements(GL_TRIANGLES, CapIndices.Count,
1035
Disable(stPolygonOffsetFill);
1038
if GL.EXT_compiled_vertex_array then
1044
CullFaceMode := cmBack;
1045
SetStencilOp(soKeep, soKeep, soIncr);
1047
GL.DrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
1050
CullFaceMode := cmFront;
1051
SetStencilOp(soKeep, soKeep, soDecr);
1053
GL.DrawElements(GL_QUADS, sil.Indices.Count, GL_UNSIGNED_INT,
1058
if (svoCacheSilhouettes in Options) and (not (osDirectDraw in
1060
lightCaster.StoreCachedSilhouette(k, sil)
1066
GL.DisableClientState(GL_VERTEX_ARRAY);
1068
// re-enable light's diffuse and specular, but no ambient
1069
LightEnabling[LightID] := True;
1070
LightAmbient[LightID] := NullHmgVector;
1071
LightDiffuse[LightID] := lightSource.Diffuse.Color;
1072
LightSpecular[LightID] := lightSource.Specular.Color;
1074
SetGLColorWriting(True);
1075
SetStencilOp(soKeep, soKeep, soKeep);
1079
CullFaceMode := cmBack;
1081
if Mode = svmAccurate then
1083
SetStencilFunc(cfEqual, 0, 255);
1084
DepthFunc := cfEqual;
1085
Self.RenderChildren(0, Count - 1, ARci);
1089
SetStencilFunc(cfNotEqual, 0, 255);
1091
DepthFunc := cfAlways;
1092
SetBlendFunc(bfSrcAlpha, bfOneMinusSrcAlpha);
1096
GL.MatrixMode(GL_PROJECTION);
1098
PM := CreateOrthoMatrix(0, 1, 1, 0, -1, 1);
1099
GL.LoadMatrixf(PGLFloat(@PM));
1101
GL.Color4fv(FDarkeningColor.AsAddress);
1102
GL.Begin_(GL_QUADS);
1110
GL.MatrixMode(GL_MODELVIEW);
1113
SetBlendFunc(bfSrcAlpha, bfOne);
1116
// disable light, but restore its ambient component
1117
LightEnabling[lightID] := False;
1118
LightAmbient[lightID] := lightSource.Ambient.Color;
1120
ARci.PipelineTransformation.Pop;
1122
// restore OpenGL state
1123
GL.LightModelfv(GL_LIGHT_MODEL_AMBIENT, @ARci.sceneAmbientColor);
1124
Scene.SetupLights(ARci.GLStates.MaxLights);
1125
Disable(stStencilTest);
1126
SetPolygonOffset(0, 0);
1127
ARci.ignoreBlendingRequests := False;
1128
ARci.ignoreDepthRequests := False;
1131
FRendering := False;
1137
//-------------------------------------------------------------
1138
//-------------------------------------------------------------
1139
//-------------------------------------------------------------
1142
//-------------------------------------------------------------
1143
//-------------------------------------------------------------
1144
//-------------------------------------------------------------
1146
RegisterClasses([TGLShadowVolume]);