GpuParticleDistort.fx 23 KB

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