GLSL.PostEffects.pas 14 KB

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