GLPerlinPFX.pas 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. {
  5. PFX particle effects revolving around the use of Perlin noise.
  6. }
  7. unit GLPerlinPFX;
  8. interface
  9. {$I GLScene.inc}
  10. uses
  11. System.Classes,
  12. System.Math,
  13. OpenGLTokens,
  14. GLParticleFX,
  15. GLGraphics,
  16. GLPerlinNoise3D,
  17. GLVectorGeometry;
  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. TGLPerlinPFXManager = class(TGLBaseSpritePFXManager)
  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: TGLBitmap32; 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
  73. write SetNoiseAmplitude default 50;
  74. property ColorMode default scmInner;
  75. property SpritesPerTexture default sptFour;
  76. property ParticleSize;
  77. property ColorInner;
  78. property ColorOuter;
  79. property LifeColors;
  80. end;
  81. // ------------------------------------------------------------------
  82. implementation
  83. // ------------------------------------------------------------------
  84. // ------------------
  85. // ------------------ TGLPerlinPFXManager ------------------
  86. // ------------------
  87. constructor TGLPerlinPFXManager.Create(aOwner: TComponent);
  88. begin
  89. inherited;
  90. FTexMapSize := 6;
  91. FNoiseScale := 100;
  92. FNoiseAmplitude := 50;
  93. FSmoothness := 1;
  94. FBrightness := 1;
  95. FGamma := 1;
  96. SpritesPerTexture := sptFour;
  97. ColorMode := scmInner;
  98. end;
  99. destructor TGLPerlinPFXManager.Destroy;
  100. begin
  101. inherited Destroy;
  102. end;
  103. procedure TGLPerlinPFXManager.SetTexMapSize(const val: Integer);
  104. begin
  105. if val <> FTexMapSize then
  106. begin
  107. FTexMapSize := val;
  108. if FTexMapSize < 3 then
  109. FTexMapSize := 3;
  110. if FTexMapSize > 9 then
  111. FTexMapSize := 9;
  112. NotifyChange(Self);
  113. end;
  114. end;
  115. procedure TGLPerlinPFXManager.SetNoiseSeed(const val: Integer);
  116. begin
  117. if val <> FNoiseSeed then
  118. begin
  119. FNoiseSeed := val;
  120. NotifyChange(Self);
  121. end;
  122. end;
  123. procedure TGLPerlinPFXManager.SetNoiseScale(const val: Integer);
  124. begin
  125. if val <> FNoiseScale then
  126. begin
  127. FNoiseScale := val;
  128. NotifyChange(Self);
  129. end;
  130. end;
  131. procedure TGLPerlinPFXManager.SetNoiseAmplitude(const val: Integer);
  132. begin
  133. if val <> FNoiseAmplitude then
  134. begin
  135. FNoiseAmplitude := val;
  136. if FNoiseAmplitude < 0 then
  137. FNoiseAmplitude := 0;
  138. if FNoiseAmplitude > 100 then
  139. FNoiseAmplitude := 100;
  140. NotifyChange(Self);
  141. end;
  142. end;
  143. procedure TGLPerlinPFXManager.SetSmoothness(const val: Single);
  144. begin
  145. if FSmoothness <> val then
  146. begin
  147. FSmoothness := ClampValue(val, 1E-3, 1E3);
  148. NotifyChange(Self);
  149. end;
  150. end;
  151. procedure TGLPerlinPFXManager.SetBrightness(const val: Single);
  152. begin
  153. if FBrightness <> val then
  154. begin
  155. FBrightness := ClampValue(val, 1E-3, 1E3);
  156. NotifyChange(Self);
  157. end;
  158. end;
  159. procedure TGLPerlinPFXManager.SetGamma(const val: Single);
  160. begin
  161. if FGamma <> val then
  162. begin
  163. FGamma := ClampValue(val, 0.1, 10);
  164. NotifyChange(Self);
  165. end;
  166. end;
  167. procedure TGLPerlinPFXManager.PrepareImage(bmp32: TGLBitmap32;
  168. var texFormat: Integer);
  169. procedure PrepareSubImage(dx, dy, s: Integer; noise: TGLPerlin3DNoise);
  170. var
  171. s2: Integer;
  172. x, y, d: Integer;
  173. is2, f, fy, pf, nBase, nAmp, df, dfg: Single;
  174. invGamma: Single;
  175. scanLine: PPixel32Array;
  176. gotIntensityCorrection: Boolean;
  177. begin
  178. s2 := s shr 1;
  179. is2 := 1 / s2;
  180. pf := FNoiseScale * 0.05 * is2;
  181. nAmp := FNoiseAmplitude * (0.01);
  182. nBase := 1 - nAmp * 0.5;
  183. if Gamma < 0.1 then
  184. invGamma := 10
  185. else
  186. invGamma := 1 / Gamma;
  187. gotIntensityCorrection := (Gamma <> 1) or (Brightness <> 1);
  188. for y := 0 to s - 1 do
  189. begin
  190. fy := Sqr((y + 0.5 - s2) * is2);
  191. scanLine := bmp32.scanLine[y + dy];
  192. for x := 0 to s - 1 do
  193. begin
  194. f := Sqr((x + 0.5 - s2) * is2) + fy;
  195. if f < 1 then
  196. begin
  197. df := nBase + nAmp * noise.noise(x * pf, y * pf);
  198. if gotIntensityCorrection then
  199. df := EnsureRange(Power(df, invGamma) * Brightness, 0, 1);
  200. dfg := EnsureRange(Power((1 - Sqrt(f)), FSmoothness), 0, 1);
  201. d := Trunc(df * 255);
  202. if d > 255 then
  203. d := 255;
  204. with scanLine^[x + dx] do
  205. begin
  206. r := d;
  207. g := d;
  208. b := d;
  209. a := Trunc(dfg * 255);
  210. end;
  211. end
  212. else
  213. PInteger(@scanLine[x + dx])^ := 0;
  214. end;
  215. end;
  216. end;
  217. var
  218. s, s2: Integer;
  219. noise: TGLPerlin3DNoise;
  220. begin
  221. s := (1 shl TexMapSize);
  222. bmp32.Width := s;
  223. bmp32.Height := s;
  224. bmp32.Blank := false;
  225. texFormat := GL_LUMINANCE_ALPHA;
  226. noise := TGLPerlin3DNoise.Create(NoiseSeed);
  227. try
  228. case SpritesPerTexture of
  229. sptOne:
  230. PrepareSubImage(0, 0, s, noise);
  231. sptFour:
  232. begin
  233. s2 := s div 2;
  234. PrepareSubImage(0, 0, s2, noise);
  235. noise.Initialize(NoiseSeed + 1);
  236. PrepareSubImage(s2, 0, s2, noise);
  237. noise.Initialize(NoiseSeed + 2);
  238. PrepareSubImage(0, s2, s2, noise);
  239. noise.Initialize(NoiseSeed + 3);
  240. PrepareSubImage(s2, s2, s2, noise);
  241. end;
  242. else
  243. Assert(false);
  244. end;
  245. finally
  246. noise.Free;
  247. end;
  248. end;
  249. // ------------------------------------------------------------------
  250. initialization
  251. // ------------------------------------------------------------------
  252. // class registrations
  253. RegisterClasses([TGLPerlinPFXManager]);
  254. end.