GXSL.ProjectedTextures.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXSL.ProjectedTextures;
  5. (* Implements projected textures through a GLScene object 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. GXS.PersistentClasses,
  23. GXS.Scene,
  24. GXS.Texture,
  25. Stage.VectorGeometry,
  26. GXS.Context,
  27. GXS.Color,
  28. GXS.RenderContextInfo,
  29. Stage.TextureFormat,
  30. Stage.PipelineTransform,
  31. Stage.VectorTypes;
  32. type
  33. TgxslProjectedTexturesStyle = (ptsLight, ptsShadow);
  34. TgxslProjectedTextures = class;
  35. (* A projected texture emitter.
  36. Can be places anywhere in the scene.
  37. Used to generate a modelview and texture matrix for the shader *)
  38. TgxslTextureEmitter = class(TgxBaseSceneObject)
  39. private
  40. FFOV: single;
  41. FAspect, FBrightness, FAttenuation: single;
  42. FStyle: TgxslProjectedTexturesStyle;
  43. FColor: TgxColor;
  44. FUseAttenuation, FAllowReverseProjection: boolean;
  45. FUseQuadraticAttenuation: boolean;
  46. protected
  47. ProjectedTexturesObject: TgxslProjectedTextures;
  48. TexMatrix: TMatrix4f;
  49. procedure SetupTexMatrix;
  50. procedure SetStyle(val: TgxslProjectedTexturesStyle);
  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: TgxRenderContextInfo; 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: TgxslProjectedTexturesStyle 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: TgxColor 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 TgxslTextureEmitters collection.
  89. TgxslTextureEmitterItem = class(TCollectionItem)
  90. private
  91. FEmitter: TgxslTextureEmitter;
  92. protected
  93. procedure SetEmitter(const val: TgxslTextureEmitter);
  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: TgxslTextureEmitter read FEmitter write SetEmitter;
  101. end;
  102. // Collection of TgxslTextureEmitter.
  103. TgxslTextureEmitters = class(TCollection)
  104. private
  105. FOwner: TgxslProjectedTextures;
  106. protected
  107. function GetOwner: TPersistent; override;
  108. function GetItems(index: Integer): TgxslTextureEmitterItem;
  109. procedure RemoveNotification(aComponent: TComponent);
  110. public
  111. procedure AddEmitter(texEmitter: TgxslTextureEmitter);
  112. property Items[index: Integer]: TgxslTextureEmitterItem 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. TgxslProjectedTextures = class(TgxSceneObject)
  118. private
  119. FEmitters: TgxslTextureEmitters;
  120. FUseLightmaps: boolean;
  121. Shader: TgxProgramHandle;
  122. FAmbient: TgxColor;
  123. procedure SetupShader;
  124. protected
  125. ShaderChanged: boolean;
  126. procedure SetUseLightmaps(val: boolean);
  127. public
  128. constructor Create(AOwner: TComponent); override;
  129. destructor Destroy; override;
  130. procedure DoRender(var rci: TgxRenderContextInfo;
  131. renderSelf, renderChildren: Boolean); override;
  132. procedure StructureChanged; override;
  133. published
  134. // List of emitters.
  135. property Emitters: TgxslTextureEmitters read FEmitters write FEmitters;
  136. //Ambient is use if no lightmap..
  137. property Ambient: TgxColor read fAmbient write fAmbient;
  138. property UseLightmaps: boolean read FUseLightmaps write SetUseLightmaps;
  139. end;
  140. //---------------------------------------------------------------------------
  141. implementation
  142. //---------------------------------------------------------------------------
  143. // ------------------
  144. // ------------------ TgxslTextureEmitter ------------------
  145. // ------------------
  146. constructor TgxslTextureEmitter.Create(aOwner: TComponent);
  147. begin
  148. inherited Create(aOwner);
  149. FFOV := 90;
  150. FAspect := 1;
  151. FStyle := ptsLight;
  152. FAllowReverseProjection := false;
  153. FUseAttenuation := false;
  154. FAttenuation := 100;
  155. FBrightness := 1;
  156. FColor := TgxColor.create(self);
  157. FColor.SetColor(1, 1, 1);
  158. end;
  159. destructor TgxslTextureEmitter.Destroy;
  160. begin
  161. FColor.Free;
  162. inherited;
  163. end;
  164. procedure TgxslTextureEmitter.DoRender(var rci: TgxRenderContextInfo;
  165. renderSelf, renderChildren: boolean);
  166. begin
  167. SetupTexMatrix;
  168. inherited;
  169. end;
  170. procedure TgxslTextureEmitter.SetupTexMatrix;
  171. const
  172. cBaseMat: TMatrix4f = (V:((X:0.5; Y:0; Z:0; W:0),
  173. (X:0; Y:0.5; Z:0; W:0),
  174. (X:0; Y:0; Z:1; W:0),
  175. (X:0.5; Y:0.5; Z:0; W:1)));
  176. begin
  177. // Set the projector's "perspective" (i.e. the "spotlight cone"):.
  178. TexMatrix := MatrixMultiply(
  179. CreatePerspectiveMatrix(FFOV, FAspect, 0.1, 1), cBaseMat);
  180. TexMatrix := MatrixMultiply(
  181. CurrentContext.PipelineTransformation.InvModelViewMatrix^, TexMatrix);
  182. end;
  183. procedure TgxslTextureEmitter.SetAllowReverseProjection(val: boolean);
  184. begin
  185. FAllowReverseProjection := val;
  186. if assigned(ProjectedTexturesObject) then
  187. ProjectedTexturesObject.ShaderChanged := true;
  188. end;
  189. procedure TgxslTextureEmitter.SetUseAttenuation(val: boolean);
  190. begin
  191. FUseAttenuation := val;
  192. if assigned(ProjectedTexturesObject) then
  193. ProjectedTexturesObject.ShaderChanged := true;
  194. end;
  195. procedure TgxslTextureEmitter.SetUseQuadraticAttenuation(val: boolean);
  196. begin
  197. FUseQuadraticAttenuation := val;
  198. if assigned(ProjectedTexturesObject) then
  199. ProjectedTexturesObject.ShaderChanged := true;
  200. end;
  201. procedure TgxslTextureEmitter.SetStyle(val: TgxslProjectedTexturesStyle);
  202. begin
  203. FStyle := val;
  204. if assigned(ProjectedTexturesObject) then
  205. ProjectedTexturesObject.ShaderChanged := true;
  206. end;
  207. // ------------------
  208. // ------------------ TgxslTextureEmitterItem ------------------
  209. // ------------------
  210. constructor TgxslTextureEmitterItem.Create(Collection: TCollection);
  211. begin
  212. inherited Create(Collection);
  213. end;
  214. procedure TgxslTextureEmitterItem.Assign(Source: TPersistent);
  215. begin
  216. if Source is TgxslTextureEmitterItem then
  217. begin
  218. FEmitter := TgxslTextureEmitterItem(Source).FEmitter;
  219. TgxslProjectedTextures(TgxslTextureEmitters(Collection).GetOwner).StructureChanged;
  220. end;
  221. inherited;
  222. end;
  223. procedure TgxslTextureEmitterItem.SetEmitter(const val: TgxslTextureEmitter);
  224. begin
  225. if FEmitter <> val then
  226. begin
  227. FEmitter := val;
  228. TgxslProjectedTextures(TgxslTextureEmitters(Collection).GetOwner).StructureChanged;
  229. end;
  230. end;
  231. procedure TgxslTextureEmitterItem.RemoveNotification(aComponent: TComponent);
  232. begin
  233. if aComponent = FEmitter then
  234. FEmitter := nil;
  235. end;
  236. function TgxslTextureEmitterItem.GetDisplayName: string;
  237. begin
  238. if Assigned(FEmitter) then
  239. begin
  240. Result := '[Emitter] ' + FEmitter.Name;
  241. end
  242. else
  243. Result := 'nil';
  244. end;
  245. // ------------------
  246. // ------------------ TgxslTextureEmitters ------------------
  247. // ------------------
  248. function TgxslTextureEmitters.GetOwner: TPersistent;
  249. begin
  250. Result := FOwner;
  251. end;
  252. function TgxslTextureEmitters.GetItems(index: Integer): TgxslTextureEmitterItem;
  253. begin
  254. Result := TgxslTextureEmitterItem(inherited Items[index]);
  255. end;
  256. procedure TgxslTextureEmitters.RemoveNotification(aComponent: TComponent);
  257. var
  258. i: Integer;
  259. begin
  260. for i := 0 to Count - 1 do
  261. begin
  262. Items[i].RemoveNotification(aComponent);
  263. TgxslProjectedTextures(GetOwner).shaderChanged := true;
  264. end;
  265. end;
  266. procedure TgxslTextureEmitters.AddEmitter(texEmitter: TgxslTextureEmitter);
  267. var
  268. item: TgxslTextureEmitterItem;
  269. begin
  270. item := TgxslTextureEmitterItem(self.Add);
  271. item.Emitter := texEmitter;
  272. item.Emitter.ProjectedTexturesObject := TgxslProjectedTextures(GetOwner);
  273. TgxslProjectedTextures(GetOwner).shaderChanged := true;
  274. end;
  275. // ------------------
  276. // ------------------ TgxslProjectedTextures ------------------
  277. // ------------------
  278. constructor TgxslProjectedTextures.Create(AOwner: TComponent);
  279. begin
  280. inherited Create(aOWner);
  281. FEmitters := TgxslTextureEmitters.Create(TgxslTextureEmitterItem);
  282. FEmitters.FOwner := self;
  283. FUseLightmaps := false;
  284. ShaderChanged := true;
  285. Ambient := TgxColor.Create(self);
  286. ambient.SetColor(0.5, 0.5, 0.5, 0.5);
  287. end;
  288. destructor TgxslProjectedTextures.Destroy;
  289. begin
  290. if assigned(shader) then
  291. Shader.free;
  292. FEmitters.Free;
  293. Ambient.Free;
  294. inherited destroy;
  295. end;
  296. procedure TgxslProjectedTextures.SetUseLightmaps(val: boolean);
  297. begin
  298. FUseLightmaps := val;
  299. ShaderChanged := true;
  300. end;
  301. procedure TgxslProjectedTextures.SetupShader;
  302. const
  303. AbsFunc: array[boolean] of string = ('', 'abs');
  304. var
  305. vp, fp: TStringlist;
  306. i: integer;
  307. emitter: TgxslTextureEmitter;
  308. OldSeparator: char;
  309. begin
  310. if assigned(shader) then
  311. FreeAndNil(shader);
  312. Shader := TgxProgramHandle.CreateAndAllocate;
  313. OldSeparator := FormatSettings.DecimalSeparator;
  314. FormatSettings.DecimalSeparator := '.';
  315. vp := TStringlist.create;
  316. fp := TStringlist.create;
  317. try
  318. //define the vertex program
  319. if emitters.count > 0 then
  320. begin
  321. for i := 0 to emitters.count - 1 do
  322. begin
  323. emitter := Emitters[i].Emitter;
  324. if not assigned(emitter) then
  325. continue;
  326. if not emitter.Visible then
  327. continue;
  328. vp.add(format('uniform mat4 TextureMatrix%d;', [i]));
  329. vp.add(format('varying vec4 ProjTexCoords%d;', [i]));
  330. end;
  331. end;
  332. vp.add('void main(){');
  333. vp.add('vec4 P = gl_Vertex;');
  334. vp.add('gl_Position = gl_ModelViewProjectionMatrix * P;');
  335. vp.add('vec4 Pe = gl_ModelViewMatrix * P;');
  336. vp.add('gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;');
  337. if UseLightmaps then
  338. vp.add('gl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;');
  339. if emitters.count > 0 then
  340. begin
  341. for i := 0 to emitters.count - 1 do
  342. begin
  343. emitter := Emitters[i].Emitter;
  344. if not assigned(emitter) then
  345. continue;
  346. vp.add(format('ProjTexCoords%d = TextureMatrix%d * Pe;', [i, i]));
  347. end;
  348. end;
  349. vp.add('}');
  350. //define the fragment program
  351. fp.add('uniform sampler2D TextureMap;');
  352. if UseLightmaps then
  353. fp.add('uniform sampler2D LightMap;');
  354. if emitters.count > 0 then
  355. begin
  356. fp.add('uniform sampler2D ProjMap;');
  357. for i := 0 to emitters.count - 1 do
  358. begin
  359. emitter := Emitters[i].Emitter;
  360. if not assigned(emitter) then
  361. continue;
  362. fp.add(format('varying vec4 ProjTexCoords%d;', [i]));
  363. if Emitter.UseAttenuation then
  364. fp.add(format('uniform float Attenuation%d;', [i]));
  365. fp.add(format('uniform float Brightness%d;', [i]));
  366. fp.add(format('uniform vec3 Color%d;', [i]));
  367. end;
  368. end;
  369. fp.add('void main(){');
  370. fp.add('vec4 color = texture2D(TextureMap, gl_TexCoord[0].st).rgba;');
  371. if UseLightmaps then
  372. fp.add('vec3 light = texture2D(LightMap, gl_TexCoord[1].st).rgb;')
  373. else
  374. fp.add(format('vec3 light = vec3(%.4, %.4, %.4);', [Ambient.Red, ambient.Green, ambient.Blue]));
  375. if emitters.count > 0 then
  376. begin
  377. fp.add('vec3 projlight = vec3(0.0);');
  378. fp.add('vec3 projshadow = vec3(0.0);');
  379. fp.add('vec3 temp;');
  380. fp.add('float dist;');
  381. for i := 0 to emitters.count - 1 do
  382. begin
  383. emitter := Emitters[i].Emitter;
  384. if not assigned(emitter) then
  385. continue;
  386. if not emitter.visible then
  387. continue;
  388. if not emitter.AllowReverseProjection then
  389. fp.add(format('if (ProjTexCoords%d.q<0.0){', [i]));
  390. case emitter.Style of
  391. ptslight:
  392. fp.add(format('projlight+= (texture2DProj(ProjMap, ProjTexCoords%d).rgb*Color%d*Brightness%d);', [i, i, i]));
  393. ptsShadow:
  394. fp.add(format('projshadow+= (texture2DProj(ProjMap, ProjTexCoords%d).rgb*Color%d*Brightness%d);', [i, i, i]));
  395. end;
  396. if emitter.UseAttenuation then
  397. begin
  398. // for attenuation we need the distance to the point
  399. // so use absolute value when AllowReverseProjection is enabled
  400. fp.add(format('dist = 1.0 - clamp(%s(ProjTexCoords%d.q/Attenuation%d), 0.0, 1.0);',
  401. [AbsFunc[emitter.AllowReverseProjection], i, i]));
  402. if emitter.UseQuadraticAttenuation then
  403. fp.add('dist *= dist;');
  404. case emitter.Style of
  405. ptslight:
  406. fp.add('projlight *= dist;');
  407. ptsShadow:
  408. fp.add('projshadow *= dist;');
  409. end;
  410. end;
  411. if not emitter.AllowReverseProjection then
  412. fp[fp.Count - 1] := fp[fp.Count - 1] + '}';
  413. end;
  414. fp.add('projlight = clamp(projlight,0.0,1.2);');
  415. fp.add('projshadow = clamp(projshadow,0.0,0.8);');
  416. fp.add('vec3 totlight = 1.0-((( 1.0-projlight)*( 1.0-light)) +(projshadow*light)) ;');
  417. end
  418. else
  419. fp.add('vec3 totlight = light;');
  420. fp.add('gl_FragColor = vec4(1.5*totlight * color.rgb, color.a);}');
  421. Shader.AddShader(TgxVertexShaderHandle, vp.Text, True);
  422. Shader.AddShader(TgxFragmentShaderHandle, fp.Text, True);
  423. finally
  424. FormatSettings.DecimalSeparator := OldSeparator;
  425. vp.free;
  426. fp.free;
  427. end;
  428. if not Shader.LinkProgram then
  429. raise Exception.Create(Shader.InfoLog);
  430. if not Shader.ValidateProgram then
  431. raise Exception.Create(Shader.InfoLog);
  432. end;
  433. procedure TgxslProjectedTextures.DoRender(var rci: TgxRenderContextInfo;
  434. renderSelf, renderChildren: boolean);
  435. var
  436. i: integer;
  437. emitter: TgxslTextureEmitter;
  438. begin
  439. if not (renderSelf or renderChildren) then
  440. Exit;
  441. if (csDesigning in ComponentState) then
  442. begin
  443. inherited;
  444. Exit;
  445. end;
  446. if ShaderChanged then
  447. begin
  448. SetupShader;
  449. ShaderChanged := false;
  450. end;
  451. with Shader do
  452. begin
  453. UseProgramObject;
  454. for i := 0 to Emitters.Count - 1 do
  455. begin
  456. emitter := Emitters[i].Emitter;
  457. if not assigned(emitter) then
  458. continue;
  459. if emitter.UseAttenuation then
  460. // negate attenuation here, instead of negating q inside the shader
  461. // otherwise the result of q/attenuation is negative.
  462. Uniform1f['Attenuation' + inttostr(i)] := -emitter.Attenuation;
  463. Uniform1f['Brightness' + inttostr(i)] := emitter.Brightness;
  464. Uniform3f['Color' + inttostr(i)] := PAffinevector(@emitter.Color.Color)^;
  465. Uniformmatrix4fv['TextureMatrix' + inttostr(i)] := emitter.texMatrix;
  466. end;
  467. Uniform1i['TextureMap'] := 0;
  468. if UseLightmaps then
  469. Uniform1i['LightMap'] := 1;
  470. if emitters.count > 0 then
  471. Shader.Uniform1i['ProjMap'] := 2;
  472. rci.gxStates.TextureBinding[2, ttTexture2D] := Material.Texture.Handle;
  473. self.RenderChildren(0, Count - 1, rci);
  474. EndUseProgramObject;
  475. end;
  476. end;
  477. procedure TgxslProjectedTextures.StructureChanged;
  478. begin
  479. inherited;
  480. shaderchanged := true;
  481. end;
  482. //===========================================================
  483. initialization
  484. //===========================================================
  485. RegisterClasses([TgxslTextureEmitter, TgxslProjectedTextures]);
  486. end.