#pragma kernel CSMain ${VFXPragmaOnlyRenderers} ${VFXPragmaRequire} ${VFXGlobalInclude} ${VFXGlobalDeclaration} #ifdef VFX_IS_RAYTRACED #pragma multi_compile _ VFX_COMPUTE_AABBS #endif #if USE_MULTI_COMPILE_XR_IN_OUTPUT_UPDATE #pragma multi_compile _ UNITY_SINGLE_PASS_STEREO STEREO_INSTANCING_ON STEREO_MULTIVIEW_ON #endif #if HAS_STRIPS StructuredBuffer stripDataBuffer; #endif ${VFXInclude("Shaders/VFXParticleCommon.template")} ByteAddressBuffer attributeBuffer; #if VFX_FEATURE_MOTION_VECTORS RWByteAddressBuffer elementToVFXBuffer; #endif #if VFX_FEATURE_SORT struct Kvp { float sortKey; uint index; }; #define IndirectOutputType Kvp #else #define IndirectOutputType uint #endif #if INDIRECT_BUFFER_COUNT > 0 RWStructuredBuffer outputBuffer0; #endif #if INDIRECT_BUFFER_COUNT > 1 RWStructuredBuffer outputBuffer1; #endif #if INDIRECT_BUFFER_COUNT > 2 RWStructuredBuffer outputBuffer2; #endif #if INDIRECT_BUFFER_COUNT > 3 RWStructuredBuffer outputBuffer3; #endif #if INDIRECT_BUFFER_COUNT > 4 #error Too many indirect buffers defined. #endif CBUFFER_START(updateParamsConst) uint dispatchWidth; ${VFXInstancingConstants} float3 cameraXRSettings; CBUFFER_END ${VFXPerPassInclude} ${VFXGeneratedBlockFunction} #if VFX_FEATURE_FRUSTUM_CULL || VFX_FEATURE_LOD #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/GeometricTools.hlsl" bool IsSphereOutsideFrustum(float3 pos, float radius, float4 frustumPlanes[6]) { bool outside = false; [unroll] for (int i = 0; i < 6; ++i) outside = outside || DistanceFromPlane(pos, frustumPlanes[i]) < -radius; return outside; } #endif #if HAS_STRIPS ${VFXDeclareGetStripTangent} #endif ${VFXDeclareAppendOutputIndirectBuffer} [numthreads(NB_THREADS_PER_GROUP,1,1)] void CSMain(uint3 groupId : SV_GroupID, uint3 groupThreadId : SV_GroupThreadID) { uint id = GetThreadId(groupId, groupThreadId, dispatchWidth); ${VFXInitInstancingCompute} ${VFXLoadContextData} uint systemSeed = contextData.systemSeed; uint nbMax = contextData.maxParticleCount; if (index < nbMax) { ${VFXLoadGraphValues} #if VFX_LOCAL_SPACE ${VFXLoadParameter:{localToWorld}} vfxLocalToWorld = localToWorld; ${VFXLoadParameter:{worldToLocal}} vfxWorldToLocal = worldToLocal; #else vfxWorldToLocal = k_identity4x4; vfxLocalToWorld = k_identity4x4; #endif VFXAttributes attributes = (VFXAttributes)0; VFXSourceAttributes sourceAttributes = (VFXSourceAttributes)0; ${VFXLoadAttributes:{alive}} #if HAS_STRIPS // For strips, we need to render the entire strip, even if some particles are dead. const StripData stripData = GetStripDataFromParticleIndex(index, instanceIndex); InitStripAttributes(index, attributes, stripData); uint relativeIndexInStrip = GetRelativeIndex(index, stripData); if (relativeIndexInStrip < stripData.nextIndex) #else if (attributes.alive) #endif { ${VFXLoadAttributes:{(?!(alive))(\b\w)}} ${VFXProcessBlocks} #if !HAS_STRIPS // Recheck alive as blocks can set it to false for manual culling. // Test will be stripped if it's not the case anyway. if (attributes.alive) #endif { ${VFXLoadSize} float3 center = attributes.position; #if HAS_STRIPS // Add an epsilon so that size is never 0 for strips size3.x += size3.x < 0.0f ? -VFX_EPSILON : VFX_EPSILON; size3.y += size3.y < 0.0f ? -VFX_EPSILON : VFX_EPSILON; size3.z += size3.z < 0.0f ? -VFX_EPSILON : VFX_EPSILON; if (relativeIndexInStrip < stripData.nextIndex - 1) { uint nextIndex = GetParticleIndex(relativeIndexInStrip + 1,stripData); center = (center + GetParticlePosition(nextIndex, instanceIndex)) * 0.5f; size3 += abs(center - attributes.position) * sign(size3); } #endif float4x4 elementToVFX = GetElementToVFXMatrix( attributes.axisX, attributes.axisY, attributes.axisZ, float3(attributes.angleX,attributes.angleY,attributes.angleZ), float3(attributes.pivotX,attributes.pivotY,attributes.pivotZ), size3, center); #if VFX_FEATURE_FRUSTUM_CULL || VFX_FEATURE_LOD #if VFX_WORLD_SPACE float4x4 elementToWorld = elementToVFX; elementToWorld._m03_m13_m23 = GetCameraRelativePositionWS(elementToWorld._m03_m13_m23); #else float4x4 elementToWorld = mul(GetObjectToWorldMatrix(),elementToVFX); #endif float xAxisSqrLength = dot(elementToWorld._m00_m10_m20, elementToWorld._m00_m10_m20); float yAxisSqrLength = dot(elementToWorld._m01_m11_m21, elementToWorld._m01_m11_m21); float zAxisSqrLength = dot(elementToWorld._m02_m12_m22, elementToWorld._m02_m12_m22); float radius = 0.5f * sqrt(xAxisSqrLength + yAxisSqrLength + zAxisSqrLength); ${VFXLoadParameter:{radiusScale}} radius *= radiusScale; #if VFX_FEATURE_FRUSTUM_CULL if (IsSphereOutsideFrustum(elementToWorld._m03_m13_m23, radius, _FrustumPlanes)) return; #endif #endif #if INDIRECT_BUFFER_COUNT > 0 #if VFX_FEATURE_LOD uint outputIndex = ~0u; #if !VFX_FEATURE_FRUSTUM_CULL // If particle is out of frustum and frustum culling is disabled, use the lowest LOD // This is useful for shadow passes for instance to avoid out of frustum particles to be culled from shadows if (IsSphereOutsideFrustum(elementToWorld._m03_m13_m23, radius, _FrustumPlanes)) outputIndex = INDIRECT_BUFFER_COUNT - 1; else #endif { float viewZ = mul(GetWorldToViewMatrix(), float4(elementToWorld._m03_m13_m23, 1)).z; float4x4 centeredProjMatrix = GetViewToHClipMatrix(); centeredProjMatrix._13_14_23_24 = 0.0f; //Cancels the jittering and/or the eye offset when in VR float4 clip = mul(centeredProjMatrix, float4(radius, radius, viewZ, 1)); float lodValue = max(abs(clip.x),abs(clip.y)) * rcp(max(VFX_EPSILON, clip.w)); ${VFXLoadParameter:{lodValues}} for (uint i = 0; i < INDIRECT_BUFFER_COUNT; ++i) if (lodValue > lodValues[i]) { outputIndex = i; break; } } #elif INDIRECT_BUFFER_COUNT == 1 uint outputIndex = 0; #else uint outputIndex = attributes.meshIndex; #endif if (outputIndex >= INDIRECT_BUFFER_COUNT) return; #if VFX_FEATURE_SORT ${VFXFeedSortingKeys} #else uint output = index; #endif if (outputIndex == 0) { AppendOutputBuffer(outputBuffer0, output, instanceActiveIndex); } #if INDIRECT_BUFFER_COUNT > 1 else if (outputIndex == 1) { AppendOutputBuffer(outputBuffer1, output, instanceActiveIndex); } #if INDIRECT_BUFFER_COUNT > 2 else if (outputIndex == 2) { AppendOutputBuffer(outputBuffer2, output, instanceActiveIndex); } #if INDIRECT_BUFFER_COUNT > 3 else if (outputIndex == 3) { AppendOutputBuffer(outputBuffer3, output, instanceActiveIndex); } #endif #endif #endif #endif #if VFX_FEATURE_MOTION_VECTORS uint offsetedIndex = (RAW_CAPACITY * instanceIndex) + index; #ifdef VFX_FEATURE_MOTION_VECTORS_VERTS uint viewTotal = asuint(cameraXRSettings.x); uint viewCount = asuint(cameraXRSettings.y); uint viewOffset = asuint(cameraXRSettings.z); uint elementToVFXIndex = offsetedIndex * (VFX_FEATURE_MOTION_VECTORS_VERTS * 2 * viewTotal + 1); #else uint elementToVFXIndex = offsetedIndex * 13; #endif ${VFXLoadParameter:{currentFrameIndex}} elementToVFXBuffer.Store(elementToVFXIndex++ << 2, attributes.alive ? asuint(currentFrameIndex) : 0u); #ifdef VFX_FEATURE_MOTION_VECTORS_VERTS elementToVFXIndex += viewOffset * viewCount * VFX_FEATURE_MOTION_VECTORS_VERTS * 2; ${VFXLoadParameter:{targetOffset}} ${VFXMotionVectorVerts} #ifdef USING_STEREO_MATRICES if (viewCount == 2) { float4 stereoOffset = float4(GetWorldStereoOffset(), 0.0f); UNITY_UNROLL for (int itIndexVert = 0; itIndexVert < VFX_FEATURE_MOTION_VECTORS_VERTS; ++itIndexVert) { float4 vertPosWorld = float4(TransformPositionVFXToWorld(verts[itIndexVert]), 1.0f); vertPosWorld.xyz = VFXTransformPositionWorldToCameraRelative(vertPosWorld.xyz); float4 vertPosA = mul(GetNonJitteredViewProjMatrix(0), vertPosWorld); float4 vertPosB = mul(GetNonJitteredViewProjMatrix(1), vertPosWorld + stereoOffset); elementToVFXBuffer.Store4(elementToVFXIndex << 2, asuint(float4(vertPosA.xy / vertPosA.w, vertPosB.xy / vertPosB.w))); elementToVFXIndex += 4; // 1 vert * 2 floats * 2 views } } else #endif { UNITY_UNROLL for (int itIndexVert = 0; itIndexVert < VFX_FEATURE_MOTION_VECTORS_VERTS - 1; itIndexVert += 2) { float4 vertPosA = TransformPositionVFXToNonJitteredClip(verts[itIndexVert]); float4 vertPosB = TransformPositionVFXToNonJitteredClip(verts[itIndexVert + 1]); elementToVFXBuffer.Store4(elementToVFXIndex << 2, asuint(float4(vertPosA.xy / vertPosA.w, vertPosB.xy / vertPosB.w))); elementToVFXIndex += 4; // 2 verts * 2 floats } if (VFX_FEATURE_MOTION_VECTORS_VERTS % 2 == 1) { int itIndexVert = VFX_FEATURE_MOTION_VECTORS_VERTS - 1; float4 vertPos = TransformPositionVFXToNonJitteredClip(verts[itIndexVert]); elementToVFXBuffer.Store2(elementToVFXIndex << 2, asuint(vertPos.xy / vertPos.w)); elementToVFXIndex += 2; // 1 verts * 2 floats } } #else UNITY_UNROLL for (int itIndexMatrixRow = 0; itIndexMatrixRow < 3; ++itIndexMatrixRow) { float4 value = elementToVFX[itIndexMatrixRow] * attributes.alive; elementToVFXBuffer.Store4(elementToVFXIndex << 2, asuint(value)); elementToVFXIndex += 4; // 1 row * 4 floats } #endif #endif } } #if VFX_COMPUTE_AABBS ${VFXLoadSizeRT} int rayTracingDecimationFactor = VFX_RT_DECIMATION_FACTOR; FillAabbBuffer(attributes, size3, index, instanceIndex, rayTracingDecimationFactor); #endif } }