您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

226 行
9.3 KiB

//--------------------------------------------------------------------------------------------------
// Definitions
//--------------------------------------------------------------------------------------------------
#pragma kernel VolumetricLightingAllLights VolumetricLighting=VolumetricLightingAllLights LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingClustered VolumetricLighting=VolumetricLightingClustered LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST
#pragma enable_d3d11_debug_symbols
#include "../../../ShaderPass/ShaderPass.cs.hlsl"
#define SHADERPASS SHADERPASS_VOLUMETRIC_LIGHTING
#define GROUP_SIZE_1D 16
#define GROUP_SIZE_2D (GROUP_SIZE_1D * GROUP_SIZE_1D)
//--------------------------------------------------------------------------------------------------
// Included headers
//--------------------------------------------------------------------------------------------------
#include "../../../../Core/ShaderLibrary/Common.hlsl"
#include "../../../../Core/ShaderLibrary/SpaceFillingCurves.hlsl"
#include "../../../../Core/ShaderLibrary/VolumeRendering.hlsl"
#include "../HomogeneousFog.cs.hlsl"
#define UNITY_MATERIAL_LIT // Need to be defined before including Material.hlsl
#include "../../../ShaderVariables.hlsl"
#include "../../../Lighting/Lighting.hlsl" // This includes Material.hlsl
//--------------------------------------------------------------------------------------------------
// Inputs & outputs
//--------------------------------------------------------------------------------------------------
TEXTURE2D(_DepthTexture); // Z-buffer
RW_TEXTURE2D(float4, _CameraColorTexture); // Updated texture
//--------------------------------------------------------------------------------------------------
// Implementation
//--------------------------------------------------------------------------------------------------
struct Ray
{
float3 originWS;
float3 directionWS; // Normalized
float maxLength; // In meters
};
// Computes the in-scattered radiance along the ray.
float3 PerformIntegration(PositionInputs posInput, Ray ray, uint numSteps)
{
float3 scattering = _GlobalFog_Scattering;
float extinction = _GlobalFog_Extinction;
float asymmetry = _GlobalFog_Asymmetry;
LightLoopContext context;
// ZERO_INITIALIZE(LightLoopContext, context);
context.shadowContext = InitShadowContext();
uint featureFlags = 0xFFFFFFFF; // TODO
float maxDepthVS = posInput.depthVS;
// Note: we are already using 'unPositionSS' for randomization of LODDitheringTransition().
float zeta = GenerateHashedRandomFloat(posInput.unPositionSS.yx);
float du = rcp(numSteps);
float u0 = 0.25 * du + 0.5 * du * zeta;
float dt = du * ray.maxLength;
float3 radiance = 0;
for (uint s = 0; s < numSteps; s++)
{
float u = u0 + s * du; // [0, 1]
float t = u * ray.maxLength; // [0, ray.maxLength]
float3 positionWS = ray.originWS + t * ray.directionWS;
float3 sampleRadiance = 0;
if (featureFlags & LIGHTFEATUREFLAGS_DIRECTIONAL)
{
for (uint i = 0; i < _DirectionalLightCount; ++i)
{
// Fetch the light.
DirectionalLightData lightData = _DirectionalLightDatas[i];
float3 L = -lightData.forward; // Lights point backwards in Unity
float intensity = 1;
float3 color = lightData.color;
// Note: we apply the scattering coefficient and the constant part of the phase function later.
intensity *= HenyeyGreensteinPhasePartVarying(asymmetry, dot(L, ray.directionWS));
[branch] if (lightData.shadowIndex >= 0)
{
float shadow = GetDirectionalShadowAttenuation(context.shadowContext, positionWS,
0, lightData.shadowIndex, L, posInput.unPositionSS);
intensity *= shadow;
}
[branch] if (lightData.cookieIndex >= 0)
{
float3 lightToSample = positionWS - lightData.positionWS;
float4 cookie = EvaluateCookie_Directional(context, lightData, lightToSample);
color *= cookie.rgb;
intensity *= cookie.a;
}
// Compute the amount of in-scattered radiance.
sampleRadiance += color * intensity;
}
}
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
{
uint punctualLightCount;
#ifdef LIGHTLOOP_TILE_PASS
uint punctualLightStart;
posInput.depthVS = u * maxDepthVS;
GetCountAndStart(posInput, LIGHTCATEGORY_PUNCTUAL, punctualLightStart, punctualLightCount);
#else
punctualLightCount = _PunctualLightCount;
#endif
for (uint i = 0; i < punctualLightCount; ++i)
{
#ifdef LIGHTLOOP_TILE_PASS
uint punctualLightIndex = FetchIndex(punctualLightStart, i);
#else
uint punctualLightIndex = i;
#endif
// Fetch the light.
LightData lightData = _LightDatas[punctualLightIndex];
int lightType = lightData.lightType;
float3 lightToSample = positionWS - lightData.positionWS;
float distSq = dot(lightToSample, lightToSample);
float dist = sqrt(distSq);
float3 L = lightToSample * -rsqrt(distSq);
float intensity = GetPunctualShapeAttenuation(lightData, L, distSq);
float3 color = lightData.color;
// Note: we apply the scattering coefficient and the constant part of the phase function later.
intensity *= HenyeyGreensteinPhasePartVarying(asymmetry, dot(L, ray.directionWS));
intensity *= Transmittance(OpticalDepthHomogeneous(extinction, dist));
[branch] if (lightData.shadowIndex >= 0)
{
// TODO: make projector lights cast shadows.
float3 offset = 0; // GetShadowPosOffset(nDotL, normal);
float shadow = GetPunctualShadowAttenuation(context.shadowContext, positionWS + offset,
0, lightData.shadowIndex, float4(L, dist), posInput.unPositionSS);
intensity *= lerp(1, shadow, lightData.shadowDimmer);
}
// Projector lights always have a cookies, so we can perform clipping inside the if().
[branch] if (lightData.cookieIndex >= 0)
{
float4 cookie = EvaluateCookie_Punctual(context, lightData, lightToSample);
color *= cookie.rgb;
intensity *= cookie.a;
}
// Compute the amount of in-scattered radiance.
sampleRadiance += color * intensity;
}
}
radiance += sampleRadiance * (Transmittance(OpticalDepthHomogeneous(extinction, t)) * dt);
}
float3 phaseConstant = scattering * HenyeyGreensteinPhasePartConstant(asymmetry);
return radiance * phaseConstant;
}
[numthreads(GROUP_SIZE_2D, 1, 1)]
void VolumetricLighting(uint2 groupId : SV_GroupID,
uint groupThreadId : SV_GroupThreadID)
{
// Note: any factor of 64 is a suitable wave size for our algorithm.
uint waveIndex = groupThreadId / 64;
uint laneIndex = groupThreadId % 64;
uint quadIndex = laneIndex / 4;
// Arrange threads in the Morton order to optimally match the memory layout of GCN tiles.
uint mortonCode = groupThreadId;
uint2 localCoord = DecodeMorton2D(mortonCode);
uint2 tileAnchor = groupId * GROUP_SIZE_1D;
uint2 pixelCoord = tileAnchor + localCoord;
uint2 tileCoord = pixelCoord / GetTileSize();
if (pixelCoord.x >= (uint)_ScreenSize.x || pixelCoord.y >= (uint)_ScreenSize.y) { return; }
// Idea: zenith angle based distance limiting to simulate aerial perspective?
#ifdef UNITY_REVERSED_Z
float z = max(LOAD_TEXTURE2D(_DepthTexture, pixelCoord).r, 0 + 0.001);
#else
float z = min(LOAD_TEXTURE2D(_DepthTexture, pixelCoord).r, 1 - 0.001);
#endif
PositionInputs posInput = GetPositionInput(pixelCoord, _ScreenSize.zw, tileCoord);
UpdatePositionInput(z, _InvViewProjMatrix, _ViewProjMatrix, posInput);
Ray cameraRay;
// Note: the camera ray does not start on the the near (camera sensor) plane.
// While this is not correct (strictly speaking), the introduced error is small.
cameraRay.originWS = GetCurrentViewPosition();
cameraRay.directionWS = posInput.positionWS - cameraRay.originWS;
cameraRay.maxLength = sqrt(dot(cameraRay.directionWS, cameraRay.directionWS));
cameraRay.directionWS *= rsqrt(dot(cameraRay.directionWS, cameraRay.directionWS)); // Normalize
float rayT = Transmittance(OpticalDepthHomogeneous(_GlobalFog_Extinction, cameraRay.maxLength));
const int numSamples = 64;
float3 inL = PerformIntegration(posInput, cameraRay, numSamples);
// In-place UAV updates do not work on Intel GPUs.
_CameraColorTexture[pixelCoord] = float4(rayT * _CameraColorTexture[pixelCoord].rgb + inL, _CameraColorTexture[pixelCoord].a);
}