You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
11 KiB
291 lines
11 KiB
#pragma target 4.5
|
|
|
|
#define SHADERPASS SHADERPASS_VOLUMETRIC_FOG_VFX_VOXELIZATION
|
|
|
|
// Note: picking is not possible with HDRP Volumetric Fog
|
|
#if VFX_PASSDEPTH == VFX_PASSDEPTH_SELECTION
|
|
int _ObjectId;
|
|
int _PassValue;
|
|
#endif
|
|
|
|
struct VertexToFragment
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
nointerpolation float4 sphereInfo : TEXCOORD0;
|
|
nointerpolation float4 colorAndDensity : TEXCOORD1;
|
|
nointerpolation float4 densityData : TEXCOORD2;
|
|
#if defined(HDRP_VOLUMETRIC_MASK)
|
|
float3 uv : TEXCOORD3;
|
|
#endif
|
|
float3 viewDirectionWS : TEXCOORD4;
|
|
nointerpolation uint depthSlice : SV_RenderTargetArrayIndex;
|
|
};
|
|
|
|
${VFXPerPassInclude}
|
|
${VFXGeneratedBlockFunction}
|
|
|
|
struct vs_input
|
|
{
|
|
VFX_DECLARE_INSTANCE_ID
|
|
uint vertexId : SV_VertexID;
|
|
};
|
|
|
|
#define VFX_VARYING_POSCS positionCS
|
|
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Builtin/BuiltinData.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricLighting.cs.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Editor/Material/FogVolume/ShaderGraph/VolumetricMaterialUtils.hlsl"
|
|
|
|
int DistanceToSlice(float distance)
|
|
{
|
|
float t0 = DecodeLogarithmicDepthGeneralized(0, _VBufferDistanceDecodingParams);
|
|
float de = _VBufferRcpSliceCount; // Log-encoded distance between slices
|
|
|
|
float e1 = EncodeLogarithmicDepthGeneralized(distance, _VBufferDistanceEncodingParams);
|
|
e1 -= de;
|
|
e1 /= de;
|
|
|
|
return int(max(0, e1 - 0.5));
|
|
}
|
|
|
|
StructuredBuffer<uint> maxSliceCount;
|
|
|
|
VertexToFragment Vert(vs_input i)
|
|
{
|
|
VertexToFragment o = (VertexToFragment)0;
|
|
|
|
UNITY_SETUP_INSTANCE_ID(i);
|
|
|
|
// Call manually init instancing to get the correct batch index needed to compute the particle ID and call VFXInitInstancing with the correct parameters.
|
|
uint batchIndex, unused1, unused2;
|
|
VFXInitInstancing(0, unused1, batchIndex, unused2);
|
|
|
|
uint id = i.vertexId + VFX_GET_INSTANCE_ID(i) * 2048 * 4;
|
|
uint quadCountPerParticle = maxSliceCount[batchIndex];
|
|
uint quadIndex = id / 4;
|
|
uint quadIndexPerParticle = quadIndex % quadCountPerParticle;
|
|
uint particleId = (quadIndex / quadCountPerParticle);
|
|
uint index = particleId;
|
|
uint viewIndex = unity_StereoEyeIndex;
|
|
|
|
// Cull particles in case more draw calls were issued than necessary, this happens quite a lot when dispatching large number of quads
|
|
if (quadIndex > indirectBuffer[batchIndex])
|
|
return o;
|
|
|
|
${VFXInitInstancing}
|
|
|
|
${VFXLoadContextData}
|
|
uint systemSeed = contextData.systemSeed;
|
|
uint nbMax = contextData.maxParticleCount;
|
|
|
|
${VFXLoadGraphValues}
|
|
VFXAttributes attributes = (VFXAttributes)0;
|
|
VFXSourceAttributes sourceAttributes = (VFXSourceAttributes)0;
|
|
index = indirectBuffer[VFXGetIndirectBufferIndex(index, instanceActiveIndex)];
|
|
${VFXLoadAttributes}
|
|
${VFXProcessBlocks}
|
|
|
|
${VFXLoadParameter:{fadeRadius}}
|
|
${VFXLoadParameter:{density}}
|
|
${VFXLoadParameter:{falloffMode}}
|
|
${VFXLoadParameter:{fogBlendMode}}
|
|
#if defined(HDRP_VOLUMETRIC_MASK)
|
|
${VFXLoadParameter:{isTextureAlpha8}}
|
|
#else
|
|
float isTextureAlpha8 = 0;
|
|
#endif
|
|
|
|
o.densityData = float4(fadeRadius, falloffMode, fogBlendMode, isTextureAlpha8);
|
|
|
|
${VFXLoadSize}
|
|
float uSize = size3.x * 0.5f;
|
|
|
|
float3x3 rot = GetEulerMatrix(radians(float3(attributes.angleX,attributes.angleY,attributes.angleZ)));
|
|
float4x4 elementToVFX = GetElementToVFXMatrix(
|
|
attributes.axisX,
|
|
attributes.axisY,
|
|
attributes.axisZ,
|
|
rot,
|
|
float3(attributes.pivotX,attributes.pivotY,attributes.pivotZ),
|
|
size3,
|
|
attributes.position);
|
|
|
|
float3 vPos = mul(elementToVFX,float4(0, 0, 0, 1.0f)).xyz;
|
|
float3 particleCenterWorldPosition = TransformPositionVFXToWorld(vPos);
|
|
|
|
#ifdef VFX_WORLD_SPACE
|
|
particleCenterWorldPosition = GetCameraRelativePositionWS(particleCenterWorldPosition);
|
|
#endif
|
|
|
|
float distanceToCamera = length(particleCenterWorldPosition);
|
|
float scale = 1.0f - (1.0f - unity_OrthoParams.w) * uSize / distanceToCamera;
|
|
|
|
o.sphereInfo.xyz = particleCenterWorldPosition;
|
|
o.sphereInfo.w = uSize;
|
|
|
|
float vBufferNearplane = DecodeLogarithmicDepthGeneralized(0, _VBufferDistanceDecodingParams);
|
|
float distanceToParticleSphere = distanceToCamera - uSize;
|
|
bool cameraInsideParticle = distanceToParticleSphere <= 0;
|
|
distanceToParticleSphere = max(distanceToParticleSphere, vBufferNearplane);
|
|
|
|
// Calculate the start slice index from the front position of the particle
|
|
uint startSliceIndex = DistanceToSlice(distanceToParticleSphere) + 1;
|
|
uint sliceIndex = startSliceIndex + quadIndexPerParticle;
|
|
o.depthSlice = sliceIndex + viewIndex * _VBufferSliceCount;
|
|
float sliceDistanceToCamera = VBufferDistanceToSliceIndex(sliceIndex);
|
|
|
|
float distanceFadeFactor = 1.0f;
|
|
#if defined(HDRP_VOLUMETRIC_DISTANCE_FADING)
|
|
${VFXLoadParameter:{rcpDistanceFadeLength}}
|
|
${VFXLoadParameter:{endTimesRcpDistanceFadeLength}}
|
|
|
|
distanceFadeFactor = Remap10(sliceDistanceToCamera, rcpDistanceFadeLength, endTimesRcpDistanceFadeLength);
|
|
#endif
|
|
o.colorAndDensity = float4(attributes.color.rgb, attributes.alpha * density * distanceFadeFactor);
|
|
|
|
float distanceBetweenCenterAndSlice = abs(distanceToCamera) - sliceDistanceToCamera;
|
|
|
|
float3 viewDirection = -normalize(particleCenterWorldPosition);
|
|
|
|
if (cameraInsideParticle)
|
|
viewDirection = -normalize(UNITY_MATRIX_V[2].xyz);
|
|
|
|
// When the camera is inside we need to change which algorithm is used to place the slice otherwise the quad
|
|
// may end up being coplanar to the camera which makes the particle disappear.
|
|
float3 offsetPositionRWS;
|
|
if (cameraInsideParticle)
|
|
{
|
|
float3 sliceCenterPosition = viewDirection * sliceDistanceToCamera;
|
|
float3 dir = -normalize(particleCenterWorldPosition - sliceCenterPosition);
|
|
distanceBetweenCenterAndSlice = length(particleCenterWorldPosition - sliceCenterPosition);
|
|
offsetPositionRWS = particleCenterWorldPosition + distanceBetweenCenterAndSlice * dir;
|
|
}
|
|
else
|
|
{
|
|
offsetPositionRWS = particleCenterWorldPosition + (distanceBetweenCenterAndSlice) * viewDirection;
|
|
}
|
|
|
|
// Calculate the exact size of the quad to fit the sphere at a certain position.
|
|
float distanceInSphere = length(offsetPositionRWS - particleCenterWorldPosition);
|
|
float normalizedSliceDistanceToCenter = saturate(distanceInSphere / uSize);
|
|
float sphereScale = saturate(sqrt(1 - abs(normalizedSliceDistanceToCenter * normalizedSliceDistanceToCenter))); // sin(acos(distance))
|
|
|
|
// Due the the froxel buffer having cone shaped froxel, we need to disable the sphere scaling as it doesn't take in account the curvature of the froxel
|
|
if (cameraInsideParticle)
|
|
sphereScale = 1;
|
|
|
|
// Hack to avoid quad artifacts when getting close to the sphere (still due to the shape of the froxel buffer, the quad size is not accurate)
|
|
sphereScale = lerp(sphereScale, 2, 1.0 - saturate(distanceToParticleSphere / uSize));
|
|
|
|
// Manually compute the world position without clip space to reduct SGPR pressure
|
|
float3 tmpCameraRight = UNITY_MATRIX_V[0].xyz;
|
|
float3 cameraUp = normalize(cross(viewDirection, tmpCameraRight));
|
|
float3 cameraRight = normalize(cross(viewDirection, cameraUp));
|
|
|
|
// Scale quad depending on how the slice cut the sphere
|
|
float2 quad = float2(float(id & 1), (id & 2) * 0.5f) * 2 - 1;
|
|
float3 positionWS = offsetPositionRWS + cameraUp * uSize * quad.y * sphereScale + cameraRight * uSize * quad.x * sphereScale;
|
|
|
|
o.positionCS = TransformWorldToHClip(positionWS);
|
|
|
|
o.viewDirectionWS = GetWorldSpaceViewDir(positionWS);
|
|
|
|
#if defined(HDRP_VOLUMETRIC_MASK)
|
|
o.uv = ((positionWS - o.sphereInfo.xyz) / (attributes.scaleX * attributes.size)) + 0.5; // We don't need to multiply by 0.5 because it's already included in the divide by size.
|
|
|
|
#if defined(VFX_APPLY_ANGULAR_ROTATION)
|
|
o.uv = o.uv * 2 - 1;
|
|
float3x3 r = mul((float3x3(attributes.axisX, attributes.axisY, attributes.axisZ)), rot);
|
|
o.uv = mul(r, o.uv);
|
|
o.uv = o.uv * 0.5 + 0.5;
|
|
#endif
|
|
|
|
${VFXLoadParameter:{uvScale}}
|
|
${VFXLoadParameter:{uvBias}}
|
|
o.uv *= uvScale;
|
|
o.uv += uvBias;
|
|
#endif
|
|
|
|
return o;
|
|
}
|
|
|
|
float2 sphIntersect( in float3 ro, in float3 rd, in float3 ce, float ra )
|
|
{
|
|
float3 oc = ro - ce;
|
|
float b = dot( oc, rd );
|
|
float c = dot( oc, oc ) - ra*ra;
|
|
float h = b*b - c;
|
|
if( h<0.0 ) return -1.0; // no intersection
|
|
h = sqrt( h );
|
|
return float2( -b-h, -b+h );
|
|
}
|
|
|
|
float3 CalculateVoxelCenterWS(VertexToFragment i)
|
|
{
|
|
float sliceDepth = VBufferDistanceToSliceIndex(i.depthSlice % _VBufferSliceCount);
|
|
// Compute voxel center position and test against volume OBB
|
|
float3 raycenterDirWS = normalize(-i.viewDirectionWS);
|
|
float3 rayoriginWS = GetCurrentViewPosition();
|
|
float3 voxelCenterWS = rayoriginWS + sliceDepth * raycenterDirWS;
|
|
|
|
return voxelCenterWS;
|
|
}
|
|
|
|
float4 Frag(VertexToFragment i) : SV_Target0
|
|
{
|
|
float3 voxelCenterWS = CalculateVoxelCenterWS(i);
|
|
float distanceToCenter = length(voxelCenterWS - i.sphereInfo.xyz);
|
|
|
|
if (distanceToCenter > i.sphereInfo.w)
|
|
clip(-1);
|
|
|
|
// Fade radius
|
|
float fade = i.densityData.x <= 0 ? 1 : saturate((i.sphereInfo.w - distanceToCenter) / i.densityData.x);
|
|
bool multiplyBlendMode = i.densityData.z == LOCALVOLUMETRICFOGBLENDINGMODE_MULTIPLY;
|
|
bool exponential = i.densityData.y == LOCALVOLUMETRICFOGFALLOFFMODE_EXPONENTIAL;
|
|
|
|
ApplyExponentialFadeFactor(fade, exponential, multiplyBlendMode);
|
|
|
|
float3 albedo = i.colorAndDensity.xyz;
|
|
float extinction = i.colorAndDensity.w;
|
|
|
|
#if defined(HDRP_VOLUMETRIC_MASK)
|
|
float4 maskValue = SAMPLE_TEXTURE3D(mask, samplermask, i.uv);
|
|
if (i.densityData.w == 1) // Alpha8 texture handling
|
|
maskValue = float4(1, 1, 1, maskValue.a);
|
|
albedo *= maskValue.rgb;
|
|
extinction *= maskValue.a;
|
|
#endif
|
|
|
|
if (multiplyBlendMode)
|
|
{
|
|
return max(0, lerp(float4(1.0, 1.0, 1.0, 1.0), float4(saturate(albedo * extinction), extinction), fade.xxxx));
|
|
}
|
|
else
|
|
{
|
|
extinction *= fade;
|
|
return max(0, float4(saturate(albedo * extinction), extinction));
|
|
}
|
|
}
|
|
|
|
float4 FragmentSceneSelection(VertexToFragment i) : SV_Target0
|
|
{
|
|
float3 voxelCenterWS = CalculateVoxelCenterWS(i);
|
|
float distanceToCenter = length(voxelCenterWS - i.sphereInfo.xyz);
|
|
|
|
if (distanceToCenter > i.sphereInfo.w)
|
|
clip(-1);
|
|
|
|
#if VFX_PASSDEPTH == VFX_PASSDEPTH_SELECTION
|
|
return float4(_ObjectId, _PassValue, 1.0, 1.0);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
float4 FragmentOverdrawDebug(VertexToFragment i) : SV_Target0
|
|
{
|
|
return float4(1, 1, 1, _VBufferRcpSliceCount);
|
|
}
|