GLSL.ProjectedTextures.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLSL.ProjectedTextures;
  5. (* Implements projected textures via GLSL.
  6. Known bugs/limitations
  7. 1. Only 1 texture can be used for all emitters
  8. 2. Only up to 6 Emitters can be used (more on better cards)
  9. A way round this is to make the emiitters a children of the 6 nearest objects
  10. to the camera.
  11. 3. Changing emitter properties causes a slight delay while recreating the shader.
  12. To make an emitter invisible, just move it to somewhere it won't project on
  13. anything, or set the brightness to 0. (?)
  14. 4. All children of the ProjectedTextures must have use a texture.
  15. The shader can't be changed between rendering each seperate object..
  16. *)
  17. interface
  18. {$I Stage.Defines.inc}
  19. uses
  20. System.Classes,
  21. System.SysUtils,
  22. Stage.PipelineTransform,
  23. GLS.Scene,
  24. GLS.PersistentClasses,
  25. GLS.Texture,
  26. Stage.VectorGeometry,
  27. GLS.Context,
  28. GLS.Color,
  29. GLS.RenderContextInfo,
  30. Stage.TextureFormat,
  31. Stage.VectorTypes;
  32. type
  33. TGLSLProjectedTexturesStyle = (ptsLight, ptsShadow);
  34. TGLSLProjectedTextures = class;
  35. (* A projected texture emmiter.
  36. Can be places anywhere in the scene.
  37. Used to generate a modelview and texture matrix for the shader*)
  38. TGLSLTextureEmitter = class(TGLBaseSceneObject)
  39. private
  40. FFOV: single;
  41. FAspect, FBrightness, FAttenuation: single;
  42. FStyle: TGLSLProjectedTexturesStyle;
  43. FColor: TGLColor;
  44. FUseAttenuation, FAllowReverseProjection: boolean;
  45. FUseQuadraticAttenuation: boolean;
  46. protected
  47. ProjectedTexturesObject: TGLSLProjectedTextures;
  48. TexMatrix: TGLMatrix;
  49. procedure SetupTexMatrix;
  50. procedure SetStyle(val: TGLSLProjectedTexturesStyle);
  51. procedure SetUseAttenuation(val: boolean);
  52. procedure SetUseQuadraticAttenuation(val: boolean);
  53. procedure SetAllowReverseProjection(val: boolean);
  54. public
  55. constructor Create(AOwner: TComponent); override;
  56. destructor Destroy; override;
  57. procedure DoRender(var rci: TGLRenderContextInfo; renderSelf, renderChildren: boolean); override;
  58. published
  59. // Indicates the field-of-view of the projection frustum.
  60. property FOV: single read FFOV write FFOV;
  61. (* x/y ratio. For no distortion, this should be set to
  62. texture.width/texture.height.*)
  63. property Aspect: single read FAspect write FAspect;
  64. // Indicates the style of the projected textures.
  65. property Style: TGLSLProjectedTexturesStyle read FStyle write SetStyle;
  66. // Fall off/ attenuation of the projected texture
  67. property Attenuation: single read FAttenuation write FAttenuation;
  68. property Brightness: single read FBrightness write FBrightness;
  69. property Color: TGLColor read FColor write FColor;
  70. property UseAttenuation: boolean read FUseAttenuation write SetUseAttenuation;
  71. property UseQuadraticAttenuation: Boolean read FUseQuadraticAttenuation write SetUseQuadraticAttenuation;
  72. property AllowReverseProjection: boolean read FAllowReverseProjection write SetAllowReverseProjection;
  73. property ObjectsSorting;
  74. property VisibilityCulling;
  75. property Direction;
  76. property PitchAngle;
  77. property Position;
  78. property RollAngle;
  79. property Scale;
  80. property ShowAxes;
  81. property TurnAngle;
  82. property Up;
  83. property Visible;
  84. property OnProgress;
  85. property Behaviours;
  86. property Effects;
  87. end;
  88. // Specifies an item on the TGLSLTextureEmitters collection.
  89. TGLSLTextureEmitterItem = class(TCollectionItem)
  90. private
  91. FEmitter: TGLSLTextureEmitter;
  92. protected
  93. procedure SetEmitter(const val: TGLSLTextureEmitter);
  94. procedure RemoveNotification(aComponent: TComponent);
  95. function GetDisplayName: string; override;
  96. public
  97. constructor Create(Collection: TCollection); override;
  98. procedure Assign(Source: TPersistent); override;
  99. published
  100. property Emitter: TGLSLTextureEmitter read FEmitter write SetEmitter;
  101. end;
  102. // Collection of TGLSLTextureEmitter.
  103. TGLSLTextureEmitters = class(TCollection)
  104. private
  105. FOwner: TGLSLProjectedTextures;
  106. protected
  107. function GetOwner: TPersistent; override;
  108. function GetItems(index: Integer): TGLSLTextureEmitterItem;
  109. procedure RemoveNotification(aComponent: TComponent);
  110. public
  111. procedure AddEmitter(texEmitter: TGLSLTextureEmitter);
  112. property Items[index: Integer]: TGLSLTextureEmitterItem read GetItems; default;
  113. end;
  114. (* Projected Texture Manager.
  115. Specifies active Emitters and receivers (children of this object).
  116. At the moment, only 1 texture can be used.*)
  117. TGLSLProjectedTextures = class(TGLSceneObject)
  118. private
  119. ShaderSupported: boolean;
  120. FEmitters: TGLSLTextureEmitters;
  121. FUseLightmaps: boolean;
  122. Shader: TGLProgramHandle;
  123. FAmbient: TGLColor;
  124. procedure SetupShader;
  125. protected
  126. ShaderChanged: boolean;
  127. procedure SetUseLightmaps(val: boolean);
  128. public
  129. constructor Create(AOwner: TComponent); override;
  130. destructor Destroy; override;
  131. procedure DoRender(var rci: TGLRenderContextInfo;
  132. renderSelf, renderChildren: Boolean); override;
  133. procedure StructureChanged; override;
  134. published
  135. // List of emitters.
  136. property Emitters: TGLSLTextureEmitters read FEmitters write FEmitters;
  137. //Ambient is use if no lightmap..
  138. property Ambient: TGLColor read fAmbient write fAmbient;
  139. property UseLightmaps: boolean read FUseLightmaps write SetUseLightmaps;
  140. end;
  141. //---------------------------------------------------------------------------
  142. implementation
  143. //---------------------------------------------------------------------------
  144. // ------------------
  145. // ------------------ TGLSLTextureEmitter ------------------
  146. // ------------------
  147. constructor TGLSLTextureEmitter.Create(aOwner: TComponent);
  148. begin
  149. inherited Create(aOwner);
  150. FFOV := 90;
  151. FAspect := 1;
  152. FStyle := ptsLight;
  153. FAllowReverseProjection := false;
  154. FUseAttenuation := false;
  155. FAttenuation := 100;
  156. FBrightness := 1;
  157. FColor := TGLColor.create(self);
  158. FColor.SetColor(1, 1, 1);
  159. end;
  160. destructor TGLSLTextureEmitter.Destroy;
  161. begin
  162. FColor.free;
  163. inherited;
  164. end;
  165. procedure TGLSLTextureEmitter.DoRender(var rci: TGLRenderContextInfo;
  166. renderSelf, renderChildren: boolean);
  167. begin
  168. SetupTexMatrix;
  169. inherited;
  170. end;
  171. procedure TGLSLTextureEmitter.SetupTexMatrix;
  172. const
  173. cBaseMat: TGLMatrix = (V:((X:0.5; Y:0; Z:0; W:0),
  174. (X:0; Y:0.5; Z:0; W:0),
  175. (X:0; Y:0; Z:1; W:0),
  176. (X:0.5; Y:0.5; Z:0; W:1)));
  177. begin
  178. // Set the projector's "perspective" (i.e. the "spotlight cone"):.
  179. TexMatrix := MatrixMultiply(
  180. CreatePerspectiveMatrix(FFOV, FAspect, 0.1, 1), cBaseMat);
  181. TexMatrix := MatrixMultiply(
  182. CurrentGLContext.PipelineTransformation.InvModelViewMatrix^, TexMatrix);
  183. end;
  184. procedure TGLSLTextureEmitter.SetAllowReverseProjection(val: boolean);
  185. begin
  186. FAllowReverseProjection := val;
  187. if assigned(ProjectedTexturesObject) then
  188. ProjectedTexturesObject.ShaderChanged := true;
  189. end;
  190. procedure TGLSLTextureEmitter.SetUseAttenuation(val: boolean);
  191. begin
  192. FUseAttenuation := val;
  193. if assigned(ProjectedTexturesObject) then
  194. ProjectedTexturesObject.ShaderChanged := true;
  195. end;
  196. procedure TGLSLTextureEmitter.SetUseQuadraticAttenuation(val: boolean);
  197. begin
  198. FUseQuadraticAttenuation := val;
  199. if assigned(ProjectedTexturesObject) then
  200. ProjectedTexturesObject.ShaderChanged := true;
  201. end;
  202. procedure TGLSLTextureEmitter.SetStyle(val: TGLSLProjectedTexturesStyle);
  203. begin
  204. FStyle := val;
  205. if assigned(ProjectedTexturesObject) then
  206. ProjectedTexturesObject.ShaderChanged := true;
  207. end;
  208. // ------------------
  209. // ------------------ TGLSLTextureEmitterItem ------------------
  210. // ------------------
  211. constructor TGLSLTextureEmitterItem.Create(Collection: TCollection);
  212. begin
  213. inherited Create(Collection);
  214. end;
  215. procedure TGLSLTextureEmitterItem.Assign(Source: TPersistent);
  216. begin
  217. if Source is TGLSLTextureEmitterItem then
  218. begin
  219. FEmitter := TGLSLTextureEmitterItem(Source).FEmitter;
  220. TGLSLProjectedTextures(TGLSLTextureEmitters(Collection).GetOwner).StructureChanged;
  221. end;
  222. inherited;
  223. end;
  224. procedure TGLSLTextureEmitterItem.SetEmitter(const val: TGLSLTextureEmitter);
  225. begin
  226. if FEmitter <> val then
  227. begin
  228. FEmitter := val;
  229. TGLSLProjectedTextures(TGLSLTextureEmitters(Collection).GetOwner).StructureChanged;
  230. end;
  231. end;
  232. procedure TGLSLTextureEmitterItem.RemoveNotification(aComponent: TComponent);
  233. begin
  234. if aComponent = FEmitter then
  235. FEmitter := nil;
  236. end;
  237. function TGLSLTextureEmitterItem.GetDisplayName: string;
  238. begin
  239. if Assigned(FEmitter) then
  240. begin
  241. Result := '[Emitter] ' + FEmitter.Name;
  242. end
  243. else
  244. Result := 'nil';
  245. end;
  246. // ------------------
  247. // ------------------ TGLSLTextureEmitters ------------------
  248. // ------------------
  249. function TGLSLTextureEmitters.GetOwner: TPersistent;
  250. begin
  251. Result := FOwner;
  252. end;
  253. function TGLSLTextureEmitters.GetItems(index: Integer): TGLSLTextureEmitterItem;
  254. begin
  255. Result := TGLSLTextureEmitterItem(inherited Items[index]);
  256. end;
  257. procedure TGLSLTextureEmitters.RemoveNotification(aComponent: TComponent);
  258. var
  259. i: Integer;
  260. begin
  261. for i := 0 to Count - 1 do
  262. begin
  263. Items[i].RemoveNotification(aComponent);
  264. TGLSLProjectedTextures(GetOwner).shaderChanged := true;
  265. end;
  266. end;
  267. procedure TGLSLTextureEmitters.AddEmitter(texEmitter: TGLSLTextureEmitter);
  268. var
  269. item: TGLSLTextureEmitterItem;
  270. begin
  271. item := TGLSLTextureEmitterItem(self.Add);
  272. item.Emitter := texEmitter;
  273. item.Emitter.ProjectedTexturesObject := TGLSLProjectedTextures(GetOwner);
  274. TGLSLProjectedTextures(GetOwner).shaderChanged := true;
  275. end;
  276. // ------------------
  277. // ------------------ TGLSLProjectedTextures ------------------
  278. // ------------------
  279. constructor TGLSLProjectedTextures.Create(AOwner: TComponent);
  280. begin
  281. inherited Create(aOWner);
  282. FEmitters := TGLSLTextureEmitters.Create(TGLSLTextureEmitterItem);
  283. FEmitters.FOwner := self;
  284. FUseLightmaps := false;
  285. ShaderChanged := true;
  286. Ambient := TGLColor.Create(self);
  287. ambient.SetColor(0.5, 0.5, 0.5, 0.5);
  288. end;
  289. destructor TGLSLProjectedTextures.Destroy;
  290. begin
  291. if assigned(shader) then
  292. Shader.free;
  293. FEmitters.Free;
  294. Ambient.Free;
  295. inherited destroy;
  296. end;
  297. procedure TGLSLProjectedTextures.SetUseLightmaps(val: boolean);
  298. begin
  299. FUseLightmaps := val;
  300. ShaderChanged := true;
  301. end;
  302. procedure TGLSLProjectedTextures.SetupShader;
  303. const
  304. AbsFunc: array[boolean] of string = ('', 'abs');
  305. var
  306. vp, fp: TStringlist;
  307. i: integer;
  308. emitter: TGLSLTextureEmitter;
  309. OldSeparator: char;
  310. begin
  311. ShaderSupported := (GL.ARB_shader_objects and GL.ARB_vertex_program and
  312. GL.ARB_vertex_shader and GL.ARB_fragment_shader);
  313. if not ShaderSupported then
  314. exit;
  315. if assigned(shader) then
  316. FreeAndNil(shader);
  317. Shader := TGLProgramHandle.CreateAndAllocate;
  318. OldSeparator := FormatSettings.DecimalSeparator;
  319. FormatSettings.DecimalSeparator := '.';
  320. vp := TStringlist.create;
  321. fp := TStringlist.create;
  322. try
  323. //define the vertex program
  324. if emitters.count > 0 then
  325. begin
  326. for i := 0 to emitters.count - 1 do
  327. begin
  328. emitter := Emitters[i].Emitter;
  329. if not assigned(emitter) then
  330. continue;
  331. if not emitter.Visible then
  332. continue;
  333. vp.add(format('uniform mat4 TextureMatrix%d;', [i]));
  334. vp.add(format('varying vec4 ProjTexCoords%d;', [i]));
  335. end;
  336. end;
  337. vp.add('void main(){');
  338. vp.add('vec4 P = gl_Vertex;');
  339. vp.add('gl_Position = gl_ModelViewProjectionMatrix * P;');
  340. vp.add('vec4 Pe = gl_ModelViewMatrix * P;');
  341. vp.add('gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;');
  342. if UseLightmaps then
  343. vp.add('gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;');
  344. if emitters.count > 0 then
  345. begin
  346. for i := 0 to emitters.count - 1 do
  347. begin
  348. emitter := Emitters[i].Emitter;
  349. if not assigned(emitter) then
  350. continue;
  351. vp.add(format('ProjTexCoords%d = TextureMatrix%d * Pe;', [i, i]));
  352. end;
  353. end;
  354. vp.add('}');
  355. //define the fragment program
  356. fp.add('uniform sampler2D TextureMap;');
  357. if UseLightmaps then
  358. fp.add('uniform sampler2D LightMap;');
  359. if emitters.count > 0 then
  360. begin
  361. fp.add('uniform sampler2D ProjMap;');
  362. for i := 0 to emitters.count - 1 do
  363. begin
  364. emitter := Emitters[i].Emitter;
  365. if not assigned(emitter) then
  366. continue;
  367. fp.add(format('varying vec4 ProjTexCoords%d;', [i]));
  368. if Emitter.UseAttenuation then
  369. fp.add(format('uniform float Attenuation%d;', [i]));
  370. fp.add(format('uniform float Brightness%d;', [i]));
  371. fp.add(format('uniform vec3 Color%d;', [i]));
  372. end;
  373. end;
  374. fp.add('void main(){');
  375. fp.add('vec4 color = texture2D(TextureMap, gl_TexCoord[0].st).rgba;');
  376. if UseLightmaps then
  377. fp.add('vec3 light = texture2D(LightMap, gl_TexCoord[1].st).rgb;')
  378. else
  379. fp.add(format('vec3 light = vec3(%.4, %.4, %.4);', [Ambient.Red, ambient.Green, ambient.Blue]));
  380. if emitters.count > 0 then
  381. begin
  382. fp.add('vec3 projlight = vec3(0.0);');
  383. fp.add('vec3 projshadow = vec3(0.0);');
  384. fp.add('vec3 temp;');
  385. fp.add('float dist;');
  386. for i := 0 to emitters.count - 1 do
  387. begin
  388. emitter := Emitters[i].Emitter;
  389. if not assigned(emitter) then
  390. continue;
  391. if not emitter.visible then
  392. continue;
  393. if not emitter.AllowReverseProjection then
  394. fp.add(format('if (ProjTexCoords%d.q<0.0){', [i]));
  395. case emitter.Style of
  396. ptslight:
  397. fp.add(format('projlight+= (texture2DProj(ProjMap, ProjTexCoords%d).rgb*Color%d*Brightness%d);', [i, i, i]));
  398. ptsShadow:
  399. fp.add(format('projshadow+= (texture2DProj(ProjMap, ProjTexCoords%d).rgb*Color%d*Brightness%d);', [i, i, i]));
  400. end;
  401. if emitter.UseAttenuation then
  402. begin
  403. // for attenuation we need the distance to the point
  404. // so use absolute value when AllowReverseProjection is enabled
  405. fp.add(format('dist = 1.0 - clamp(%s(ProjTexCoords%d.q/Attenuation%d), 0.0, 1.0);',
  406. [AbsFunc[emitter.AllowReverseProjection], i, i]));
  407. if emitter.UseQuadraticAttenuation then
  408. fp.add('dist *= dist;');
  409. case emitter.Style of
  410. ptslight:
  411. fp.add('projlight *= dist;');
  412. ptsShadow:
  413. fp.add('projshadow *= dist;');
  414. end;
  415. end;
  416. if not emitter.AllowReverseProjection then
  417. fp[fp.Count - 1] := fp[fp.Count - 1] + '}';
  418. end;
  419. fp.add('projlight = clamp(projlight,0.0,1.2);');
  420. fp.add('projshadow = clamp(projshadow,0.0,0.8);');
  421. fp.add('vec3 totlight = 1.0-((( 1.0-projlight)*( 1.0-light)) +(projshadow*light)) ;');
  422. end
  423. else
  424. fp.add('vec3 totlight = light;');
  425. fp.add('gl_FragColor = vec4(1.5*totlight * color.rgb, color.a);}');
  426. Shader.AddShader(TGLVertexShaderHandle, vp.Text, True);
  427. Shader.AddShader(TGLFragmentShaderHandle, fp.Text, True);
  428. finally
  429. FormatSettings.DecimalSeparator := OldSeparator;
  430. vp.free;
  431. fp.free;
  432. end;
  433. if not Shader.LinkProgram then
  434. raise Exception.Create(Shader.InfoLog);
  435. if not Shader.ValidateProgram then
  436. raise Exception.Create(Shader.InfoLog);
  437. end;
  438. procedure TGLSLProjectedTextures.DoRender(var rci: TGLRenderContextInfo;
  439. renderSelf, renderChildren: boolean);
  440. var
  441. i: integer;
  442. emitter: TGLSLTextureEmitter;
  443. begin
  444. if not (renderSelf or renderChildren) then
  445. Exit;
  446. if (csDesigning in ComponentState) then
  447. begin
  448. inherited;
  449. Exit;
  450. end;
  451. if ShaderChanged then
  452. begin
  453. SetupShader;
  454. ShaderChanged := false;
  455. end;
  456. if ShaderSupported then
  457. with Shader do
  458. begin
  459. UseProgramObject;
  460. for i := 0 to Emitters.Count - 1 do
  461. begin
  462. emitter := Emitters[i].Emitter;
  463. if not assigned(emitter) then
  464. continue;
  465. if emitter.UseAttenuation then
  466. // negate attenuation here, instead of negating q inside the shader
  467. // otherwise the result of q/attenuation is negative.
  468. Uniform1f['Attenuation' + inttostr(i)] := -emitter.Attenuation;
  469. Uniform1f['Brightness' + inttostr(i)] := emitter.Brightness;
  470. Uniform3f['Color' + inttostr(i)] := PAffinevector(@emitter.Color.Color)^;
  471. Uniformmatrix4fv['TextureMatrix' + inttostr(i)] := emitter.texMatrix;
  472. end;
  473. Uniform1i['TextureMap'] := 0;
  474. if UseLightmaps then
  475. Uniform1i['LightMap'] := 1;
  476. if emitters.count > 0 then
  477. Shader.Uniform1i['ProjMap'] := 2;
  478. rci.GLStates.TextureBinding[2, ttTexture2D] := Material.Texture.Handle;
  479. self.RenderChildren(0, Count - 1, rci);
  480. EndUseProgramObject;
  481. end
  482. else
  483. self.RenderChildren(0, Count - 1, rci);
  484. end;
  485. procedure TGLSLProjectedTextures.StructureChanged;
  486. begin
  487. inherited;
  488. shaderchanged := true;
  489. end;
  490. initialization
  491. RegisterClasses([TGLSLTextureEmitter, TGLSLProjectedTextures]);
  492. end.