ParticleVertex.bslinc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #include "$ENGINE$\PerCameraData.bslinc"
  2. #include "$ENGINE$\PerObjectData.bslinc"
  3. // Note: I should include VertexCommon.bslinc here, instead of redefining vertex input code below
  4. mixin ParticleVertex
  5. {
  6. mixin PerCameraData;
  7. mixin PerObjectData;
  8. variations
  9. {
  10. ORIENT = { 0, 1, 2 }; // 0 - Camera plane, 1 - Camera position, 2 - Axis
  11. LOCK_Y = { false, true };
  12. GPU = { false, true };
  13. IS_3D = { false, true };
  14. };
  15. code
  16. {
  17. #define RENDER_3D IS_3D && !GPU
  18. struct VertexInput
  19. {
  20. #if RENDER_3D
  21. float3 position : POSITION;
  22. #else
  23. float2 position : POSITION;
  24. #endif
  25. float2 uv0 : TEXCOORD0;
  26. uint instanceId : SV_InstanceID;
  27. #ifdef LIGHTING_DATA
  28. float3 normal : NORMAL; // Note: Half-precision could be used
  29. float4 tangent : TANGENT; // Note: Half-precision could be used
  30. #endif
  31. };
  32. #if RENDER_3D
  33. struct ParticleInput
  34. {
  35. float3 position;
  36. float3 rotation;
  37. float3 size;
  38. float3 velocity;
  39. float4 color;
  40. };
  41. #else
  42. struct ParticleInput
  43. {
  44. float3 position;
  45. float rotation;
  46. float2 uvflip;
  47. float2 size;
  48. float3 velocity;
  49. float frame;
  50. float4 color;
  51. };
  52. #endif
  53. struct VStoFS
  54. {
  55. float4 position : SV_Position;
  56. float2 uv0 : TEXCOORD0;
  57. float4 color : COLOR0;
  58. #ifdef VIEW_DEPTH
  59. float depth : TEXCOORD1;
  60. #endif
  61. #ifdef LIGHTING_DATA
  62. float3 worldPosition : TEXCOORD2;
  63. float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
  64. float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
  65. #endif
  66. };
  67. #if GPU
  68. [internal]
  69. Texture2D gPositionTimeTex; // position in .xyz, time (in [0, 1] range) in .w
  70. [internal]
  71. Texture2D gSizeRotationTex; // size in .xy, rotation in .z
  72. [internal]
  73. Texture2D gCurvesTex;
  74. [alias(gCurvesTex)]
  75. SamplerState gCurvesSampler;
  76. [internal]
  77. cbuffer GpuParticleParams
  78. {
  79. float2 gColorCurveOffset;
  80. float2 gColorCurveScale;
  81. float2 gSizeScaleFrameIdxCurveOffset;
  82. float2 gSizeScaleFrameIdxCurveScale;
  83. };
  84. ParticleInput getParticleInput(uint2 index)
  85. {
  86. uint3 pixelPos;
  87. pixelPos.xy = index;
  88. pixelPos.z = 0;
  89. ParticleInput pi;
  90. float4 posAndTime = gPositionTimeTex.Load(pixelPos);
  91. pi.position = posAndTime.xyz;
  92. float time = posAndTime.w;
  93. float aliveMask = step(time, 1.0f);
  94. float4 sizeAndRotation = gSizeRotationTex.Load(pixelPos);
  95. float2 sizeScaleFrameIdxCurveUV = gSizeScaleFrameIdxCurveOffset + time * gSizeScaleFrameIdxCurveScale;
  96. float3 sizeScaleFrameIdx = gCurvesTex.SampleLevel(gCurvesSampler, sizeScaleFrameIdxCurveUV, 0).xyz;
  97. float2 sizeScale = sizeScaleFrameIdx.xy;
  98. pi.uvflip = sign(sizeAndRotation.xy);
  99. pi.size = abs(sizeAndRotation.xy) * sizeScale.xy * aliveMask;
  100. pi.rotation = sizeAndRotation.z;
  101. pi.frame = sizeScaleFrameIdx.z;
  102. float2 colorCurveUV = gColorCurveOffset + time * gColorCurveScale;
  103. pi.color = gCurvesTex.SampleLevel(gCurvesSampler, colorCurveUV, 0);
  104. return pi;
  105. }
  106. #elif IS_3D
  107. [internal]
  108. Texture2D gPositionTex;
  109. [internal]
  110. Texture2D gColorTex;
  111. [internal]
  112. Texture2D gSizeTex;
  113. [internal]
  114. Texture2D gRotationTex;
  115. ParticleInput getParticleInput(uint2 index)
  116. {
  117. uint3 pixelPos;
  118. pixelPos.xy = index;
  119. pixelPos.z = 0;
  120. ParticleInput pi;
  121. pi.position = gPositionTex.Load(pixelPos).xyz;
  122. pi.rotation = gRotationTex.Load(pixelPos).xyz;
  123. pi.color = gColorTex.Load(pixelPos);
  124. pi.size = gSizeTex.Load(pixelPos).xyz;
  125. return pi;
  126. }
  127. float3x3 getRotationScaleMatrix(float3 anglesRad, float3 scale)
  128. {
  129. float cx, sx, cy, sy, cz, sz;
  130. sincos(anglesRad.x, sx, cx);
  131. sincos(anglesRad.y, sy, cy);
  132. sincos(anglesRad.z, sz, cz);
  133. float3x3 m;
  134. m[0][0] = (cy * cz + sx * sy * sz) * scale.x;
  135. m[0][1] = cz * sx * sy - cy * sz;
  136. m[0][2] = cx * sy;
  137. m[1][0] = cx * sz;
  138. m[1][1] = cx * cz * scale.y;
  139. m[1][2] = -sx;
  140. m[2][0] = -cz * sy + cy * sx * sz;
  141. m[2][1] = cy * cz * sx + sy * sz;
  142. m[2][2] = cx * cy * scale.z;
  143. return m;
  144. }
  145. #else
  146. [internal]
  147. Texture2D gPositionAndRotTex; // position in .xyz, rotation in radians in .w
  148. [internal]
  149. Texture2D gColorTex;
  150. [internal]
  151. Texture2D gSizeAndFrameIdxTex; // size in .xy, uv flip encoded in sign of .xy, frame index in .z, .w unused
  152. ParticleInput getParticleInput(uint2 index)
  153. {
  154. uint3 pixelPos;
  155. pixelPos.xy = index;
  156. pixelPos.z = 0;
  157. ParticleInput pi;
  158. float4 posAndRot = gPositionAndRotTex.Load(pixelPos);
  159. pi.position = posAndRot.xyz;
  160. pi.rotation = posAndRot.w;
  161. float4 sizeAndFrame = gSizeAndFrameIdxTex.Load(pixelPos);
  162. pi.uvflip = sign(sizeAndFrame.xy);
  163. pi.size = abs(sizeAndFrame.xy);
  164. pi.frame = sizeAndFrame.z;
  165. pi.color = gColorTex.Load(pixelPos);
  166. return pi;
  167. }
  168. #endif
  169. [internal]
  170. Buffer<uint2> gIndices;
  171. [internal]
  172. cbuffer ParticleParams
  173. {
  174. float4 gSubImageSize; // .xy column/row count, .zw inverse column/row count
  175. float2 gParticleUVOffset;
  176. float2 gParticleUVSize;
  177. float3 gAxisUp;
  178. uint gTexSize;
  179. float3 gAxisRight;
  180. uint gBufferOffset;
  181. }
  182. #ifdef LIGHTING_DATA
  183. float3x3 getTangentToLocal(VertexInput input, out float tangentSign)
  184. {
  185. float3 normal = input.normal * 2.0f - 1.0f;
  186. float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
  187. tangentSign = input.tangent.w < 0.5f ? -1.0f : 1.0f;
  188. float3 bitangent = cross(normal, tangent) * tangentSign;
  189. tangentSign *= gWorldDeterminantSign;
  190. // Note: Maybe it's better to store everything in row vector format?
  191. float3x3 result = float3x3(tangent, bitangent, normal);
  192. result = transpose(result);
  193. return result;
  194. }
  195. float3 calcWorldNormal(VStoFS input, float3 surfaceNormal)
  196. {
  197. float3 tangentToWorldX = input.tangentToWorldX.xyz;
  198. float3 tangentToWorldZ = input.tangentToWorldZ;
  199. float3 tangentToWorldY = cross(tangentToWorldZ, tangentToWorldX) * input.tangentToWorldX.w;
  200. float3x3 tangentToWorld = float3x3(tangentToWorldX, tangentToWorldY, tangentToWorldZ);
  201. // Multiplication order flipped because we stored basis vectors as rows
  202. return normalize(mul(surfaceNormal, tangentToWorld));
  203. }
  204. #endif
  205. VStoFS vsmain(VertexInput input)
  206. {
  207. ParticleInput pi = getParticleInput(gIndices[gBufferOffset + input.instanceId]);
  208. VStoFS output;
  209. output.color = pi.color;
  210. float4 worldPosition = mul(gMatWorld, float4(pi.position, 1.0f));
  211. #if RENDER_3D
  212. float3x3 rotScale = getRotationScaleMatrix(pi.rotation, pi.size);
  213. worldPosition.xyz += mul(rotScale, input.position);
  214. output.uv0 = input.uv0;
  215. #else // RENDER_3D
  216. float3 axisRight, axisUp;
  217. #if ORIENT == 2 // Axis
  218. axisRight = gAxisRight;
  219. axisUp = gAxisUp;
  220. #elif ORIENT == 1 // Towards camera origin
  221. float3 diff = gViewOrigin - worldPosition.xyz;
  222. float3 cameraUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
  223. axisRight = normalize(cross(diff, cameraUp));
  224. #if LOCK_Y
  225. axisUp = float3(0, 1, 0);
  226. #else
  227. axisUp = normalize(cross(axisRight, diff));
  228. #endif
  229. #else // Towards camera plane
  230. axisRight = float3(gMatView[0].x, gMatView[0].y, gMatView[0].z);
  231. #if LOCK_Y
  232. axisUp = float3(0, 1, 0);
  233. #else
  234. axisUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
  235. #endif
  236. #endif
  237. float rotSin, rotCos;
  238. sincos(pi.rotation, rotSin, rotCos);
  239. float3 rotAxisRight = rotSin * axisUp + rotCos * axisRight;
  240. float3 rotAxisUp = rotCos * axisUp - rotSin * axisRight;
  241. worldPosition.xyz += rotAxisRight * input.position.x * pi.size.x + rotAxisUp * input.position.y * pi.size.y;
  242. float2 uv;
  243. uv.x = pi.uvflip.x >= 0.0f ? input.uv0.x : 1.0f - input.uv0.x;
  244. uv.y = pi.uvflip.y >= 0.0f ? input.uv0.y : 1.0f - input.uv0.y;
  245. float frame = pi.frame - frac(pi.frame);
  246. float row = floor(frame * gSubImageSize.z);
  247. float column = fmod(frame, gSubImageSize.x);
  248. float2 subUV = (float2(row, column) + uv) * gSubImageSize.zw;
  249. output.uv0 = gParticleUVOffset + subUV * gParticleUVSize;
  250. #endif // RENDER_3D
  251. #ifdef LIGHTING_DATA
  252. float tangentSign;
  253. float3x3 tangentToLocal = getTangentToLocal(input, tangentSign);
  254. float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
  255. // Note: Consider transposing these externally, for easier reads
  256. output.tangentToWorldZ = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
  257. output.tangentToWorldX = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
  258. output.worldPosition = worldPosition.xyz;
  259. #endif
  260. output.position = mul(gMatViewProj, worldPosition);
  261. #ifdef VIEW_DEPTH
  262. output.depth = output.position.w;
  263. #endif
  264. return output;
  265. }
  266. };
  267. };