GXS.PerlinPFX.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.PerlinPFX;
  5. (* PFX particle effects revolving around the use of Perlin noise *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. System.Classes,
  11. System.Math,
  12. Stage.VectorTypes,
  13. Stage.VectorGeometry,
  14. GXS.ParticleFX,
  15. GXS.Graphics;
  16. const
  17. cPERLIN_TABLE_SIZE = 256; // must be a power of two
  18. type
  19. (* A sprite-based particles FX manager using perlin-based sprites.
  20. This PFX manager is more suited for smoke or fire effects, and with proper
  21. tweaking of the texture and perlin parameters, may help render a convincing
  22. effect with less particles.
  23. The sprite generate by this manager is the composition of a distance-based
  24. intensity and a perlin noise. *)
  25. TgxPerlinPFXManager = class(TgxBaseSpritePFXManager)
  26. private
  27. FTexMapSize: Integer;
  28. FNoiseSeed: Integer;
  29. FNoiseScale: Integer;
  30. FNoiseAmplitude: Integer;
  31. FSmoothness: Single;
  32. FBrightness, FGamma: Single;
  33. protected
  34. procedure PrepareImage(bmp32: TgxBitmap32; var texFormat: Integer); override;
  35. procedure SetTexMapSize(const val: Integer);
  36. procedure SetNoiseSeed(const val: Integer);
  37. procedure SetNoiseScale(const val: Integer);
  38. procedure SetNoiseAmplitude(const val: Integer);
  39. procedure SetSmoothness(const val: Single);
  40. procedure SetBrightness(const val: Single);
  41. procedure SetGamma(const val: Single);
  42. public
  43. constructor Create(aOwner: TComponent); override;
  44. destructor Destroy; override;
  45. published
  46. (* Underlying texture map size, as a power of two.
  47. Min value is 3 (size=8), max value is 9 (size=512). *)
  48. property TexMapSize: Integer read FTexMapSize write SetTexMapSize default 6;
  49. (* Smoothness of the distance-based intensity.
  50. This value is the exponent applied to the intensity in the texture,
  51. basically with a value of 1 (default) the intensity decreases linearly,
  52. with higher values, it will remain 'constant' in the center then
  53. fade-off more abruptly, and with values below 1, there will be a
  54. sharp spike in the center. *)
  55. property Smoothness: Single read FSmoothness write SetSmoothness;
  56. (* Brightness factor applied to the perlin texture intensity.
  57. Brightness acts as a scaling, non-saturating factor. Examples:
  58. Brightness = 1 : intensities in the [0; 1] range
  59. Brightness = 2 : intensities in the [0.5; 1] range
  60. Brightness = 0.5 : intensities in the [0; 0.5] range
  61. Brightness is applied to the final texture (and thus affects
  62. the distance based intensity). *)
  63. property Brightness: Single read FBrightness write SetBrightness;
  64. property Gamma: Single read FGamma write SetGamma;
  65. // Random seed to use for the perlin noise.
  66. property NoiseSeed: Integer read FNoiseSeed write SetNoiseSeed default 0;
  67. // Scale applied to the perlin noise (stretching).
  68. property NoiseScale: Integer read FNoiseScale write SetNoiseScale default 100;
  69. (* Amplitude applied to the perlin noise (intensity).
  70. This value represent the percentage of the sprite luminance affected by
  71. the perlin texture. *)
  72. property NoiseAmplitude: Integer read FNoiseAmplitude write SetNoiseAmplitude default 50;
  73. property ColorMode default scmInner;
  74. property SpritesPerTexture default sptFour;
  75. property ParticleSize;
  76. property ColorInner;
  77. property ColorOuter;
  78. property LifeColors;
  79. end;
  80. (* Generates Perlin Noise in the [-1; 1] range.
  81. 2D noise requests are taken in the Z=0 slice *)
  82. TgxPerlin3DNoise = class(TObject)
  83. protected
  84. FPermutations: packed array [0 .. cPERLIN_TABLE_SIZE - 1] of Integer;
  85. FGradients: packed array [0 .. cPERLIN_TABLE_SIZE * 3 - 1] of Single;
  86. protected
  87. function Lattice(ix, iy, iz: Integer; fx, fy, fz: Single): Single; overload;
  88. function Lattice(ix, iy: Integer; fx, fy: Single): Single; overload;
  89. public
  90. constructor Create(randomSeed: Integer);
  91. procedure Initialize(randomSeed: Integer);
  92. function Noise(const x, y: Single): Single; overload;
  93. function Noise(const x, y, z: Single): Single; overload;
  94. function Noise(const v: TAffineVector): Single; overload;
  95. function Noise(const v: TVector4f): Single; overload;
  96. end;
  97. // ------------------------------------------------------------------
  98. implementation
  99. // ------------------------------------------------------------------
  100. // ------------------
  101. // ------------------ TgxPerlinPFXManager ------------------
  102. // ------------------
  103. constructor TgxPerlinPFXManager.Create(aOwner : TComponent);
  104. begin
  105. inherited;
  106. FTexMapSize:=6;
  107. FNoiseScale:=100;
  108. FNoiseAmplitude:=50;
  109. FSmoothness:=1;
  110. FBrightness:=1;
  111. FGamma:=1;
  112. SpritesPerTexture:=sptFour;
  113. ColorMode:=scmInner;
  114. end;
  115. destructor TgxPerlinPFXManager.Destroy;
  116. begin
  117. inherited Destroy;
  118. end;
  119. procedure TgxPerlinPFXManager.SetTexMapSize(const val : Integer);
  120. begin
  121. if val<>FTexMapSize then
  122. begin
  123. FTexMapSize:=val;
  124. if FTexMapSize<3 then
  125. FTexMapSize:=3;
  126. if FTexMapSize>9 then
  127. FTexMapSize:=9;
  128. NotifyChange(Self);
  129. end;
  130. end;
  131. procedure TgxPerlinPFXManager.SetNoiseSeed(const val : Integer);
  132. begin
  133. if val<>FNoiseSeed then
  134. begin
  135. FNoiseSeed:=val;
  136. NotifyChange(Self);
  137. end;
  138. end;
  139. procedure TgxPerlinPFXManager.SetNoiseScale(const val : Integer);
  140. begin
  141. if val<>FNoiseScale then
  142. begin
  143. FNoiseScale:=val;
  144. NotifyChange(Self);
  145. end;
  146. end;
  147. procedure TgxPerlinPFXManager.SetNoiseAmplitude(const val : Integer);
  148. begin
  149. if val<>FNoiseAmplitude then
  150. begin
  151. FNoiseAmplitude:=val;
  152. if FNoiseAmplitude<0 then
  153. FNoiseAmplitude:=0;
  154. if FNoiseAmplitude>100 then
  155. FNoiseAmplitude:=100;
  156. NotifyChange(Self);
  157. end;
  158. end;
  159. procedure TgxPerlinPFXManager.SetSmoothness(const val : Single);
  160. begin
  161. if FSmoothness<>val then
  162. begin
  163. FSmoothness:=ClampValue(val, 1e-3, 1e3);
  164. NotifyChange(Self);
  165. end;
  166. end;
  167. procedure TgxPerlinPFXManager.SetBrightness(const val : Single);
  168. begin
  169. if FBrightness<>val then
  170. begin
  171. FBrightness:=ClampValue(val, 1e-3, 1e3);
  172. NotifyChange(Self);
  173. end;
  174. end;
  175. procedure TgxPerlinPFXManager.SetGamma(const val : Single);
  176. begin
  177. if FGamma<>val then
  178. begin
  179. FGamma:=ClampValue(val, 0.1, 10);
  180. NotifyChange(Self);
  181. end;
  182. end;
  183. procedure TgxPerlinPFXManager.PrepareImage(bmp32 : TgxBitmap32; var texFormat : Integer);
  184. procedure PrepareSubImage(dx, dy, s : Integer; noise : TgxPerlin3DNoise);
  185. var
  186. s2 : Integer;
  187. x, y, d : Integer;
  188. is2, f, fy, pf, nBase, nAmp, df, dfg : Single;
  189. invGamma : Single;
  190. scanLine : PgxPixel32Array;
  191. gotIntensityCorrection : Boolean;
  192. begin
  193. s2:=s shr 1;
  194. is2:=1/s2;
  195. pf:=FNoiseScale*0.05*is2;
  196. nAmp:=FNoiseAmplitude*(0.01);
  197. nBase:=1-nAmp*0.5;
  198. if Gamma<0.1 then
  199. invGamma:=10
  200. else
  201. invGamma:=1/Gamma;
  202. gotIntensityCorrection:=(Gamma<>1) or (Brightness<>1);
  203. for y:=0 to s-1 do
  204. begin
  205. fy:=Sqr((y+0.5-s2)*is2);
  206. scanLine := bmp32.ScanLine[y+dy];
  207. for x:=0 to s-1 do
  208. begin
  209. f:=Sqr((x+0.5-s2)*is2)+fy;
  210. if f<1 then
  211. begin
  212. df := nBase+nAmp * noise.Noise(x*pf, y*pf);
  213. if gotIntensityCorrection then
  214. df := ClampValue(Power(df, InvGamma)*Brightness, 0, 1);
  215. dfg := Power((1-Sqrt(f)), FSmoothness);
  216. d := Trunc(df*255);
  217. if d > 255 then
  218. d:=255;
  219. with scanLine^[x+dx] do
  220. begin
  221. r:=d;
  222. g:=d;
  223. b:=d;
  224. a:=Trunc(dfg*255);
  225. end;
  226. end
  227. else
  228. PInteger(@scanLine[x+dx])^:=0;
  229. end;
  230. end;
  231. end;
  232. var
  233. s, s2 : Integer;
  234. noise : TgxPerlin3DNoise;
  235. begin
  236. s:=(1 shl TexMapSize);
  237. bmp32.Width:=s;
  238. bmp32.Height:=s;
  239. bmp32.Blank := false;
  240. texFormat:=GL_LUMINANCE_ALPHA;
  241. noise:=TgxPerlin3DNoise.Create(NoiseSeed);
  242. try
  243. case SpritesPerTexture of
  244. sptOne: PrepareSubImage(0, 0, s, noise);
  245. sptFour:
  246. begin
  247. s2:=s div 2;
  248. PrepareSubImage(0, 0, s2, noise);
  249. noise.Initialize(NoiseSeed+1);
  250. PrepareSubImage(s2, 0, s2, noise);
  251. noise.Initialize(NoiseSeed+2);
  252. PrepareSubImage(0, s2, s2, noise);
  253. noise.Initialize(NoiseSeed+3);
  254. PrepareSubImage(s2, s2, s2, noise);
  255. end;
  256. else
  257. Assert(False);
  258. end;
  259. finally
  260. noise.Free;
  261. end;
  262. end;
  263. // ------------------
  264. // ------------------ TgxPerlin3DNoise ------------------
  265. // ------------------
  266. constructor TgxPerlin3DNoise.Create(randomSeed: Integer);
  267. begin
  268. inherited Create;
  269. Initialize(randomSeed);
  270. end;
  271. procedure TgxPerlin3DNoise.Initialize(randomSeed: Integer);
  272. var
  273. seedBackup: Integer;
  274. i, t, j: Integer;
  275. z, r: Single;
  276. begin
  277. seedBackup := RandSeed;
  278. RandSeed := randomSeed;
  279. // Generate random gradient vectors.
  280. for i := 0 to cPERLIN_TABLE_SIZE - 1 do
  281. begin
  282. z := 1 - 2 * Random;
  283. r := Sqrt(1 - z * z);
  284. SinCosine(c2PI * Random, r, FGradients[i * 3], FGradients[i * 3 + 1]);
  285. FGradients[i * 3 + 2] := z;
  286. end;
  287. // Initialize permutations table
  288. for i := 0 to cPERLIN_TABLE_SIZE - 1 do
  289. FPermutations[i] := i;
  290. // Shake up
  291. for i := 0 to cPERLIN_TABLE_SIZE - 1 do
  292. begin
  293. j := Random(cPERLIN_TABLE_SIZE);
  294. t := FPermutations[i];
  295. FPermutations[i] := FPermutations[j];
  296. FPermutations[j] := t;
  297. end;
  298. RandSeed := seedBackup;
  299. end;
  300. function TgxPerlin3DNoise.Lattice(ix, iy, iz: Integer;
  301. fx, fy, fz: Single): Single;
  302. const
  303. cMask = cPERLIN_TABLE_SIZE - 1;
  304. var
  305. g: Integer;
  306. begin
  307. g := FPermutations[(ix + FPermutations[(iy + FPermutations[iz and cMask]) and
  308. cMask]) and cMask] * 3;
  309. Result := FGradients[g] * fx + FGradients[g + 1] * fy + FGradients
  310. [g + 2] * fz;
  311. end;
  312. function TgxPerlin3DNoise.Lattice(ix, iy: Integer; fx, fy: Single): Single;
  313. const
  314. cMask = cPERLIN_TABLE_SIZE - 1;
  315. var
  316. g: Integer;
  317. begin
  318. g := FPermutations[(ix + FPermutations[(iy + FPermutations[0]) and cMask])
  319. and cMask] * 3;
  320. Result := FGradients[g] * fx + FGradients[g + 1] * fy;
  321. end;
  322. function TgxPerlin3DNoise.Noise(const v: TAffineVector): Single;
  323. function Smooth(var x: Single): Single;
  324. begin
  325. Result := x * x * (3 - 2 * x);
  326. end;
  327. var
  328. ix, iy, iz: Integer;
  329. fx0, fx1, fy0, fy1, fz0, fz1: Single;
  330. wx, wy, wz: Single;
  331. vy0, vy1, vz0, vz1: Single;
  332. begin
  333. ix := Floor(v.x);
  334. fx0 := v.x - ix;
  335. fx1 := fx0 - 1;
  336. wx := Smooth(fx0);
  337. iy := Floor(v.y);
  338. fy0 := v.y - iy;
  339. fy1 := fy0 - 1;
  340. wy := Smooth(fy0);
  341. iz := Floor(v.z);
  342. fz0 := v.z - iz;
  343. fz1 := fz0 - 1;
  344. wz := Smooth(fz0);
  345. vy0 := Lerp(Lattice(ix, iy, iz, fx0, fy0, fz0), Lattice(ix + 1, iy, iz, fx1,
  346. fy0, fz0), wx);
  347. vy1 := Lerp(Lattice(ix, iy + 1, iz, fx0, fy1, fz0),
  348. Lattice(ix + 1, iy + 1, iz, fx1, fy1, fz0), wx);
  349. vz0 := Lerp(vy0, vy1, wy);
  350. vy0 := Lerp(Lattice(ix, iy, iz + 1, fx0, fy0, fz1),
  351. Lattice(ix + 1, iy, iz + 1, fx1, fy0, fz1), wx);
  352. vy1 := Lerp(Lattice(ix, iy + 1, iz + 1, fx0, fy1, fz1),
  353. Lattice(ix + 1, iy + 1, iz + 1, fx1, fy1, fz1), wx);
  354. vz1 := Lerp(vy0, vy1, wy);
  355. Result := Lerp(vz0, vz1, wz);
  356. end;
  357. function TgxPerlin3DNoise.Noise(const x, y: Single): Single;
  358. function Smooth(var x: Single): Single;
  359. begin
  360. Result := x * x * (3 - 2 * x);
  361. end;
  362. var
  363. ix, iy: Integer;
  364. fx0, fx1, fy0, fy1: Single;
  365. wx, wy: Single;
  366. vy0, vy1: Single;
  367. begin
  368. ix := Floor(x);
  369. fx0 := x - ix;
  370. fx1 := fx0 - 1;
  371. wx := Smooth(fx0);
  372. iy := Floor(y);
  373. fy0 := y - iy;
  374. fy1 := fy0 - 1;
  375. wy := Smooth(fy0);
  376. vy0 := Lerp(Lattice(ix, iy, fx0, fy0), Lattice(ix + 1, iy, fx1, fy0), wx);
  377. vy1 := Lerp(Lattice(ix, iy + 1, fx0, fy1), Lattice(ix + 1, iy + 1, fx1,
  378. fy1), wx);
  379. Result := Lerp(vy0, vy1, wy);
  380. end;
  381. function TgxPerlin3DNoise.Noise(const x, y, z: Single): Single;
  382. begin
  383. Result := Noise(AffineVectorMake(x, y, z));
  384. end;
  385. function TgxPerlin3DNoise.Noise(const v: TVector4f): Single;
  386. begin
  387. Result := Noise(PAffineVector(@v)^);
  388. end;
  389. // ------------------------------------------------------------------
  390. initialization
  391. // ------------------------------------------------------------------
  392. RegisterClasses([TgxPerlinPFXManager]);
  393. end.