GpuParticle.fx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. //////////////////////////////////////////////////////////////////////////////
  2. // 2005 Electronic Arts Inc
  3. //
  4. // GPU vertex particle FX Shader
  5. //////////////////////////////////////////////////////////////////////////////
  6. //#define FORCE_PARTICLES_INTO_XY_PLANE // Define this for xy (ground) aligned instead of screen facing particles
  7. #define SUPPORT_FOG 1
  8. #include "Common.fxh"
  9. #include "Gamma.fxh"
  10. #include "CommonParticle.fxh"
  11. //
  12. // draw params
  13. //
  14. SAMPLER_2D_BEGIN( ParticleTextureSampler,
  15. string UIWidget = "None";
  16. string SasBindAddress = "Particle.Draw.Texture";
  17. )
  18. MinFilter = Linear;
  19. MagFilter = Linear;
  20. MipFilter = Linear;
  21. AddressU = Clamp;
  22. AddressV = Clamp;
  23. SAMPLER_2D_END
  24. // $Note(WSK) - We shouldn't need to use a different sampler, and it actually doesn't work on ps3
  25. // We should be able to do the same for pc and xenon but since they are close to final,
  26. // we want to be safe and make this change for ps3 only
  27. #if defined(EA_PLATFORM_PS3)
  28. static const sampler2D NextFrameTextureSamplerSampler = ParticleTextureSamplerSampler;
  29. #else // #if defined(EA_PLATFORM_PS3)
  30. SAMPLER_2D_BEGIN( NextFrameTextureSampler,
  31. string UIWidget = "None";
  32. string SasBindAddress = "Particle.Draw.Texture";
  33. )
  34. MinFilter = Linear;
  35. MagFilter = Linear;
  36. MipFilter = Linear;
  37. AddressU = Clamp;
  38. AddressV = Clamp;
  39. SAMPLER_2D_END
  40. #endif // #if defined(EA_PLATFORM_PS3)
  41. // Used to see where the particle volume intersects an object.
  42. SAMPLER_2D_BEGIN( DepthTexture,
  43. string SasBindAddress = "WW3D.DepthTexture";
  44. )
  45. MinFilter = Linear;
  46. MagFilter = Linear;
  47. MipFilter = Point;
  48. AddressU = Clamp;
  49. AddressV = Clamp;
  50. SAMPLER_2D_END
  51. //--------------------------------- GENERAL STUFF --------------------------------------
  52. bool ShouldDrawParticleSoft
  53. <
  54. string UIWidget = "None";
  55. string SasBindAddress = "Particle.Draw.ShouldDrawParticleSoft";
  56. > = false;
  57. // Variationgs for handling fog in the pixel shader
  58. static const int FogMode_Disabled = 0;
  59. static const int FogMode_Opaque = 1;
  60. static const int FogMode_Additive = 2;
  61. // Transformations
  62. float4x4 WorldView : WorldView;
  63. float4x4 Projection: Projection;
  64. float4x4 WorldViewProjection : WorldViewProjection;
  65. float4x3 View : View;
  66. float4x4 ProjectionI : ProjectionInverse;
  67. #if defined(_WW3D_)
  68. #if !defined(USE_INDIRECT_CONSTANT)
  69. float3 EyePosition
  70. <
  71. string UIWidget = "None";
  72. string SasBindAddress = "Sas.Camera.Position";
  73. >;
  74. #endif // #if !defined(USE_INDIRECT_CONSTANT)
  75. float3 GetEyePosition()
  76. {
  77. return EyePosition;
  78. }
  79. #else // #if defined(_WW3D_)
  80. float4x3 ViewI : ViewInverse;
  81. float3 GetEyePosition()
  82. {
  83. return ViewI[3];
  84. }
  85. #endif // #if defined(_WW3D_)
  86. // Time (ie. material is animated)
  87. float Time : Time;
  88. float3 TransformParticleCornerInto3D(float2 relativeCornerPos)
  89. {
  90. #if defined(FORCE_PARTICLES_INTO_XY_PLANE)
  91. // Just treat 2D position as being 3D
  92. return float3(relativeCornerPos, 0);
  93. #else
  94. // Bring 2D "particle space" into view space
  95. float3x2 transform = (float3x2)View;
  96. return mul(transform, relativeCornerPos);
  97. #endif
  98. }
  99. // ----------------------------------------------------------------------------
  100. // SHADER: HIGH ~ ULTRAHIGH
  101. // ----------------------------------------------------------------------------
  102. struct ParticleVSOutput_H
  103. {
  104. float4 Position : POSITION;
  105. float2 ParticleTexCoord : TEXCOORD0;
  106. float4 Color : COLOR0;
  107. float3 Fog : TEXCOORD1; // This is just a scalar, but PS1.1 can't replicate-swizzle, so replicate scalar into a vector in vertex shader
  108. float Depth : TEXCOORD2; // For _CreateShadowMap technique
  109. float3 NextFrameTexCoord : TEXCOORD3; // for interpolating between two frames
  110. float4 NDCPosition : TEXCOORD4;
  111. float2 ZPositions : TEXCOORD5; // x is the particle z position, y is the ViewEyeDirection z component.
  112. };
  113. // Making this value smaller will increase the size of the transparent edge where
  114. // the particle intersects the background.
  115. #define PARTICLE_VOLUME_SCALE 5
  116. // For indexing the shader array based on what type of blend is needed for the particle draw type.
  117. #define SOFTBLEND_ADDITIVE 0
  118. #define SOFTBLEND_ALPHA 1
  119. #define SOFTBLEND_DISABLED 2
  120. // ----------------------------------------------------------------------------
  121. ParticleVSOutput_H ParticleVertexShader_H(float4 StartPositionLifeInFrames : POSITION,
  122. float4 StartVelocityCreationFrame : TEXCOORD0, float2 SeedAndIndex : TEXCOORD1,
  123. uniform int fogMode)
  124. {
  125. ParticleVSOutput_H Out;
  126. // decode vertex data
  127. float3 StartPosition = StartPositionLifeInFrames.xyz;
  128. float LifeInFrames = StartPositionLifeInFrames.w;
  129. float3 StartVelocity = StartVelocityCreationFrame.xyz;
  130. float CreationFrame = StartVelocityCreationFrame.w;
  131. float Seed = SeedAndIndex.x;
  132. float Index = SeedAndIndex.y;
  133. // particle system works with frames, so first convert time to frame
  134. // rather than converting everything else to time
  135. float age = (Time * CLIENT_FRAMES_PER_SECOND - CreationFrame);
  136. // first eliminate dead particles
  137. if (age > LifeInFrames)
  138. Index = 0;
  139. float relativeAge = age / LifeInFrames;
  140. float3 particlePosition;
  141. float size;
  142. float2x2 zRotationMatrix;
  143. Particle_ComputePhysics(particlePosition, size, zRotationMatrix,
  144. age, StartPosition, StartVelocity, Seed);
  145. // Calculate vertex position
  146. float2 vertexCorner = VertexCorners[Index];
  147. float2 relativeCornerPos = mul(vertexCorner, zRotationMatrix);
  148. float3 cornerPosition = particlePosition + size * TransformParticleCornerInto3D(relativeCornerPos);
  149. // Convert the particle position into view space so that we can get the distance
  150. // between the particle and the background.
  151. float particleSize = size / PARTICLE_VOLUME_SCALE;
  152. float4 particlePosView = mul(float4(cornerPosition, 1), WorldView);
  153. Out.ZPositions.x = particlePosView.z / particleSize;
  154. // Finish projecting the position.
  155. float4 ndcPos = mul(particlePosView, Projection);
  156. Out.Position = ndcPos;
  157. Out.NDCPosition = ndcPos;
  158. Out.Depth = Out.Position.z / Out.Position.w;
  159. // Convert the position into a view vector to the far plane.
  160. float4 screenFarPlanePosition = float4(ndcPos.xy, 1, 1);
  161. float4 viewFarPlanePosition4 = mul(screenFarPlanePosition, ProjectionI);
  162. float3 viewFarPlanePosition = viewFarPlanePosition4.xyz / viewFarPlanePosition4.w;
  163. // We don't really want a vector to the far plane. We want the vector with a z length of 1,
  164. // so that we know how much in x-y we need to step per z-depth that we get from the depth texture.
  165. Out.ZPositions.y = (viewFarPlanePosition.z / viewFarPlanePosition.z) / particleSize;
  166. if (fogMode != FogMode_Disabled)
  167. {
  168. // Fog depends on world position, but world matrix should be identity.
  169. Out.Fog = CalculateFog(Fog, cornerPosition, GetEyePosition()).xxx;
  170. }
  171. else
  172. {
  173. Out.Fog = 0;
  174. }
  175. Particle_ComputeVideoTexture(age, Seed, vertexCorner, GetVertexTexCoord(vertexCorner), Out.ParticleTexCoord, Out.NextFrameTexCoord);
  176. // compute color
  177. Out.Color = Particle_ComputeColor(relativeAge, Seed, true);
  178. return Out;
  179. }
  180. // ----------------------------------------------------------------------------
  181. float4 SoftParticlePixelShader(ParticleVSOutput_H In, uniform int fogMode, uniform int blendMode) COLORTARGET
  182. {
  183. float3 ndcPosition = In.NDCPosition.xyz / In.NDCPosition.w;
  184. // Convert the position into texture space then grab the depth value from the
  185. // depth texture. Then, move along that vector by the depth to find the pixel position.
  186. float2 depthTexCoord = ndcPosition.xy * float2(0.5, -0.5) + 0.5;
  187. float backgroundDepth = tex2D(SAMPLER(DepthTexture), depthTexCoord).x;
  188. float backgroundPosView = backgroundDepth * In.ZPositions.y;
  189. // The closer the particle is to the background, the more transparent it is.
  190. float linearBlend = saturate(In.ZPositions.x - backgroundPosView);
  191. // do interpolation between two frames (for particles with video textures)
  192. float4 TextureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  193. float4 NextFrameColor = tex2D( SAMPLER(NextFrameTextureSampler), In.NextFrameTexCoord.xy);
  194. float4 Color = lerp(TextureColor, NextFrameColor, In.NextFrameTexCoord.z) * In.Color;
  195. Color.xyz = GammaToLinear(Color.xyz);
  196. // Don't do soft particles for ground aligned particles, as they will often be almost fully transparent due to terrain proximity
  197. #if !defined(FORCE_PARTICLES_INTO_XY_PLANE)
  198. // Modify the color (or alpha) of a the particle depending on how it's being drawn
  199. // so the particle has a soft edge to it.
  200. if (blendMode == SOFTBLEND_ALPHA)
  201. {
  202. Color.w *= linearBlend;
  203. }
  204. else if (blendMode == SOFTBLEND_ADDITIVE)
  205. {
  206. Color.xyz *= linearBlend;
  207. }
  208. #endif
  209. // Apply fog
  210. float3 fogStrength = saturate(In.Fog) ;
  211. if (fogMode == FogMode_Opaque)
  212. {
  213. // apply fog
  214. Color.xyz = lerp(Fog.Color, Color.xyz, fogStrength);
  215. }
  216. else if (fogMode == FogMode_Additive)
  217. {
  218. // Fog used with additive blending just needs to reduce the additive influence, not blend towards the fog color
  219. Color.xyz *= fogStrength;
  220. }
  221. return Color;
  222. }
  223. // ----------------------------------------------------------------------------
  224. // SHADER: XENON
  225. // ----------------------------------------------------------------------------
  226. float4 ParticlePixelShader_Xenon(ParticleVSOutput_H In ) : COLOR
  227. {
  228. return SoftParticlePixelShader( In,
  229. Fog.IsEnabled ? ((Draw.ShaderType == ShaderType_Additive || Draw.ShaderType == ShaderType_AdditiveAlphaTest || Draw.ShaderType == ShaderType_Multiply) ? FogMode_Additive : FogMode_Opaque) : FogMode_Disabled,
  230. !ShouldDrawParticleSoft ? SOFTBLEND_DISABLED : ((Draw.ShaderType == ShaderType_Alpha) ? SOFTBLEND_ALPHA : SOFTBLEND_ADDITIVE));
  231. }
  232. // ----------------------------------------------------------------------------
  233. // TECHNIQUE: HIGH ~ ULTRAHIGH (And XENON)
  234. // ----------------------------------------------------------------------------
  235. DEFINE_ARRAY_MULTIPLIER( PS_Multiplier_FogMode = 1 );
  236. #define PS_FogMode_H(drawType) \
  237. compile PS_3_0 SoftParticlePixelShader(FogMode_Disabled, drawType), \
  238. compile PS_3_0 SoftParticlePixelShader(FogMode_Opaque, drawType), \
  239. compile PS_3_0 SoftParticlePixelShader(FogMode_Additive, drawType)
  240. DEFINE_ARRAY_MULTIPLIER( PS_Multiplier_ShaderType = PS_Multiplier_FogMode * 3 );
  241. #define PS_ShaderType_H \
  242. PS_FogMode_H(SOFTBLEND_ADDITIVE), \
  243. PS_FogMode_H(SOFTBLEND_ALPHA), \
  244. PS_FogMode_H(SOFTBLEND_DISABLED)
  245. DEFINE_ARRAY_MULTIPLIER( PS_Multiplier_Final_H = PS_Multiplier_ShaderType * 3 );
  246. #if SUPPORTS_SHADER_ARRAYS
  247. pixelshader PS_Array_H[PS_Multiplier_Final_H] =
  248. {
  249. PS_ShaderType_H
  250. };
  251. #endif
  252. technique Default
  253. {
  254. pass P0
  255. <
  256. USE_EXPRESSION_EVALUATOR("Particle")
  257. >
  258. {
  259. VertexShader = compile VS_3_0 ParticleVertexShader_H(FogMode_Opaque);
  260. PixelShader = ARRAY_EXPRESSION_PS( PS_Array_H,
  261. (!ShouldDrawParticleSoft ? SOFTBLEND_DISABLED : ((Draw.ShaderType == ShaderType_Alpha) ? SOFTBLEND_ALPHA : SOFTBLEND_ADDITIVE)) * PS_Multiplier_ShaderType
  262. + (Fog.IsEnabled ? ((Draw.ShaderType == ShaderType_Additive || Draw.ShaderType == ShaderType_AdditiveAlphaTest || Draw.ShaderType == ShaderType_Multiply) ? FogMode_Additive : FogMode_Opaque) : FogMode_Disabled),
  263. compile PS_VERSION ParticlePixelShader_Xenon()
  264. );
  265. ZEnable = true;
  266. ZFunc = ZFUNC_INFRONT;
  267. ZWriteEnable = false;
  268. CullMode = None;
  269. SETUP_ALPHA_BLEND_AND_TEST(Draw.ShaderType);
  270. }
  271. }
  272. // ----------------------------------------------------------------------------
  273. // SHADER: MEDIUM
  274. // ----------------------------------------------------------------------------
  275. struct ParticleVSOutput_M
  276. {
  277. float4 Position : POSITION;
  278. float2 ParticleTexCoord : TEXCOORD0;
  279. float4 Color : COLOR0;
  280. float3 Fog : TEXCOORD1; // This is just a scalar, but PS1.1 can't replicate-swizzle, so replicate scalar into a vector in vertex shader
  281. float Depth : TEXCOORD2; // For _CreateShadowMap technique
  282. float3 NextFrameTexCoord : TEXCOORD3; // for interpolating between two frames
  283. };
  284. // ----------------------------------------------------------------------------
  285. ParticleVSOutput_M ParticleVertexShader_M(float4 StartPositionLifeInFrames : POSITION,
  286. float4 StartVelocityCreationFrame : TEXCOORD0, float2 SeedAndIndex : TEXCOORD1,
  287. uniform int fogMode)
  288. {
  289. ParticleVSOutput_M Out;
  290. // decode vertex data
  291. float3 StartPosition = StartPositionLifeInFrames.xyz;
  292. float LifeInFrames = StartPositionLifeInFrames.w;
  293. float3 StartVelocity = StartVelocityCreationFrame.xyz;
  294. float CreationFrame = StartVelocityCreationFrame.w;
  295. float Seed = SeedAndIndex.x;
  296. float Index = SeedAndIndex.y;
  297. // particle system works with frames, so first convert time to frame
  298. // rather than converting everything else to time
  299. float age = (Time * CLIENT_FRAMES_PER_SECOND - CreationFrame);
  300. // first eliminate dead particles
  301. if (age > LifeInFrames)
  302. Index = 0;
  303. float relativeAge = age / LifeInFrames;
  304. float3 particlePosition;
  305. float size;
  306. float2x2 zRotationMatrix;
  307. Particle_ComputePhysics(particlePosition, size, zRotationMatrix,
  308. age, StartPosition, StartVelocity, Seed);
  309. // Calculate vertex position
  310. float2 vertexCorner = VertexCorners[Index];
  311. float2 relativeCornerPos = mul(vertexCorner, zRotationMatrix);
  312. float3 cornerPosition = particlePosition + size * TransformParticleCornerInto3D(relativeCornerPos);
  313. Out.Position = mul(float4(cornerPosition, 1), WorldViewProjection);
  314. Out.Depth = Out.Position.z / Out.Position.w;
  315. if (fogMode != FogMode_Disabled)
  316. {
  317. // Fog depends on world position, but world matrix should be identity.
  318. Out.Fog = CalculateFog(Fog, cornerPosition, GetEyePosition()).xxx;
  319. }
  320. else
  321. {
  322. Out.Fog = 0;
  323. }
  324. Particle_ComputeVideoTexture(age, Seed, vertexCorner, GetVertexTexCoord(vertexCorner), Out.ParticleTexCoord, Out.NextFrameTexCoord);
  325. // compute color
  326. Out.Color = Particle_ComputeColor(relativeAge, Seed, true);
  327. return Out;
  328. }
  329. // ----------------------------------------------------------------------------
  330. float4 ParticlePixelShader(ParticleVSOutput_M In, uniform int fogMode) : COLOR
  331. {
  332. float4 TextureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  333. float4 NextFrameColor = tex2D( SAMPLER(NextFrameTextureSampler), In.NextFrameTexCoord.xy);
  334. // do interpolation between two frames (for particles with video textures)
  335. float4 Color = lerp(TextureColor, NextFrameColor, In.NextFrameTexCoord.z) * In.Color;
  336. // Apply fog
  337. float3 fogStrength = saturate(In.Fog) ;
  338. if (fogMode == FogMode_Opaque)
  339. {
  340. // apply fog
  341. Color.xyz = lerp(Fog.Color, Color.xyz, fogStrength);
  342. }
  343. else if (fogMode == FogMode_Additive)
  344. {
  345. // Fog used with additive blending just needs to reduce the additive influence, not blend towards the fog color
  346. Color.xyz *= fogStrength;
  347. }
  348. return Color;
  349. }
  350. // Having this ensures that xenon doesn't load techniques that it doesn't use.
  351. // This has to be below the medium functions because that is used for the shadowmap.
  352. #if ENABLE_LOD
  353. // ----------------------------------------------------------------------------
  354. // TECHNIQUE: MEDIUM
  355. // ----------------------------------------------------------------------------
  356. #define PS_ShaderType_M \
  357. compile PS_2_0 ParticlePixelShader(FogMode_Disabled), \
  358. compile PS_2_0 ParticlePixelShader(FogMode_Opaque), \
  359. compile PS_2_0 ParticlePixelShader(FogMode_Additive)
  360. DEFINE_ARRAY_MULTIPLIER( PS_Multiplier_Final_M = 3 );
  361. #if SUPPORTS_SHADER_ARRAYS
  362. pixelshader PS_Array_M[PS_Multiplier_Final_M] =
  363. {
  364. PS_ShaderType_M
  365. };
  366. #endif
  367. technique Default_M
  368. {
  369. pass P0
  370. <
  371. USE_EXPRESSION_EVALUATOR("Particle")
  372. >
  373. {
  374. VertexShader = compile VS_2_0 ParticleVertexShader_M(FogMode_Opaque);
  375. PixelShader = ARRAY_EXPRESSION_PS( PS_Array_M,
  376. Fog.IsEnabled ? ((Draw.ShaderType == ShaderType_Additive || Draw.ShaderType == ShaderType_AdditiveAlphaTest || Draw.ShaderType == ShaderType_Multiply) ? FogMode_Additive : FogMode_Opaque) : FogMode_Disabled,
  377. compile PS_VERSION ParticlePixelShader_Xenon()
  378. );
  379. ZEnable = true;
  380. ZFunc = ZFUNC_INFRONT;
  381. ZWriteEnable = false;
  382. CullMode = None;
  383. SETUP_ALPHA_BLEND_AND_TEST(Draw.ShaderType);
  384. }
  385. }
  386. // ----------------------------------------------------------------------------
  387. // SHADER: LowQuality
  388. // ----------------------------------------------------------------------------
  389. struct ParticleVSLowOutput
  390. {
  391. float4 Position : POSITION;
  392. float2 ParticleTexCoord : TEXCOORD0;
  393. float4 Color : COLOR0;
  394. };
  395. // ----------------------------------------------------------------------------
  396. ParticleVSLowOutput ParticleVertexShaderLow(float4 StartPositionLifeInFrames : POSITION,
  397. float4 StartVelocityCreationFrame : TEXCOORD0, float2 SeedAndIndex : TEXCOORD1)
  398. {
  399. ParticleVSLowOutput Out;
  400. // decode vertex data
  401. float3 StartPosition = StartPositionLifeInFrames.xyz;
  402. float LifeInFrames = StartPositionLifeInFrames.w;
  403. float3 StartVelocity = StartVelocityCreationFrame.xyz;
  404. float CreationFrame = StartVelocityCreationFrame.w;
  405. float Seed = SeedAndIndex.x;
  406. float Index = SeedAndIndex.y;
  407. // particle system works with frames, so first convert time to frame
  408. // rather than converting everything else to time
  409. float age = (Time * CLIENT_FRAMES_PER_SECOND - CreationFrame);
  410. // first eliminate dead particles
  411. if (age > LifeInFrames)
  412. Index = 0;
  413. float relativeAge = age / LifeInFrames;
  414. float3 particlePosition;
  415. float size;
  416. float2x2 zRotationMatrix;
  417. Particle_ComputePhysics_Simplified(particlePosition, size, zRotationMatrix,
  418. age, StartPosition, StartVelocity, Seed);
  419. // Calculate vertex position
  420. float2 vertexCorner = VertexCorners[Index];
  421. float2 relativeCornerPos = mul(vertexCorner, zRotationMatrix);
  422. float3 cornerPosition = particlePosition + size * TransformParticleCornerInto3D(relativeCornerPos);
  423. Out.Position = mul(float4(cornerPosition, 1), WorldViewProjection);
  424. // texture coordinate
  425. Particle_ComputeVideoTextureNoNextFrame(age, Seed, GetVertexTexCoord(vertexCorner), Out.ParticleTexCoord);
  426. // compute color
  427. Out.Color = Particle_ComputeColor(relativeAge, Seed, false);
  428. return Out;
  429. }
  430. // ----------------------------------------------------------------------------
  431. float4 ParticlePixelShaderLow(ParticleVSLowOutput In) : COLOR
  432. {
  433. float4 TextureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  434. float4 Color = TextureColor * In.Color;
  435. return Color;
  436. }
  437. // ----------------------------------------------------------------------------
  438. // TECHNIQUE: LowQuality
  439. // ----------------------------------------------------------------------------
  440. technique Default_L
  441. {
  442. pass P0
  443. <
  444. USE_EXPRESSION_EVALUATOR("Particle")
  445. >
  446. {
  447. VertexShader = compile VS_2_0 ParticleVertexShaderLow();
  448. PixelShader = compile PS_2_0 ParticlePixelShaderLow();
  449. ZEnable = true;
  450. ZFunc = ZFUNC_INFRONT;
  451. ZWriteEnable = false;
  452. CullMode = None;
  453. SETUP_ALPHA_BLEND_AND_TEST(Draw.ShaderType);
  454. }
  455. }
  456. #endif // #if ENABLE_LOD
  457. // ----------------------------------------------------------------------------
  458. // SHADER: CreateShadowMap
  459. // ----------------------------------------------------------------------------
  460. float4 CreateShadowMapPS(ParticleVSOutput_M In, uniform int shaderType) COLORTARGET
  461. {
  462. float4 textureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  463. float4 color = textureColor * In.Color;
  464. // Threshold where "alpha testing" or color equivalents set pixel to be opaque.
  465. // Needs to be much lower than common alpha test threshold as a single particle is usually quite transparent.
  466. const float opacityThreshold = 0.1;
  467. if (shaderType == ShaderType_Additive)
  468. {
  469. // The brighter the color the denser the particle. Clip dark areas.
  470. clip(dot(color, float3(1, 1, 1)) - 3 * opacityThreshold);
  471. }
  472. else if (shaderType == ShaderType_Multiply)
  473. {
  474. // The darker the color the denser the particle. Clip bright areas.
  475. clip(3 * opacityThreshold - dot(color, float3(1, 1, 1)));
  476. }
  477. else if (shaderType == ShaderType_AdditiveAlphaTest || shaderType == ShaderType_Alpha
  478. || shaderType == ShaderType_AlphaTest)
  479. {
  480. // Simulate alpha testing
  481. clip(color.a - opacityThreshold);
  482. }
  483. return In.Depth;
  484. }
  485. // ----------------------------------------------------------------------------
  486. float4 CreateShadowMapPS_Xenon( ParticleVSOutput_M In ) : COLOR
  487. {
  488. return CreateShadowMapPS( In, min(Draw.ShaderType, ShaderType_Multiply) );
  489. }
  490. // ----------------------------------------------------------------------------
  491. // TECHNIQUE: CreateShadowMap
  492. // ----------------------------------------------------------------------------
  493. #define PSCreateShadowMap_ShaderType \
  494. compile PS_2_0 CreateShadowMapPS(0), \
  495. compile PS_2_0 CreateShadowMapPS(ShaderType_Additive), \
  496. compile PS_2_0 CreateShadowMapPS(ShaderType_AdditiveAlphaTest), \
  497. compile PS_2_0 CreateShadowMapPS(ShaderType_Alpha), \
  498. compile PS_2_0 CreateShadowMapPS(ShaderType_AlphaTest), \
  499. compile PS_2_0 CreateShadowMapPS(ShaderType_Multiply)
  500. DEFINE_ARRAY_MULTIPLIER( PSCreateShadowMap_Multiplier_Final = 6 );
  501. #if SUPPORTS_SHADER_ARRAYS
  502. pixelshader PSCreateShadowMap_Array[PSCreateShadowMap_Multiplier_Final] =
  503. {
  504. PSCreateShadowMap_ShaderType
  505. };
  506. #endif
  507. // ----------------------------------------------------------------------------
  508. technique _CreateShadowMap
  509. {
  510. pass P0
  511. <
  512. USE_EXPRESSION_EVALUATOR("GPUParticle_CreateShadowMap")
  513. >
  514. {
  515. VertexShader = compile VS_2_0 ParticleVertexShader_M(FogMode_Disabled);
  516. PixelShader = ARRAY_EXPRESSION_PS( PSCreateShadowMap_Array,
  517. min(Draw.ShaderType, ShaderType_Multiply),
  518. compile PS_VERSION CreateShadowMapPS_Xenon()
  519. );
  520. ZEnable = true;
  521. ZFunc = ZFUNC_INFRONT;
  522. ZWriteEnable = true;
  523. CullMode = None;
  524. AlphaBlendEnable = false;
  525. AlphaTestEnable = false; // Handled in pixel shader
  526. }
  527. }