| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- #include "$ENGINE$\PerCameraData.bslinc"
- #include "$ENGINE$\PerObjectData.bslinc"
- // Note: I should include VertexCommon.bslinc here, instead of redefining vertex input code below
- mixin ParticleVertex
- {
- mixin PerCameraData;
- mixin PerObjectData;
- variations
- {
- ORIENT = { 0, 1, 2 }; // 0 - Camera plane, 1 - Camera position, 2 - Axis
- LOCK_Y = { false, true };
- GPU = { false, true };
- IS_3D = { false, true };
- };
- code
- {
- #define RENDER_3D IS_3D && !GPU
-
- struct VertexInput
- {
- #if RENDER_3D
- float3 position : POSITION;
- #else
- float2 position : POSITION;
- #endif
-
- float2 uv0 : TEXCOORD0;
- uint instanceId : SV_InstanceID;
- #ifdef LIGHTING_DATA
- float3 normal : NORMAL; // Note: Half-precision could be used
- float4 tangent : TANGENT; // Note: Half-precision could be used
- #endif
- };
-
- #if RENDER_3D
- struct ParticleInput
- {
- float3 position;
- float3 rotation;
- float3 size;
- float3 velocity;
- float4 color;
- };
- #else
- struct ParticleInput
- {
- float3 position;
- float rotation;
- float2 uvflip;
- float2 size;
- float3 velocity;
- float frame;
- float4 color;
- };
- #endif
-
- struct VStoFS
- {
- float4 position : SV_Position;
- float2 uv0 : TEXCOORD0;
- float4 color : COLOR0;
-
- #ifdef VIEW_DEPTH
- float depth : TEXCOORD1;
- #endif
- #ifdef LIGHTING_DATA
- float3 worldPosition : TEXCOORD2;
-
- float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used
- float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used
- #endif
- };
-
- #if GPU
- [internal]
- Texture2D gPositionTimeTex; // position in .xyz, time (in [0, 1] range) in .w
-
- [internal]
- Texture2D gSizeRotationTex; // size in .xy, rotation in .z
-
- [internal]
- Texture2D gCurvesTex;
-
- [alias(gCurvesTex)]
- SamplerState gCurvesSampler;
-
- [internal]
- cbuffer GpuParticleParams
- {
- float2 gColorCurveOffset;
- float2 gColorCurveScale;
- float2 gSizeScaleFrameIdxCurveOffset;
- float2 gSizeScaleFrameIdxCurveScale;
- };
-
- ParticleInput getParticleInput(uint2 index)
- {
- uint3 pixelPos;
- pixelPos.xy = index;
- pixelPos.z = 0;
-
- ParticleInput pi;
-
- float4 posAndTime = gPositionTimeTex.Load(pixelPos);
- pi.position = posAndTime.xyz;
-
- float time = posAndTime.w;
- float aliveMask = step(time, 1.0f);
-
- float4 sizeAndRotation = gSizeRotationTex.Load(pixelPos);
-
- float2 sizeScaleFrameIdxCurveUV = gSizeScaleFrameIdxCurveOffset + time * gSizeScaleFrameIdxCurveScale;
- float3 sizeScaleFrameIdx = gCurvesTex.SampleLevel(gCurvesSampler, sizeScaleFrameIdxCurveUV, 0).xyz;
-
- float2 sizeScale = sizeScaleFrameIdx.xy;
-
- pi.uvflip = sign(sizeAndRotation.xy);
- pi.size = abs(sizeAndRotation.xy) * sizeScale.xy * aliveMask;
- pi.rotation = sizeAndRotation.z;
-
- pi.frame = sizeScaleFrameIdx.z;
-
- float2 colorCurveUV = gColorCurveOffset + time * gColorCurveScale;
- pi.color = gCurvesTex.SampleLevel(gCurvesSampler, colorCurveUV, 0);
-
- return pi;
- }
- #elif IS_3D
- [internal]
- Texture2D gPositionTex;
-
- [internal]
- Texture2D gColorTex;
-
- [internal]
- Texture2D gSizeTex;
-
- [internal]
- Texture2D gRotationTex;
-
- ParticleInput getParticleInput(uint2 index)
- {
- uint3 pixelPos;
- pixelPos.xy = index;
- pixelPos.z = 0;
-
- ParticleInput pi;
-
- pi.position = gPositionTex.Load(pixelPos).xyz;
- pi.rotation = gRotationTex.Load(pixelPos).xyz;
- pi.color = gColorTex.Load(pixelPos);
- pi.size = gSizeTex.Load(pixelPos).xyz;
-
- return pi;
- }
-
- float3x3 getRotationScaleMatrix(float3 anglesRad, float3 scale)
- {
- float cx, sx, cy, sy, cz, sz;
- sincos(anglesRad.x, sx, cx);
- sincos(anglesRad.y, sy, cy);
- sincos(anglesRad.z, sz, cz);
-
- float3x3 m;
- m[0][0] = (cy * cz + sx * sy * sz) * scale.x;
- m[0][1] = cz * sx * sy - cy * sz;
- m[0][2] = cx * sy;
- m[1][0] = cx * sz;
- m[1][1] = cx * cz * scale.y;
- m[1][2] = -sx;
- m[2][0] = -cz * sy + cy * sx * sz;
- m[2][1] = cy * cz * sx + sy * sz;
- m[2][2] = cx * cy * scale.z;
-
- return m;
- }
-
- #else
- [internal]
- Texture2D gPositionAndRotTex; // position in .xyz, rotation in radians in .w
- [internal]
- Texture2D gColorTex;
- [internal]
- Texture2D gSizeAndFrameIdxTex; // size in .xy, uv flip encoded in sign of .xy, frame index in .z, .w unused
-
- ParticleInput getParticleInput(uint2 index)
- {
- uint3 pixelPos;
- pixelPos.xy = index;
- pixelPos.z = 0;
-
- ParticleInput pi;
-
- float4 posAndRot = gPositionAndRotTex.Load(pixelPos);
- pi.position = posAndRot.xyz;
- pi.rotation = posAndRot.w;
-
- float4 sizeAndFrame = gSizeAndFrameIdxTex.Load(pixelPos);
- pi.uvflip = sign(sizeAndFrame.xy);
- pi.size = abs(sizeAndFrame.xy);
- pi.frame = sizeAndFrame.z;
-
- pi.color = gColorTex.Load(pixelPos);
-
- return pi;
- }
- #endif
-
- [internal]
- Buffer<uint2> gIndices;
- [internal]
- cbuffer ParticleParams
- {
- float4 gSubImageSize; // .xy column/row count, .zw inverse column/row count
- float2 gParticleUVOffset;
- float2 gParticleUVSize;
- float3 gAxisUp;
- uint gTexSize;
- float3 gAxisRight;
- uint gBufferOffset;
- }
-
- #ifdef LIGHTING_DATA
- float3x3 getTangentToLocal(VertexInput input, out float tangentSign)
- {
- float3 normal = input.normal * 2.0f - 1.0f;
- float3 tangent = input.tangent.xyz * 2.0f - 1.0f;
-
- tangentSign = input.tangent.w < 0.5f ? -1.0f : 1.0f;
- float3 bitangent = cross(normal, tangent) * tangentSign;
- tangentSign *= gWorldDeterminantSign;
-
- // Note: Maybe it's better to store everything in row vector format?
- float3x3 result = float3x3(tangent, bitangent, normal);
- result = transpose(result);
-
- return result;
- }
-
- float3 calcWorldNormal(VStoFS input, float3 surfaceNormal)
- {
- float3 tangentToWorldX = input.tangentToWorldX.xyz;
- float3 tangentToWorldZ = input.tangentToWorldZ;
- float3 tangentToWorldY = cross(tangentToWorldZ, tangentToWorldX) * input.tangentToWorldX.w;
-
- float3x3 tangentToWorld = float3x3(tangentToWorldX, tangentToWorldY, tangentToWorldZ);
-
- // Multiplication order flipped because we stored basis vectors as rows
- return normalize(mul(surfaceNormal, tangentToWorld));
- }
- #endif
- VStoFS vsmain(VertexInput input)
- {
- ParticleInput pi = getParticleInput(gIndices[gBufferOffset + input.instanceId]);
-
- VStoFS output;
- output.color = pi.color;
-
- float4 worldPosition = mul(gMatWorld, float4(pi.position, 1.0f));
- #if RENDER_3D
- float3x3 rotScale = getRotationScaleMatrix(pi.rotation, pi.size);
- worldPosition.xyz += mul(rotScale, input.position);
- output.uv0 = input.uv0;
- #else // RENDER_3D
- float3 axisRight, axisUp;
- #if ORIENT == 2 // Axis
- axisRight = gAxisRight;
- axisUp = gAxisUp;
- #elif ORIENT == 1 // Towards camera origin
- float3 diff = gViewOrigin - worldPosition.xyz;
-
- float3 cameraUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
- axisRight = normalize(cross(diff, cameraUp));
-
- #if LOCK_Y
- axisUp = float3(0, 1, 0);
- #else
- axisUp = normalize(cross(axisRight, diff));
- #endif
- #else // Towards camera plane
- axisRight = float3(gMatView[0].x, gMatView[0].y, gMatView[0].z);
-
- #if LOCK_Y
- axisUp = float3(0, 1, 0);
- #else
- axisUp = float3(gMatView[1].x, gMatView[1].y, gMatView[1].z);
- #endif
- #endif
- float rotSin, rotCos;
- sincos(pi.rotation, rotSin, rotCos);
-
- float3 rotAxisRight = rotSin * axisUp + rotCos * axisRight;
- float3 rotAxisUp = rotCos * axisUp - rotSin * axisRight;
-
- worldPosition.xyz += rotAxisRight * input.position.x * pi.size.x + rotAxisUp * input.position.y * pi.size.y;
-
- float2 uv;
- uv.x = pi.uvflip.x >= 0.0f ? input.uv0.x : 1.0f - input.uv0.x;
- uv.y = pi.uvflip.y >= 0.0f ? input.uv0.y : 1.0f - input.uv0.y;
-
- float frame = pi.frame - frac(pi.frame);
- float row = floor(frame * gSubImageSize.z);
- float column = fmod(frame, gSubImageSize.x);
-
- float2 subUV = (float2(row, column) + uv) * gSubImageSize.zw;
- output.uv0 = gParticleUVOffset + subUV * gParticleUVSize;
- #endif // RENDER_3D
- #ifdef LIGHTING_DATA
- float tangentSign;
- float3x3 tangentToLocal = getTangentToLocal(input, tangentSign);
- float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal);
-
- // Note: Consider transposing these externally, for easier reads
- output.tangentToWorldZ = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector
- output.tangentToWorldX = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], tangentSign); // Tangent basis vector
-
- output.worldPosition = worldPosition.xyz;
- #endif
-
- output.position = mul(gMatViewProj, worldPosition);
-
- #ifdef VIEW_DEPTH
- output.depth = output.position.w;
- #endif
-
- return output;
- }
- };
- };
|