2
0

GXSL.PostEffects.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXSL.PostEffects;
  5. (* A collection of components that generate post effects *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.SysUtils,
  12. GXS.PersistentClasses,
  13. Stage.VectorGeometry,
  14. Stage.Strings,
  15. GXS.Scene,
  16. GXS.Texture,
  17. GXS.Graphics,
  18. GXSL.CustomShader,
  19. GXS.Context,
  20. GXS.RenderContextInfo,
  21. GXS.Material,
  22. Stage.TextureFormat;
  23. type
  24. EGLPostShaderHolderException = class(Exception);
  25. TgxPostShaderHolder = class;
  26. TgxPostShaderCollectionItem = class(TCollectionItem)
  27. private
  28. FShader: TgxShader;
  29. FPostShaderInterface: IgxPostShader;
  30. procedure SetShader(const Value: TgxShader);
  31. protected
  32. function GetRealOwner: TgxPostShaderHolder;
  33. function GetDisplayName: string; override;
  34. public
  35. procedure Assign(Source: TPersistent); override;
  36. published
  37. property Shader: TgxShader read FShader write SetShader;
  38. end;
  39. TgxPostShaderCollection = class(TOwnedCollection)
  40. private
  41. function GetItems(const Index: Integer): TgxPostShaderCollectionItem;
  42. procedure SetItems(const Index: Integer;
  43. const Value: TgxPostShaderCollectionItem);
  44. public
  45. procedure Remove(const Item: TgxShader);
  46. function Add: TgxPostShaderCollectionItem;
  47. property Items[const Index: Integer]: TgxPostShaderCollectionItem read GetItems write SetItems; default;
  48. end;
  49. (* A class that allows several post-shaders to be applied to the scene,
  50. one after another. It does not provide any optimizations related to
  51. multi-shader rendering, just a convenient interface. *)
  52. TgxPostShaderHolder = class(TgxBaseSCeneObject)
  53. private
  54. FShaders: TgxPostShaderCollection;
  55. FTempTexture: TgxTextureHandle;
  56. FPreviousViewportSize: TGXSize;
  57. FTempTextureTarget: TglTextureTarget;
  58. procedure SetShaders(const Value: TgxPostShaderCollection);
  59. protected
  60. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  61. public
  62. constructor Create(Owner: TComponent); override;
  63. destructor Destroy; override;
  64. procedure Assign(Source: TPersistent); override;
  65. procedure DoRender(var rci : TgxRenderContextInfo;
  66. renderSelf, renderChildren : Boolean); override;
  67. published
  68. property TempTextureTarget: TglTextureTarget read FTempTextureTarget write FTempTextureTarget default ttTexture2d;
  69. property Shaders: TgxPostShaderCollection read FShaders write SetShaders;
  70. // Publish some stuff from TgxBaseSceneObject.
  71. property Visible;
  72. property OnProgress;
  73. end;
  74. TgxPostEffectColor = record
  75. R, G, B, A: GLubyte;
  76. end;
  77. TgxPostEffectBuffer = array of TgxPostEffectColor;
  78. TgxOnCustomPostEffectEvent = procedure(Sender: TObject; var rci : TgxRenderContextInfo; var Buffer: TgxPostEffectBuffer) of object;
  79. (* Some presets for TgxPostEffect:
  80. pepNone - does nothing.
  81. pepGray - makes picture gray.
  82. pepNegative - inverts all colors.
  83. pepDistort - simulates shaky TV image.
  84. pepNoise - just adds random niose.
  85. pepNightVision - simulates nightvision goggles.
  86. pepBlur - blurs the scene.
  87. pepCustom - calls the OnCustomEffect event. *)
  88. TgxPostEffectPreset = (pepNone, pepGray, pepNegative, pepDistort, pepNoise,
  89. pepNightVision, pepBlur, pepCustom);
  90. (* Provides a simple way to producing post-effects without shaders.
  91. It is slow as hell, but it's worth it in some cases.*)
  92. TgxPostEffect = class(TgxBaseSCeneObject)
  93. private
  94. FOnCustomEffect: TgxOnCustomPostEffectEvent;
  95. FPreset: TgxPostEffectPreset;
  96. FRenderBuffer: TgxPostEffectBuffer;
  97. protected
  98. // May be should be private...
  99. procedure MakeGrayEffect; virtual;
  100. procedure MakeNegativeEffect; virtual;
  101. procedure MakeDistortEffect; virtual;
  102. procedure MakeNoiseEffect; virtual;
  103. procedure MakeNightVisionEffect; virtual;
  104. procedure MakeBlurEffect(var rci : TgxRenderContextInfo); virtual;
  105. procedure DoOnCustomEffect(var rci : TgxRenderContextInfo; var Buffer: TgxPostEffectBuffer); virtual;
  106. public
  107. procedure DoRender(var rci : TgxRenderContextInfo;
  108. renderSelf, renderChildren : Boolean); override;
  109. procedure Assign(Source: TPersistent); override;
  110. published
  111. property Preset: TgxPostEffectPreset read FPreset write FPreset default pepNone;
  112. // User creates this effect.
  113. property OnCustomEffect: TgxOnCustomPostEffectEvent read FOnCustomEffect write FOnCustomEffect;
  114. // Publish some stuff from TgxBaseSCeneObject.
  115. property Visible;
  116. property OnProgress;
  117. end;
  118. //-----------------------------------------------------------------------------
  119. implementation
  120. //-----------------------------------------------------------------------------
  121. //-------------------------------
  122. // TgxPostEffect
  123. //-------------------------------
  124. procedure TgxPostEffect.Assign(Source: TPersistent);
  125. begin
  126. inherited;
  127. if Source is TgxPostEffect then
  128. begin
  129. FPreset := TgxPostEffect(Source).FPreset;
  130. end;
  131. end;
  132. procedure TgxPostEffect.DoOnCustomEffect(
  133. var rci : TgxRenderContextInfo; var Buffer: TgxPostEffectBuffer);
  134. begin
  135. if Assigned(FOnCustomEffect) then
  136. FOnCustomEffect(Self, rci, Buffer);
  137. end;
  138. procedure TgxPostEffect.DoRender(var rci : TgxRenderContextInfo;
  139. renderSelf, renderChildren : Boolean);
  140. var
  141. NewScreenSize: Integer;
  142. begin
  143. if (not rci.ignoreMaterials) and (FPreset <> pepNone) and (rci.drawState <> dsPicking) then
  144. begin
  145. NewScreenSize := rci.viewPortSize.cx * rci.viewPortSize.cy;
  146. if NewScreenSize <> Length(FRenderBuffer) then
  147. SetLength(FRenderBuffer, NewScreenSize);
  148. glReadPixels(0, 0, rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA, GL_UNSIGNED_BYTE, FRenderBuffer);
  149. case FPreset of
  150. // pepNone is handled in the first line.
  151. pepGray: MakeGrayEffect;
  152. pepNegative: MakeNegativeEffect;
  153. pepDistort: MakeDistortEffect;
  154. pepNoise: MakeNoiseEffect;
  155. pepNightVision: MakeNightVisionEffect;
  156. pepBlur: MakeBlurEffect(rci);
  157. pepCustom: DoOnCustomEffect(rci, FRenderBuffer);
  158. else
  159. Assert(False, strErrorEx + strUnknownType);
  160. end;
  161. glDrawPixels(rci.viewPortSize.cx, rci.viewPortSize.cy, GL_RGBA, GL_UNSIGNED_BYTE, FRenderBuffer);
  162. end;
  163. // Start rendering children (if any).
  164. if renderChildren then
  165. Self.RenderChildren(0, Count - 1, rci);
  166. end;
  167. procedure TgxPostEffect.MakeGrayEffect;
  168. var
  169. I: Longword;
  170. gray: GLubyte;
  171. begin
  172. for I := 0 to High(FRenderBuffer) do
  173. begin
  174. gray := Round((0.30 * FRenderBuffer[I].r) +
  175. (0.59 * FRenderBuffer[I].g) +
  176. (0.11 * FRenderBuffer[I].b));
  177. FRenderBuffer[I].r := gray;
  178. FRenderBuffer[I].g := gray;
  179. FRenderBuffer[I].b := gray;
  180. end;
  181. end;
  182. procedure TgxPostEffect.MakeNegativeEffect;
  183. var
  184. I: Longword;
  185. begin
  186. for I := 0 to High(FRenderBuffer) do
  187. begin
  188. FRenderBuffer[I].r := 255 - FRenderBuffer[I].r;
  189. FRenderBuffer[I].g := 255 - FRenderBuffer[I].g;
  190. FRenderBuffer[I].b := 255 - FRenderBuffer[I].b;
  191. end;
  192. end;
  193. procedure TgxPostEffect.MakeDistortEffect;
  194. var
  195. I: Integer;
  196. lMaxLength: Integer;
  197. lNewIndex: Integer;
  198. begin
  199. lMaxLength := High(FRenderBuffer);
  200. for I := 0 to lMaxLength do
  201. begin
  202. lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(10) - 5));
  203. FRenderBuffer[I].r := FRenderBuffer[lNewIndex].r;
  204. FRenderBuffer[I].g := FRenderBuffer[lNewIndex].g;
  205. FRenderBuffer[I].b := FRenderBuffer[lNewIndex].b;
  206. end;
  207. end;
  208. procedure TgxPostEffect.MakeNoiseEffect;
  209. var
  210. I: Longword;
  211. rnd: Single;
  212. begin
  213. for I := 0 to High(FRenderBuffer) do
  214. begin
  215. rnd := 0.25 + Random(75)/100;
  216. FRenderBuffer[I].r := Round(FRenderBuffer[I].r * rnd);
  217. FRenderBuffer[I].g := Round(FRenderBuffer[I].g * rnd);
  218. FRenderBuffer[I].b := Round(FRenderBuffer[I].b * rnd);
  219. end;
  220. end;
  221. procedure TgxPostEffect.MakeNightVisionEffect;
  222. var
  223. gray: Single;
  224. I: Integer;
  225. lNewIndex, lMaxLength: Integer;
  226. begin
  227. lMaxLength := High(FRenderBuffer);
  228. for I := 0 to lMaxLength do
  229. begin
  230. lNewIndex := MaxInteger(0, MinInteger(lMaxLength, I + Random(20) - 10));
  231. gray := 60 + (0.30 * FRenderBuffer[lNewIndex].r) +
  232. (0.59 * FRenderBuffer[lNewIndex].g) +
  233. (0.11 * FRenderBuffer[lNewIndex].b);
  234. FRenderBuffer[I].r := Round(gray * 0.25);
  235. FRenderBuffer[I].g := Round((gray + 4) * 0.6);
  236. FRenderBuffer[I].b := Round((gray + 4) * 0.11);
  237. end;
  238. end;
  239. procedure TgxPostEffect.MakeBlurEffect(var rci : TgxRenderContextInfo);
  240. const
  241. lOffset: Integer = 2;
  242. var
  243. I: Integer;
  244. lUp: Integer;
  245. begin
  246. lUp := rci.viewPortSize.cx * lOffset;
  247. for I := lUp to High(FRenderBuffer) - lUp do
  248. begin
  249. FRenderBuffer[I].r := (FRenderBuffer[I].r + FRenderBuffer[I - lOffset].r +
  250. FRenderBuffer[I + lOffset].r + FRenderBuffer[I - lUp].r +
  251. FRenderBuffer[I + lUp].r) div 5;
  252. FRenderBuffer[I].g := (FRenderBuffer[I].g + FRenderBuffer[I - lOffset].g +
  253. FRenderBuffer[I + lOffset].g + FRenderBuffer[I - lUp].g +
  254. FRenderBuffer[I + lUp].r) div 5;
  255. FRenderBuffer[I].b := (FRenderBuffer[I].b + FRenderBuffer[I - lOffset].b +
  256. FRenderBuffer[I + lOffset].b + FRenderBuffer[I - lUp].g +
  257. FRenderBuffer[I + lUp].r) div 5;
  258. end;
  259. end;
  260. //-------------------------------
  261. // TgxPostShaderCollectionItem
  262. //-------------------------------
  263. procedure TgxPostShaderCollectionItem.Assign(Source: TPersistent);
  264. begin
  265. if Source is TgxPostShaderCollectionItem then
  266. begin
  267. SetShader(TgxPostShaderCollectionItem(Source).FShader);
  268. end
  269. else
  270. inherited; // Die!!!
  271. end;
  272. function TgxPostShaderCollectionItem.GetDisplayName: string;
  273. begin
  274. if FShader = nil then
  275. Result := ''
  276. else
  277. begin
  278. if FShader.Name <> '' then
  279. Result := FShader.Name
  280. else
  281. Result := FShader.ClassName;
  282. end;
  283. end;
  284. type
  285. // Required for Delphi5 compatibility.
  286. THackCollection = class(TOwnedCollection)end;
  287. function TgxPostShaderCollectionItem.GetRealOwner: TgxPostShaderHolder;
  288. begin
  289. if Collection = nil then
  290. Result := nil
  291. else
  292. Result := TgxPostShaderHolder(THackCollection(Collection).GetOwner);
  293. end;
  294. procedure TgxPostShaderCollectionItem.SetShader(const Value: TgxShader);
  295. var
  296. RealOwner: TgxPostShaderHolder;
  297. begin
  298. if FShader = Value then Exit;
  299. RealOwner := GetRealOwner;
  300. if FShader <> nil then
  301. FShader.RemoveFreeNotification(RealOwner);
  302. if not Supports(TObject(Value), IgxPostShader, FPostShaderInterface) then
  303. raise EGLPostShaderHolderException.Create('Shader must support interface IPostShader!');
  304. if RealOwner <> nil then
  305. if FPostShaderInterface.GetTextureTarget <> RealOwner.TempTextureTarget then
  306. raise EGLPostShaderHolderException.Create(strErrorEx + 'TextureTarget is not compatible!');
  307. // If RealOwner = nil, we ignore this case and hope it will turn out ok...
  308. FShader := Value;
  309. if FShader <> nil then
  310. if RealOwner <> nil then
  311. FShader.FreeNotification(RealOwner);
  312. end;
  313. //-------------------------------
  314. // TgxPostShaderHolder
  315. //-------------------------------
  316. procedure TgxPostShaderHolder.Assign(Source: TPersistent);
  317. begin
  318. if Source is TgxPostShaderHolder then
  319. begin
  320. FShaders.Assign(TgxPostShaderHolder(Source).FShaders);
  321. FTempTextureTarget := TgxPostShaderHolder(Source).FTempTextureTarget;
  322. end;
  323. inherited;
  324. end;
  325. constructor TgxPostShaderHolder.Create(Owner: TComponent);
  326. begin
  327. inherited;
  328. FTempTexture := TgxTextureHandle.Create;
  329. FTempTextureTarget :=ttTexture2D;
  330. FShaders := TgxPostShaderCollection.Create(Self, TgxPostShaderCollectionItem);
  331. end;
  332. destructor TgxPostShaderHolder.Destroy;
  333. begin
  334. FShaders.Destroy;
  335. FTempTexture.Destroy;
  336. inherited;
  337. end;
  338. procedure TgxPostShaderHolder.DoRender(var rci: TgxRenderContextInfo;
  339. renderSelf, renderChildren: Boolean);
  340. var
  341. I: Integer;
  342. begin
  343. if not (rci.ignoreMaterials) and not (csDesigning in ComponentState) and
  344. (rci.drawState <> dsPicking) then
  345. begin
  346. if (FPreviousViewportSize.cx <> rci.viewPortSize.cx) or
  347. (FPreviousViewportSize.cy <> rci.viewPortSize.cy) then
  348. begin
  349. InitTexture(FTempTexture.Handle, rci.viewPortSize,
  350. FTempTextureTarget);
  351. FPreviousViewportSize := rci.viewPortSize;
  352. end;
  353. if FShaders.Count <> 0 then
  354. begin
  355. for I := 0 to FShaders.Count - 1 do
  356. begin
  357. Assert(Assigned(FShaders[I].FShader));
  358. if FShaders[I].FShader.Enabled then
  359. begin
  360. rci.gxStates.ActiveTextureEnabled[FTempTextureTarget] := True;
  361. FShaders[I].FShader.Apply(rci, Self);
  362. repeat
  363. CopyScreenToTexture(rci.viewPortSize, DecodeTextureTarget(FTempTextureTarget));
  364. FShaders[I].FPostShaderInterface.DoUseTempTexture(FTempTexture, FTempTextureTarget);
  365. DrawTexturedScreenQuad5(rci.viewPortSize);
  366. until not FShaders[I].FShader.UnApply(rci);
  367. rci.gxStates.ActiveTextureEnabled[FTempTextureTarget] := False;
  368. end;
  369. end;
  370. end;
  371. end;
  372. if renderChildren then
  373. Self.RenderChildren(0, Count - 1, rci);
  374. end;
  375. procedure TgxPostShaderHolder.Notification(AComponent: TComponent;
  376. Operation: TOperation);
  377. begin
  378. inherited;
  379. if Operation = opRemove then
  380. begin
  381. if AComponent is TgxShader then
  382. FShaders.Remove(TgxShader(AComponent));
  383. end;
  384. end;
  385. procedure TgxPostShaderHolder.SetShaders(
  386. const Value: TgxPostShaderCollection);
  387. begin
  388. FShaders.Assign(Value);
  389. end;
  390. //-------------------------------
  391. // TgxPostShaderCollection
  392. //-------------------------------
  393. function TgxPostShaderCollection.Add: TgxPostShaderCollectionItem;
  394. begin
  395. Result := TgxPostShaderCollectionItem(inherited Add);
  396. end;
  397. function TgxPostShaderCollection.GetItems(
  398. const Index: Integer): TgxPostShaderCollectionItem;
  399. begin
  400. Result := TgxPostShaderCollectionItem(GetItem(Index));
  401. end;
  402. procedure TgxPostShaderCollection.Remove(
  403. const Item: TgxShader);
  404. var
  405. I: Integer;
  406. begin
  407. if Count <> 0 then
  408. for I := Count - 1 downto 0 do
  409. if GetItems(I).FShader = Item then
  410. Delete(I);
  411. // Don't exit because the same shader might be applied more than once.
  412. end;
  413. procedure TgxPostShaderCollection.SetItems(const Index: Integer;
  414. const Value: TgxPostShaderCollectionItem);
  415. begin
  416. GetItems(Index).Assign(Value);
  417. end;
  418. //---------------------------------------------------------
  419. initialization
  420. //---------------------------------------------------------
  421. RegisterClasses([TgxPostEffect, TgxPostShaderHolder,
  422. TgxPostShaderCollection, TgxPostShaderCollectionItem]);
  423. end.