123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- //
- // The graphics engine GLScene
- //
- unit GLSL.PostEffects;
- (* A collection of components that generate post effects *)
- interface
- {$I Stage.Defines.inc}
- uses
- Winapi.OpenGL,
- System.Classes,
- System.SysUtils,
- Stage.OpenGLTokens,
- GLS.Scene,
- GLS.State,
- GLS.Context,
- GLS.PersistentClasses,
- GLS.Texture,
- GLS.Graphics,
- Stage.Strings,
- GLSL.CustomShader,
- Stage.VectorGeometry,
- GLS.RenderContextInfo,
- GLS.Material,
- Stage.TextureFormat;
- type
- EGLPostShaderHolderException = class(Exception);
- TGLPostShaderHolder = class;
- TGLPostShaderCollectionItem = class(TCollectionItem)
- private
- FShader: TGLShader;
- FPostShaderInterface: IGLPostShader;
- procedure SetShader(const Value: TGLShader);
- protected
- function GetRealOwner: TGLPostShaderHolder;
- function GetDisplayName: string; override;
- public
- procedure Assign(Source: TPersistent); override;
- published
- property Shader: TGLShader read FShader write SetShader;
- end;
- TGLPostShaderCollection = class(TOwnedCollection)
- private
- function GetItems(const Index: Integer): TGLPostShaderCollectionItem;
- procedure SetItems(const Index: Integer;
- const Value: TGLPostShaderCollectionItem);
- public
- procedure Remove(const Item: TGLShader);
- function Add: TGLPostShaderCollectionItem;
- property Items[const Index: Integer]: TGLPostShaderCollectionItem
- read GetItems write SetItems; default;
- end;
- (* A class that allows several post-shaders to be applied to the scene,
- one after another. It does not provide any optimizations related to
- multi-shader rendering, just a convenient interface. *)
- TGLPostShaderHolder = class(TGLBaseSCeneObject)
- private
- FShaders: TGLPostShaderCollection;
- FTempTexture: TGLTextureHandle;
- FPreviousViewportSize: TGLSize;
- FTempTextureTarget: TGLTextureTarget;
- procedure SetShaders(const Value: TGLPostShaderCollection);
- protected
- procedure Notification(AComponent: TComponent;
- Operation: TOperation); override;
- public
- constructor Create(Owner: TComponent); override;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure DoRender(var rci: TGLRenderContextInfo;
- renderSelf, renderChildren: Boolean); override;
- published
- property TempTextureTarget: TGLTextureTarget read FTempTextureTarget
- write FTempTextureTarget default ttTexture2d;
- property Shaders: TGLPostShaderCollection read FShaders write SetShaders;
- // Publish some stuff from TGLBaseSceneObject.
- property Visible;
- property OnProgress;
- end;
- TGLPostEffectColor = record
- R, G, B, A: Byte;
- end;
- TGLPostEffectBuffer = array of TGLPostEffectColor;
- TGLOnCustomPostEffectEvent = procedure(Sender: TObject;
- var rci: TGLRenderContextInfo; var Buffer: TGLPostEffectBuffer) of object;
- (* Some presets for TGLPostEffect:
- pepNone - does nothing.
- pepGray - makes picture gray.
- pepNegative - inverts all colors.
- pepDistort - simulates shaky TV image.
- pepNoise - just adds random niose.
- pepNightVision - simulates nightvision goggles.
- pepBlur - blurs the scene.
- pepCustom - calls the OnCustomEffect event. *)
- TGLPostEffectPreset = (pepNone, pepGray, pepNegative, pepDistort, pepNoise,
- pepNightVision, pepBlur, pepCustom);
- (* Provides a simple way to producing post-effects without shaders.
- It is slow as hell, but it's worth it in some cases. *)
- TGLPostEffect = class(TGLBaseSCeneObject)
- private
- FOnCustomEffect: TGLOnCustomPostEffectEvent;
- FPreset: TGLPostEffectPreset;
- FRenderBuffer: TGLPostEffectBuffer;
- protected
- // May be should be private...
- procedure MakeGrayEffect; virtual;
- procedure MakeNegativeEffect; virtual;
- procedure MakeDistortEffect; virtual;
- procedure MakeNoiseEffect; virtual;
- procedure MakeNightVisionEffect; virtual;
- procedure MakeBlurEffect(var rci: TGLRenderContextInfo); virtual;
- procedure DoOnCustomEffect(var rci: TGLRenderContextInfo;
- var Buffer: TGLPostEffectBuffer); virtual;
- public
- procedure DoRender(var rci: TGLRenderContextInfo;
- renderSelf, renderChildren: Boolean); override;
- procedure Assign(Source: TPersistent); override;
- published
- property Preset: TGLPostEffectPreset read FPreset write FPreset
- default pepNone;
- // User creates this effect.
- property OnCustomEffect: TGLOnCustomPostEffectEvent read FOnCustomEffect
- write FOnCustomEffect;
- // Publish some stuff from TGLBaseSCeneObject.
- property Visible;
- property OnProgress;
- end;
- // -------------------------------------------------------------------------
- implementation
- // -------------------------------------------------------------------------
- { TGLPostEffect }
- procedure TGLPostEffect.Assign(Source: TPersistent);
- begin
- inherited;
- if Source is TGLPostEffect then
- begin
- FPreset := TGLPostEffect(Source).FPreset;
- end;
- end;
- procedure TGLPostEffect.DoOnCustomEffect(var rci: TGLRenderContextInfo;
- var Buffer: TGLPostEffectBuffer);
- begin
- if Assigned(FOnCustomEffect) then
- FOnCustomEffect(Self, rci, Buffer);
- end;
- procedure TGLPostEffect.DoRender(var rci: TGLRenderContextInfo;
- renderSelf, renderChildren: Boolean);
- var
- NewScreenSize: Integer;
- begin
- if (not rci.ignoreMaterials) and (FPreset <> pepNone) and
- (rci.drawState <> dsPicking) then
- begin
- NewScreenSize := rci.viewPortSize.cx * rci.viewPortSize.cy;
- if NewScreenSize <> Length(FRenderBuffer) then
- SetLength(FRenderBuffer, NewScreenSize);
- gl.ReadPixels(0, 0, rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA,
- GL_UNSIGNED_BYTE, FRenderBuffer);
- case FPreset of
- // pepNone is handled in the first line.
- pepGray: MakeGrayEffect;
- pepNegative: MakeNegativeEffect;
- pepDistort: MakeDistortEffect;
- pepNoise: MakeNoiseEffect;
- pepNightVision: MakeNightVisionEffect;
- pepBlur: MakeBlurEffect(rci);
- pepCustom: DoOnCustomEffect(rci, FRenderBuffer);
- else
- Assert(False, strErrorEx + strUnknownType);
- end;
- gl.DrawPixels(rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA,
- GL_UNSIGNED_BYTE, FRenderBuffer);
- end;
- // Start rendering children (if any).
- if renderChildren then
- Self.renderChildren(0, Count - 1, rci);
- end;
- procedure TGLPostEffect.MakeGrayEffect;
- var
- I: Longword;
- gray: Byte;
- begin
- for I := 0 to High(FRenderBuffer) do
- begin
- gray := Round((0.30 * FRenderBuffer[I].R) + (0.59 * FRenderBuffer[I].G) +
- (0.11 * FRenderBuffer[I].B));
- FRenderBuffer[I].R := gray;
- FRenderBuffer[I].G := gray;
- FRenderBuffer[I].B := gray;
- end;
- end;
- procedure TGLPostEffect.MakeNegativeEffect;
- var
- I: Longword;
- begin
- for I := 0 to High(FRenderBuffer) do
- begin
- FRenderBuffer[I].R := 255 - FRenderBuffer[I].R;
- FRenderBuffer[I].G := 255 - FRenderBuffer[I].G;
- FRenderBuffer[I].B := 255 - FRenderBuffer[I].B;
- end;
- end;
- procedure TGLPostEffect.MakeDistortEffect;
- var
- I: Integer;
- lMaxLength: Integer;
- lNewIndex: Integer;
- begin
- lMaxLength := High(FRenderBuffer);
- for I := 0 to lMaxLength do
- begin
- lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(10) - 5));
- FRenderBuffer[I].R := FRenderBuffer[lNewIndex].R;
- FRenderBuffer[I].G := FRenderBuffer[lNewIndex].G;
- FRenderBuffer[I].B := FRenderBuffer[lNewIndex].B;
- end;
- end;
- procedure TGLPostEffect.MakeNoiseEffect;
- var
- I: Longword;
- rnd: Single;
- begin
- for I := 0 to High(FRenderBuffer) do
- begin
- rnd := 0.25 + Random(75) / 100;
- FRenderBuffer[I].R := Round(FRenderBuffer[I].R * rnd);
- FRenderBuffer[I].G := Round(FRenderBuffer[I].G * rnd);
- FRenderBuffer[I].B := Round(FRenderBuffer[I].B * rnd);
- end;
- end;
- procedure TGLPostEffect.MakeNightVisionEffect;
- var
- gray: Single;
- I: Integer;
- lNewIndex, lMaxLength: Integer;
- begin
- lMaxLength := High(FRenderBuffer);
- for I := 0 to lMaxLength do
- begin
- lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(20) - 10));
- gray := 60 + (0.30 * FRenderBuffer[lNewIndex].R) +
- (0.59 * FRenderBuffer[lNewIndex].G) + (0.11 * FRenderBuffer[lNewIndex].B);
- FRenderBuffer[I].R := Round(gray * 0.25);
- FRenderBuffer[I].G := Round((gray + 4) * 0.6);
- FRenderBuffer[I].B := Round((gray + 4) * 0.11);
- end;
- end;
- procedure TGLPostEffect.MakeBlurEffect(var rci: TGLRenderContextInfo);
- const
- lOffset: Integer = 2;
- var
- I: Integer;
- lUp: Integer;
- begin
- lUp := rci.viewPortSize.cx * lOffset;
- for I := lUp to High(FRenderBuffer) - lUp do
- begin
- FRenderBuffer[I].R := (FRenderBuffer[I].R + FRenderBuffer[I - lOffset].R +
- FRenderBuffer[I + lOffset].R + FRenderBuffer[I - lUp].R + FRenderBuffer
- [I + lUp].R) div 5;
- FRenderBuffer[I].G := (FRenderBuffer[I].G + FRenderBuffer[I - lOffset].G +
- FRenderBuffer[I + lOffset].G + FRenderBuffer[I - lUp].G + FRenderBuffer
- [I + lUp].R) div 5;
- FRenderBuffer[I].B := (FRenderBuffer[I].B + FRenderBuffer[I - lOffset].B +
- FRenderBuffer[I + lOffset].B + FRenderBuffer[I - lUp].G + FRenderBuffer
- [I + lUp].R) div 5;
- end;
- end;
- { TGLPostShaderCollectionItem }
- procedure TGLPostShaderCollectionItem.Assign(Source: TPersistent);
- begin
- if Source is TGLPostShaderCollectionItem then
- begin
- SetShader(TGLPostShaderCollectionItem(Source).FShader);
- end
- else
- inherited; // Die!!!
- end;
- function TGLPostShaderCollectionItem.GetDisplayName: string;
- begin
- if FShader = nil then
- Result := ''
- else
- begin
- if FShader.Name <> '' then
- Result := FShader.Name
- else
- Result := FShader.ClassName;
- end;
- end;
- type
- // Required for Delphi5 compatibility.
- THackCollection = class(TOwnedCollection)
- end;
- function TGLPostShaderCollectionItem.GetRealOwner: TGLPostShaderHolder;
- begin
- if Collection = nil then
- Result := nil
- else
- Result := TGLPostShaderHolder(THackCollection(Collection).GetOwner);
- end;
- procedure TGLPostShaderCollectionItem.SetShader(const Value: TGLShader);
- var
- RealOwner: TGLPostShaderHolder;
- begin
- if FShader = Value then
- Exit;
- RealOwner := GetRealOwner;
- if FShader <> nil then
- FShader.RemoveFreeNotification(RealOwner);
- if not Supports(TObject(Value), IGLPostShader, FPostShaderInterface) then
- raise EGLPostShaderHolderException.Create
- ('Shader must support interface IGLPostShader!');
- if RealOwner <> nil then
- if FPostShaderInterface.GetTextureTarget <> RealOwner.TempTextureTarget then
- raise EGLPostShaderHolderException.Create
- (strErrorEx + 'TextureTarget is not compatible!');
- // If RealOwner = nil, we ignore this case and hope it will turn out ok...
- FShader := Value;
- if FShader <> nil then
- if RealOwner <> nil then
- FShader.FreeNotification(RealOwner);
- end;
- //------------------------------
- // TGLPostShaderHolder
- //------------------------------
- procedure TGLPostShaderHolder.Assign(Source: TPersistent);
- begin
- if Source is TGLPostShaderHolder then
- begin
- FShaders.Assign(TGLPostShaderHolder(Source).FShaders);
- FTempTextureTarget := TGLPostShaderHolder(Source).FTempTextureTarget;
- end;
- inherited;
- end;
- constructor TGLPostShaderHolder.Create(Owner: TComponent);
- begin
- inherited;
- FTempTexture := TGLTextureHandle.Create;
- FTempTextureTarget := ttTexture2d;
- FShaders := TGLPostShaderCollection.Create(Self, TGLPostShaderCollectionItem);
- end;
- destructor TGLPostShaderHolder.Destroy;
- begin
- FShaders.Destroy;
- FTempTexture.Destroy;
- inherited;
- end;
- procedure TGLPostShaderHolder.DoRender(var rci: TGLRenderContextInfo;
- renderSelf, renderChildren: Boolean);
- var
- I: Integer;
- begin
- if not(rci.ignoreMaterials) and not(csDesigning in ComponentState) and
- (rci.drawState <> dsPicking) then
- begin
- if (FPreviousViewportSize.cx <> rci.viewPortSize.cx) or
- (FPreviousViewportSize.cy <> rci.viewPortSize.cy) then
- begin
- InitTexture(FTempTexture.Handle, rci.viewPortSize, FTempTextureTarget);
- FPreviousViewportSize := rci.viewPortSize;
- end;
- if FShaders.Count <> 0 then
- begin
- for I := 0 to FShaders.Count - 1 do
- begin
- Assert(Assigned(FShaders[I].FShader));
- if FShaders[I].FShader.Enabled then
- begin
- rci.GLStates.ActiveTextureEnabled[FTempTextureTarget] := True;
- FShaders[I].FShader.Apply(rci, Self);
- repeat
- CopyScreenToTexture(rci.viewPortSize,
- DecodeTextureTarget(FTempTextureTarget));
- FShaders[I].FPostShaderInterface.DoUseTempTexture(FTempTexture,
- FTempTextureTarget);
- DrawTexturedScreenQuad5(rci.viewPortSize);
- until not FShaders[I].FShader.UnApply(rci);
- rci.GLStates.ActiveTextureEnabled[FTempTextureTarget] := False;
- end;
- end;
- end;
- end;
- if renderChildren then
- Self.renderChildren(0, Count - 1, rci);
- end;
- procedure TGLPostShaderHolder.Notification(AComponent: TComponent;
- Operation: TOperation);
- begin
- inherited;
- if Operation = opRemove then
- begin
- if AComponent is TGLShader then
- FShaders.Remove(TGLShader(AComponent));
- end;
- end;
- procedure TGLPostShaderHolder.SetShaders(const Value: TGLPostShaderCollection);
- begin
- FShaders.Assign(Value);
- end;
- { TGLPostShaderCollection }
- function TGLPostShaderCollection.Add: TGLPostShaderCollectionItem;
- begin
- Result := TGLPostShaderCollectionItem(inherited Add);
- end;
- function TGLPostShaderCollection.GetItems(const Index: Integer)
- : TGLPostShaderCollectionItem;
- begin
- Result := TGLPostShaderCollectionItem(GetItem(Index));
- end;
- procedure TGLPostShaderCollection.Remove(const Item: TGLShader);
- var
- I: Integer;
- begin
- if Count <> 0 then
- for I := Count - 1 downto 0 do
- if GetItems(I).FShader = Item then
- Delete(I);
- // Don't exit because the same shader might be applied more than once.
- end;
- procedure TGLPostShaderCollection.SetItems(const Index: Integer;
- const Value: TGLPostShaderCollectionItem);
- begin
- GetItems(Index).Assign(Value);
- end;
- // ------------------------------------------------
- initialization
- // ------------------------------------------------
- RegisterClasses([TGLPostEffect, TGLPostShaderHolder, TGLPostShaderCollection,
- TGLPostShaderCollectionItem]);
- end.
|