2
// This unit is part of the GLScene Engine https://github.com/glscene
5
A collection of components that generate post effects.
8
23/08/10 - Yar - Added OpenGLTokens to uses, replaced OpenGL1x functions to OpenGLAdapter
9
22/04/10 - Yar - Fixes after GLState revision
10
28/05/08 - DaStr - Fixed AV in TGLPostEffect.MakeDistortEffect()
11
Got rid of all R- hacks
12
10/04/08 - DaStr - Added a Delpi 5 interface bug work-around to
13
TGLPostShaderCollectionItem.SetShader()
14
(BugTracker ID = 1938988)
15
16/08/07 - DaStr - Added pepBlur preset (by Paul van Dinther)
16
25/03/07 - DaStr - Small fix for Delphi5 compatibility
17
23/03/07 - DaStr - Added TGLPostShaderHolder.Assign
18
20/03/07 - DaStr - Fixed TGLPostShaderHolder.DoRender
19
09/03/07 - DaStr - Added pepNightVision preset (thanks Roman Ganz)
20
Changed back all Trunc() calls to Round()
21
07/03/07 - DaStr - Moved "Weird" effect to the demo
22
Added "Distort" effect
23
Modified "RedNoise" to simple monochrome noise
24
(preset renamed to "Noise")
25
Made "Negative" effect really negative,
26
instead swapping R and B channels
27
Changed all Round() calls to Trunc()
28
Removed all TGLPostEffectColor typecasts
29
(All above changes were made by Michail Glukhov)
30
TGLPostEffect and TGLPostShaderHolder are not
31
rendered when DrawState=dsPicking (suggested by Riz)
32
04/03/07 - DaStr - Added TGLPostShaderHolder
33
02/03/07 - DaStr - TGLOnCustomPostEffectEvent now passes rci
34
pepNone preset does not call gl[Read/Draw]Pixels
35
23/02/07 - DaStr - Initial version of TGLPostEffect
36
(based on OldCity demo by FedeX)
51
GLScene, GLTexture, OpenGLTokens, GLGraphics, GLStrings, GLCustomShader,
52
GLContext, GLVectorGeometry, GLRenderContextInfo, GLMaterial, GLTextureFormat;
55
EGLPostShaderHolderException = class(Exception);
56
TGLPostShaderHolder = class;
58
TGLPostShaderCollectionItem = class(TCollectionItem)
61
FPostShaderInterface: IGLPostShader;
62
procedure SetShader(const Value: TGLShader);
64
function GetRealOwner: TGLPostShaderHolder;
65
function GetDisplayName: string; override;
67
procedure Assign(Source: TPersistent); override;
69
property Shader: TGLShader read FShader write SetShader;
72
TGLPostShaderCollection = class(TOwnedCollection)
74
function GetItems(const Index: Integer): TGLPostShaderCollectionItem;
75
procedure SetItems(const Index: Integer;
76
const Value: TGLPostShaderCollectionItem);
78
procedure Remove(const Item: TGLShader);
79
function Add: TGLPostShaderCollectionItem;
81
property Items[const Index: Integer]: TGLPostShaderCollectionItem read GetItems write SetItems; default;
84
{ A class that allows several post-shaders to be applied to the scene,
85
one after another. It does not provide any optimizations related to
86
multi-shader rendering, just a convenient interface. }
87
TGLPostShaderHolder = class(TGLBaseSCeneObject)
89
FShaders: TGLPostShaderCollection;
90
FTempTexture: TGLTextureHandle;
91
FPreviousViewportSize: TGLSize;
92
FTempTextureTarget: TGLTextureTarget;
93
procedure SetShaders(const Value: TGLPostShaderCollection);
95
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
97
constructor Create(Owner: TComponent); override;
98
destructor Destroy; override;
99
procedure Assign(Source: TPersistent); override;
100
procedure DoRender(var rci : TGLRenderContextInfo;
101
renderSelf, renderChildren : Boolean); override;
103
property TempTextureTarget: TGLTextureTarget read FTempTextureTarget write FTempTextureTarget default ttTexture2d;
104
property Shaders: TGLPostShaderCollection read FShaders write SetShaders;
106
// Publish some stuff from TGLBaseSceneObject.
112
TGLPostEffectColor = record
113
R, G, B, A: TGLubyte;
116
TGLPostEffectBuffer = array of TGLPostEffectColor;
118
TGLOnCustomPostEffectEvent = procedure(Sender: TObject; var rci : TGLRenderContextInfo; var Buffer: TGLPostEffectBuffer) of object;
120
{ Some presets for TGLPostEffect:
121
pepNone - does nothing.
122
pepGray - makes picture gray.
123
pepNegative - inverts all colors.
124
pepDistort - simulates shaky TV image.
125
pepNoise - just adds random niose.
126
pepNightVision - simulates nightvision goggles.
127
pepBlur - blurs the scene.
128
pepCustom - calls the OnCustomEffect event.
130
TGLPostEffectPreset = (pepNone, pepGray, pepNegative, pepDistort, pepNoise,
131
pepNightVision, pepBlur, pepCustom);
133
{ Provides a simple way to producing post-effects without shaders.
134
It is slow as hell, but it's worth it in some cases.}
135
TGLPostEffect = class(TGLBaseSCeneObject)
137
FOnCustomEffect: TGLOnCustomPostEffectEvent;
138
FPreset: TGLPostEffectPreset;
139
FRenderBuffer: TGLPostEffectBuffer;
141
// May be should be private...
142
procedure MakeGrayEffect; virtual;
143
procedure MakeNegativeEffect; virtual;
144
procedure MakeDistortEffect; virtual;
145
procedure MakeNoiseEffect; virtual;
146
procedure MakeNightVisionEffect; virtual;
147
procedure MakeBlurEffect(var rci : TGLRenderContextInfo); virtual;
148
procedure DoOnCustomEffect(var rci : TGLRenderContextInfo; var Buffer: TGLPostEffectBuffer); virtual;
150
procedure DoRender(var rci : TGLRenderContextInfo;
151
renderSelf, renderChildren : Boolean); override;
152
procedure Assign(Source: TPersistent); override;
154
property Preset: TGLPostEffectPreset read FPreset write FPreset default pepNone;
155
// User creates this effect.
156
property OnCustomEffect: TGLOnCustomPostEffectEvent read FOnCustomEffect write FOnCustomEffect;
157
// Publish some stuff from TGLBaseSCeneObject.
166
procedure TGLPostEffect.Assign(Source: TPersistent);
169
if Source is TGLPostEffect then
171
FPreset := TGLPostEffect(Source).FPreset;
175
procedure TGLPostEffect.DoOnCustomEffect(
176
var rci : TGLRenderContextInfo; var Buffer: TGLPostEffectBuffer);
178
if Assigned(FOnCustomEffect) then
179
FOnCustomEffect(Self, rci, Buffer);
182
procedure TGLPostEffect.DoRender(var rci : TGLRenderContextInfo;
183
renderSelf, renderChildren : Boolean);
185
NewScreenSize: Integer;
187
if (not rci.ignoreMaterials) and (FPreset <> pepNone) and (rci.drawState <> dsPicking) then
189
NewScreenSize := rci.viewPortSize.cx * rci.viewPortSize.cy;
190
if NewScreenSize <> Length(FRenderBuffer) then
191
SetLength(FRenderBuffer, NewScreenSize);
193
GL.ReadPixels(0, 0, rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA, GL_UNSIGNED_BYTE, FRenderBuffer);
195
// pepNone is handled in the first line.
196
pepGray: MakeGrayEffect;
197
pepNegative: MakeNegativeEffect;
198
pepDistort: MakeDistortEffect;
199
pepNoise: MakeNoiseEffect;
200
pepNightVision: MakeNightVisionEffect;
201
pepBlur: MakeBlurEffect(rci);
202
pepCustom: DoOnCustomEffect(rci, FRenderBuffer);
204
Assert(False, glsErrorEx + glsUnknownType);
206
GL.DrawPixels(rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA, GL_UNSIGNED_BYTE, FRenderBuffer);
209
// Start rendering children (if any).
210
if renderChildren then
211
Self.RenderChildren(0, Count - 1, rci);
214
procedure TGLPostEffect.MakeGrayEffect;
219
for I := 0 to High(FRenderBuffer) do
221
gray := Round((0.30 * FRenderBuffer[I].r) +
222
(0.59 * FRenderBuffer[I].g) +
223
(0.11 * FRenderBuffer[I].b));
224
FRenderBuffer[I].r := gray;
225
FRenderBuffer[I].g := gray;
226
FRenderBuffer[I].b := gray;
230
procedure TGLPostEffect.MakeNegativeEffect;
234
for I := 0 to High(FRenderBuffer) do
236
FRenderBuffer[I].r := 255 - FRenderBuffer[I].r;
237
FRenderBuffer[I].g := 255 - FRenderBuffer[I].g;
238
FRenderBuffer[I].b := 255 - FRenderBuffer[I].b;
242
procedure TGLPostEffect.MakeDistortEffect;
248
lMaxLength := High(FRenderBuffer);
250
for I := 0 to lMaxLength do
252
lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(10) - 5));
253
FRenderBuffer[I].r := FRenderBuffer[lNewIndex].r;
254
FRenderBuffer[I].g := FRenderBuffer[lNewIndex].g;
255
FRenderBuffer[I].b := FRenderBuffer[lNewIndex].b;
259
procedure TGLPostEffect.MakeNoiseEffect;
264
for I := 0 to High(FRenderBuffer) do
266
rnd := 0.25 + Random(75)/100;
268
FRenderBuffer[I].r := Round(FRenderBuffer[I].r * rnd);
269
FRenderBuffer[I].g := Round(FRenderBuffer[I].g * rnd);
270
FRenderBuffer[I].b := Round(FRenderBuffer[I].b * rnd);
274
procedure TGLPostEffect.MakeNightVisionEffect;
278
lNewIndex, lMaxLength: Integer;
280
lMaxLength := High(FRenderBuffer);
282
for I := 0 to lMaxLength do
284
lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(20) - 10));
286
gray := 60 + (0.30 * FRenderBuffer[lNewIndex].r) +
287
(0.59 * FRenderBuffer[lNewIndex].g) +
288
(0.11 * FRenderBuffer[lNewIndex].b);
290
FRenderBuffer[I].r := Round(gray * 0.25);
291
FRenderBuffer[I].g := Round((gray + 4) * 0.6);
292
FRenderBuffer[I].b := Round((gray + 4) * 0.11);
296
procedure TGLPostEffect.MakeBlurEffect(var rci : TGLRenderContextInfo);
298
lOffset: Integer = 2;
303
lUp := rci.viewPortSize.cx * lOffset;
304
for I := lUp to High(FRenderBuffer) - lUp do
306
FRenderBuffer[I].r := (FRenderBuffer[I].r + FRenderBuffer[I - lOffset].r +
307
FRenderBuffer[I + lOffset].r + FRenderBuffer[I - lUp].r +
308
FRenderBuffer[I + lUp].r) div 5;
309
FRenderBuffer[I].g := (FRenderBuffer[I].g + FRenderBuffer[I - lOffset].g +
310
FRenderBuffer[I + lOffset].g + FRenderBuffer[I - lUp].g +
311
FRenderBuffer[I + lUp].r) div 5;
312
FRenderBuffer[I].b := (FRenderBuffer[I].b + FRenderBuffer[I - lOffset].b +
313
FRenderBuffer[I + lOffset].b + FRenderBuffer[I - lUp].g +
314
FRenderBuffer[I + lUp].r) div 5;
318
{ TGLPostShaderCollectionItem }
320
procedure TGLPostShaderCollectionItem.Assign(Source: TPersistent);
322
if Source is TGLPostShaderCollectionItem then
324
SetShader(TGLPostShaderCollectionItem(Source).FShader);
330
function TGLPostShaderCollectionItem.GetDisplayName: string;
332
if FShader = nil then
336
if FShader.Name <> '' then
337
Result := FShader.Name
339
Result := FShader.ClassName;
344
// Required for Delphi5 compatibility.
345
THackCollection = class(TOwnedCollection)end;
347
function TGLPostShaderCollectionItem.GetRealOwner: TGLPostShaderHolder;
349
if Collection = nil then
352
Result := TGLPostShaderHolder(THackCollection(Collection).GetOwner);
355
procedure TGLPostShaderCollectionItem.SetShader(const Value: TGLShader);
357
RealOwner: TGLPostShaderHolder;
359
if FShader = Value then Exit;
360
RealOwner := GetRealOwner;
362
if FShader <> nil then
363
FShader.RemoveFreeNotification(RealOwner);
365
if not Supports(TObject(Value), IGLPostShader, FPostShaderInterface) then
366
raise EGLPostShaderHolderException.Create('Shader must support interface IGLPostShader!');
368
if RealOwner <> nil then
369
if FPostShaderInterface.GetTextureTarget <> RealOwner.TempTextureTarget then
370
raise EGLPostShaderHolderException.Create(glsErrorEx + 'TextureTarget is not compatible!');
371
// If RealOwner = nil, we ignore this case and hope it will turn out ok...
375
if FShader <> nil then
376
if RealOwner <> nil then
377
FShader.FreeNotification(RealOwner);
380
{ TGLPostShaderHolder }
382
procedure TGLPostShaderHolder.Assign(Source: TPersistent);
384
if Source is TGLPostShaderHolder then
386
FShaders.Assign(TGLPostShaderHolder(Source).FShaders);
387
FTempTextureTarget := TGLPostShaderHolder(Source).FTempTextureTarget;
392
constructor TGLPostShaderHolder.Create(Owner: TComponent);
395
FTempTexture := TGLTextureHandle.Create;
396
FTempTextureTarget :=ttTexture2D;
397
FShaders := TGLPostShaderCollection.Create(Self, TGLPostShaderCollectionItem);
400
destructor TGLPostShaderHolder.Destroy;
403
FTempTexture.Destroy;
407
procedure TGLPostShaderHolder.DoRender(var rci: TGLRenderContextInfo;
408
renderSelf, renderChildren: Boolean);
412
if not (rci.ignoreMaterials) and not (csDesigning in ComponentState) and
413
(rci.drawState <> dsPicking) then
415
if (FPreviousViewportSize.cx <> rci.viewPortSize.cx) or
416
(FPreviousViewportSize.cy <> rci.viewPortSize.cy) then
418
InitTexture(FTempTexture.Handle, rci.viewPortSize,
420
FPreviousViewportSize := rci.viewPortSize;
423
if FShaders.Count <> 0 then
425
for I := 0 to FShaders.Count - 1 do
427
Assert(Assigned(FShaders[I].FShader));
428
if FShaders[I].FShader.Enabled then
430
rci.GLStates.ActiveTextureEnabled[FTempTextureTarget] := True;
431
FShaders[I].FShader.Apply(rci, Self);
433
CopyScreenToTexture(rci.viewPortSize, DecodeGLTextureTarget(FTempTextureTarget));
434
FShaders[I].FPostShaderInterface.DoUseTempTexture(FTempTexture, FTempTextureTarget);
435
DrawTexturedScreenQuad5(rci.viewPortSize);
436
until not FShaders[I].FShader.UnApply(rci);
437
rci.GLStates.ActiveTextureEnabled[FTempTextureTarget] := False;
442
if renderChildren then
443
Self.RenderChildren(0, Count - 1, rci);
446
procedure TGLPostShaderHolder.Notification(AComponent: TComponent;
447
Operation: TOperation);
450
if Operation = opRemove then
452
if AComponent is TGLShader then
453
FShaders.Remove(TGLShader(AComponent));
457
procedure TGLPostShaderHolder.SetShaders(
458
const Value: TGLPostShaderCollection);
460
FShaders.Assign(Value);
463
{ TGLPostShaderCollection }
465
function TGLPostShaderCollection.Add: TGLPostShaderCollectionItem;
467
Result := TGLPostShaderCollectionItem(inherited Add);
470
function TGLPostShaderCollection.GetItems(
471
const Index: Integer): TGLPostShaderCollectionItem;
473
Result := TGLPostShaderCollectionItem(GetItem(Index));
476
procedure TGLPostShaderCollection.Remove(
477
const Item: TGLShader);
482
for I := Count - 1 downto 0 do
483
if GetItems(I).FShader = Item then
485
// Don't exit because the same shader might be applied more than once.
488
procedure TGLPostShaderCollection.SetItems(const Index: Integer;
489
const Value: TGLPostShaderCollectionItem);
491
GetItems(Index).Assign(Value);
495
RegisterClasses([TGLPostEffect, TGLPostShaderHolder,
496
TGLPostShaderCollection, TGLPostShaderCollectionItem]);