GPUParticleLit.fx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. //////////////////////////////////////////////////////////////////////////////
  2. // ©2005 Electronic Arts Inc
  3. //
  4. // GPU vertex particle FX Shader
  5. //////////////////////////////////////////////////////////////////////////////
  6. #define SUPPORT_FOG 1
  7. #define SUPPORT_GLOBAL_LIGHTS 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. //--------------------------------- GENERAL STUFF --------------------------------------
  42. // Variationgs for handling fog in the pixel shader
  43. static const int FogMode_Disabled = 0;
  44. static const int FogMode_Opaque = 1;
  45. static const int FogMode_Additive = 2;
  46. // Transformations
  47. float4x4 WorldViewProjection : WorldViewProjection;
  48. float4x3 View : View;
  49. float4x3 World : World;
  50. // Time (ie. material is animated)
  51. float Time : Time;
  52. // ----------------------------------------------------------------------------
  53. // SHADER: Default
  54. // ----------------------------------------------------------------------------
  55. struct ParticleVSOutput
  56. {
  57. float4 Position : POSITION;
  58. float2 ParticleTexCoord : TEXCOORD0;
  59. float4 Color : COLOR0;
  60. // float3 Fog : TEXCOORD2; // This is just a scalar, but PS1.1 can't replicate-swizzle, so replicate scalar into a vector in vertex shader
  61. float Depth : TEXCOORD3; // For _CreateShadowMap technique
  62. };
  63. // ----------------------------------------------------------------------------
  64. ParticleVSOutput ParticleVertexShader(
  65. float4 StartPositionLifeInFrames : POSITION,
  66. float4 StartVelocityCreationFrame : TEXCOORD0,
  67. float2 SeedAndIndex : TEXCOORD1,
  68. uniform int fogMode)
  69. {
  70. ParticleVSOutput Out;
  71. // decode vertex data
  72. float3 StartPosition = StartPositionLifeInFrames.xyz;
  73. float LifeInFrames = StartPositionLifeInFrames.w;
  74. float3 StartVelocity = StartVelocityCreationFrame.xyz;
  75. float CreationFrame = StartVelocityCreationFrame.w;
  76. float Seed = SeedAndIndex.x;
  77. float Index = SeedAndIndex.y;
  78. // particle system works with frames, so first convert time to frame
  79. // rather than converting everything else to time
  80. float age = (Time * CLIENT_FRAMES_PER_SECOND - CreationFrame);
  81. // first eliminate dead particles
  82. if (age > LifeInFrames)
  83. {
  84. Index = 0;
  85. }
  86. float relativeAge = age / LifeInFrames;
  87. float3 particlePosition;
  88. float size;
  89. float2x2 zRotationMatrix;
  90. Particle_ComputePhysics(particlePosition, size, zRotationMatrix,
  91. age, StartPosition, StartVelocity, Seed);
  92. // Calculate vertex position
  93. float2 vertexCorner = VertexCorners[Index];
  94. float2 relativeCornerPos = mul(vertexCorner, zRotationMatrix);
  95. float3 xVector = float3( View[0][0], View[1][0], View[2][0] );
  96. float3 zVector = float3( View[0][1], View[1][1], View[2][1] );
  97. float3 cornerPosition = particlePosition + size * (relativeCornerPos.x * xVector + relativeCornerPos.y * zVector);
  98. Out.Position = mul(float4(cornerPosition, 1), WorldViewProjection);
  99. Out.Depth = Out.Position.z / Out.Position.w;
  100. //zVector = -zVector;
  101. float3 Normal = cross(xVector, zVector);
  102. float3 worldNormal = normalize(mul(Normal, (float3x3)World));
  103. float3 worldTangent = -zVector;
  104. float3 worldBinormal = xVector;
  105. float3x3 zRotation3D = float3x3(float3(zRotationMatrix[0], 0), float3(zRotationMatrix[1], 0), float3(0, 0, 1));
  106. // Build 3x3 tranform from object to world space
  107. float3x3 particleToWorldSpace = mul(transpose(zRotation3D), float3x3(-worldBinormal, -worldTangent, worldNormal));
  108. // Build normal for particle vertex
  109. static const float flattenNormal = 1.0; // 0 = totally flat, 1 = sphere normal
  110. /* this stuff depends on the radius of our "sphere" being 1,
  111. but since our corner vertex is at 1,1 if we multiply the original vertices (.5,.5) by 2 the radius is actually
  112. 1.4142. I scaled down the vertex multiplier to make the radius = 1
  113. instead of
  114. float3 normal = float3(vertexCorner * float2(-2, 2) * flattenNormal, 0);
  115. */
  116. float3 normal = float3(vertexCorner * float2(-1.4142, 1.4142) * flattenNormal, 0);
  117. normal.z = sqrt(1.0 - normal.x * normal.x - normal.y * normal.y);
  118. float3 vertexWorldNormal = normalize(mul(normal, particleToWorldSpace));
  119. #if defined(EA_PLATFORM_PS3)
  120. Out.Color = 1.0.xxxx;
  121. #else // #if defined(EA_PLATFORM_PS3)
  122. // Compute directional lights
  123. float3 diffuseLight = 0;
  124. for (int i = 0; i < NumDirectionalLights; i++)
  125. {
  126. float3 lightColor = DirectionalLight[i].Color;
  127. diffuseLight += lightColor * max(0, dot(vertexWorldNormal, normalize(DirectionalLight[i].Direction)));
  128. }
  129. Out.Color = float4(diffuseLight, 1);
  130. #endif // #if defined(EA_PLATFORM_PS3)
  131. // if (fogMode != FogMode_Disabled)
  132. // {
  133. // // Fog depends on world position, but world matrix should be identity.
  134. // Out.Fog = CalculateFog(Fog, cornerPosition, GetEyePosition()).xxx;
  135. // }
  136. // else
  137. // {
  138. // Out.Fog = 0;
  139. // }
  140. // Texture coordinate
  141. float randomIndex = GetRandomFloatValue(float2(0.0f, 1.0f), Seed, 7) * Draw.VideoTex_NumPerRow_LastFrame_SingleRow_isRand.y;
  142. randomIndex -= frac(randomIndex);
  143. float currentTexFrame = age * Draw.SpeedMultiplier + randomIndex;
  144. float2 texCoord = GetVertexTexCoord(vertexCorner);
  145. Out.ParticleTexCoord = Particle_ComputeVideoTextureDefault(currentTexFrame, texCoord);
  146. Out.Color *= Particle_ComputeColor(relativeAge, Seed, true);
  147. return Out;
  148. }
  149. // ----------------------------------------------------------------------------
  150. float4 ParticlePixelShader(ParticleVSOutput In, uniform int fogMode) COLORTARGET
  151. {
  152. float4 TextureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  153. TextureColor.xyz = GammaToLinear(TextureColor.xyz);
  154. float4 color = In.Color * TextureColor;
  155. // float3 fogStrength = saturate(In.Fog);
  156. // if (fogMode == FogMode_Opaque)
  157. // {
  158. // // apply fog
  159. // Color.xyz = lerp(Fog.Color, Color.xyz, fogStrength);
  160. // }
  161. // else if (fogMode == FogMode_Additive)
  162. // {
  163. // // Fog used with additive blending just needs to reduce the additive influence, not blend towards the fog color
  164. // Color.xyz *= fogStrength;
  165. // }
  166. return color;
  167. }
  168. // ----------------------------------------------------------------------------
  169. float4 ParticlePixelShader_Xenon( ParticleVSOutput In ) : COLOR
  170. {
  171. return ParticlePixelShader( In, Fog.IsEnabled ? ((Draw.ShaderType == ShaderType_Additive || Draw.ShaderType == ShaderType_AdditiveAlphaTest || Draw.ShaderType == ShaderType_Multiply) ? FogMode_Additive : FogMode_Opaque) : FogMode_Disabled );
  172. }
  173. // ----------------------------------------------------------------------------
  174. // TECHNIQUE: DEFAULT
  175. // ----------------------------------------------------------------------------
  176. #define PS_ShaderType \
  177. compile PS_2_0 ParticlePixelShader(FogMode_Disabled), \
  178. compile PS_2_0 ParticlePixelShader(FogMode_Opaque), \
  179. compile PS_2_0 ParticlePixelShader(FogMode_Additive)
  180. DEFINE_ARRAY_MULTIPLIER( PS_Multiplier_Final = 3 );
  181. #if SUPPORTS_SHADER_ARRAYS
  182. pixelshader PS_Array[PS_Multiplier_Final] =
  183. {
  184. PS_ShaderType
  185. };
  186. #endif
  187. // ----------------------------------------------------------------------------
  188. technique Default_M
  189. {
  190. pass P0
  191. <
  192. USE_EXPRESSION_EVALUATOR("Particle")
  193. >
  194. {
  195. VertexShader = compile VS_2_0 ParticleVertexShader(FogMode_Opaque);
  196. PixelShader = ARRAY_EXPRESSION_PS( PS_Array,
  197. Fog.IsEnabled ? ((Draw.ShaderType == ShaderType_Additive || Draw.ShaderType == ShaderType_AdditiveAlphaTest || Draw.ShaderType == ShaderType_Multiply) ? FogMode_Additive : FogMode_Opaque) : FogMode_Disabled,
  198. compile PS_VERSION ParticlePixelShader_Xenon()
  199. );
  200. ZEnable = true;
  201. ZFunc = ZFUNC_INFRONT;
  202. ZWriteEnable = false;
  203. CullMode = None;
  204. SETUP_ALPHA_BLEND_AND_TEST(Draw.ShaderType);
  205. }
  206. }
  207. #if ENABLE_LOD
  208. // ----------------------------------------------------------------------------
  209. // SHADER: LowQuality
  210. // ----------------------------------------------------------------------------
  211. struct ParticleVSLowOutput
  212. {
  213. float4 Position : POSITION;
  214. float2 ParticleTexCoord : TEXCOORD0;
  215. float4 Color : COLOR0;
  216. };
  217. // ----------------------------------------------------------------------------
  218. ParticleVSLowOutput ParticleVertexShaderLow(float4 StartPositionLifeInFrames : POSITION,
  219. float4 StartVelocityCreationFrame : TEXCOORD0, float2 SeedAndIndex : TEXCOORD1)
  220. {
  221. ParticleVSLowOutput Out;
  222. // decode vertex data
  223. float3 StartPosition = StartPositionLifeInFrames.xyz;
  224. float LifeInFrames = StartPositionLifeInFrames.w;
  225. float3 StartVelocity = StartVelocityCreationFrame.xyz;
  226. float CreationFrame = StartVelocityCreationFrame.w;
  227. float Seed = SeedAndIndex.x;
  228. float Index = SeedAndIndex.y;
  229. // particle system works with frames, so first convert time to frame
  230. // rather than converting everything else to time
  231. float age = (Time * CLIENT_FRAMES_PER_SECOND - CreationFrame);
  232. // first eliminate dead particles
  233. if (age > LifeInFrames)
  234. Index = 0;
  235. float relativeAge = age / LifeInFrames;
  236. float3 particlePosition;
  237. float size;
  238. float2x2 zRotationMatrix;
  239. Particle_ComputePhysics_Simplified(particlePosition, size, zRotationMatrix,
  240. age, StartPosition, StartVelocity, Seed);
  241. // Calculate vertex position
  242. float2 vertexCorner = VertexCorners[Index];
  243. float2 relativeCornerPos = mul(vertexCorner, zRotationMatrix);
  244. float3 xVector = float3( View[0][0], View[1][0], View[2][0] );
  245. float3 zVector = float3( View[0][1], View[1][1], View[2][1] );
  246. float3 cornerPosition = particlePosition + size * (relativeCornerPos.x * xVector + relativeCornerPos.y * zVector);
  247. Out.Position = mul(float4(cornerPosition, 1), WorldViewProjection);
  248. // texture coordinate
  249. float2 texCoord = GetVertexTexCoord(vertexCorner);
  250. Out.ParticleTexCoord = Particle_ComputeVideoTextureDefault(age * Draw.SpeedMultiplier, texCoord);
  251. // compute color
  252. Out.Color = Particle_ComputeColor(relativeAge, Seed, false);
  253. return Out;
  254. }
  255. // ----------------------------------------------------------------------------
  256. float4 ParticlePixelShaderLow(ParticleVSLowOutput In) : COLOR
  257. {
  258. float4 TextureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  259. float4 Color = TextureColor * In.Color;
  260. return Color;
  261. }
  262. // ----------------------------------------------------------------------------
  263. // TECHNIQUE: LowQuality
  264. // ----------------------------------------------------------------------------
  265. technique _Default_L
  266. {
  267. pass P0
  268. <
  269. USE_EXPRESSION_EVALUATOR("Particle")
  270. >
  271. {
  272. VertexShader = compile VS_2_0 ParticleVertexShaderLow();
  273. PixelShader = compile PS_2_0 ParticlePixelShaderLow();
  274. ZEnable = true;
  275. ZFunc = ZFUNC_INFRONT;
  276. ZWriteEnable = false;
  277. CullMode = None;
  278. SETUP_ALPHA_BLEND_AND_TEST(Draw.ShaderType);
  279. }
  280. }
  281. #endif // #if ENABLE_LOD
  282. // ----------------------------------------------------------------------------
  283. // SHADER: ShadowMap
  284. // ----------------------------------------------------------------------------
  285. float4 CreateShadowMapPS(ParticleVSOutput In, uniform int shaderType) COLORTARGET
  286. {
  287. float4 textureColor = tex2D( SAMPLER(ParticleTextureSampler), In.ParticleTexCoord);
  288. float4 color = textureColor * In.Color;
  289. // Threshold where "alpha testing" or color equivalents set pixel to be opaque.
  290. // Needs to be much lower than common alpha test threshold as a single particle is usually quite transparent.
  291. const float opacityThreshold = 0.1;
  292. if (shaderType == ShaderType_Additive)
  293. {
  294. // The brighter the color the denser the particle. Clip dark areas.
  295. clip(dot(color, float3(1, 1, 1)) - 3 * opacityThreshold);
  296. }
  297. else if (shaderType == ShaderType_Multiply)
  298. {
  299. // The darker the color the denser the particle. Clip bright areas.
  300. clip(3 * opacityThreshold - dot(color, float3(1, 1, 1)));
  301. }
  302. else if (shaderType == ShaderType_AdditiveAlphaTest || shaderType == ShaderType_Alpha
  303. || shaderType == ShaderType_AlphaTest)
  304. {
  305. // Simulate alpha testing
  306. clip(color.a - opacityThreshold);
  307. }
  308. return In.Depth;
  309. }
  310. // ----------------------------------------------------------------------------
  311. float4 CreateShadowMapPS_Xenon( ParticleVSOutput In ) : COLOR
  312. {
  313. return CreateShadowMapPS( In, min(Draw.ShaderType, ShaderType_Multiply) );
  314. }
  315. // ----------------------------------------------------------------------------
  316. // TECHNIQUE: CreateShadowMap
  317. // ----------------------------------------------------------------------------
  318. #define PSCreateShadowMap_ShaderType \
  319. compile PS_2_0 CreateShadowMapPS(0), \
  320. compile PS_2_0 CreateShadowMapPS(ShaderType_Additive), \
  321. compile PS_2_0 CreateShadowMapPS(ShaderType_AdditiveAlphaTest), \
  322. compile PS_2_0 CreateShadowMapPS(ShaderType_Alpha), \
  323. compile PS_2_0 CreateShadowMapPS(ShaderType_AlphaTest), \
  324. compile PS_2_0 CreateShadowMapPS(ShaderType_Multiply)
  325. DEFINE_ARRAY_MULTIPLIER( PSCreateShadowMap_Multiplier_Final = 6 );
  326. #if SUPPORTS_SHADER_ARRAYS
  327. pixelshader PSCreateShadowMap_Array[PSCreateShadowMap_Multiplier_Final] =
  328. {
  329. PSCreateShadowMap_ShaderType
  330. };
  331. #endif
  332. // ----------------------------------------------------------------------------
  333. technique _CreateShadowMap
  334. {
  335. pass P0
  336. <
  337. USE_EXPRESSION_EVALUATOR("GPUParticle_CreateShadowMap")
  338. >
  339. {
  340. VertexShader = compile VS_2_0 ParticleVertexShader(FogMode_Disabled);
  341. PixelShader = ARRAY_EXPRESSION_PS( PSCreateShadowMap_Array,
  342. min(Draw.ShaderType, ShaderType_Multiply),
  343. compile PS_VERSION CreateShadowMapPS_Xenon()
  344. );
  345. ZEnable = true;
  346. ZFunc = ZFUNC_INFRONT;
  347. ZWriteEnable = true;
  348. CullMode = None;
  349. AlphaBlendEnable = false;
  350. AlphaTestEnable = false; // Handled in pixel shader
  351. }
  352. }