浏览代码

TODO: should we divide by ray segment length?

/Yibing-Project-2
Evgenii Golubev 7 年前
当前提交
43d373d4
共有 9 个文件被更改,包括 233 次插入100 次删除
  1. 2
      ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl
  2. 75
      ScriptableRenderPipeline/Core/ShaderLibrary/VolumeRendering.hlsl
  3. 2
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/Deferred.shader
  4. 2
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/Deferred.compute
  5. 28
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl
  6. 218
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/Resources/VolumetricLighting.compute
  7. 2
      ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/VolumetricLighting.cs
  8. 2
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl
  9. 2
      ScriptableRenderPipeline/HDRenderPipeline/Sky/AtmosphericScattering/AtmosphericScattering.hlsl

2
ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl


#define HALF_PI 1.57079632679
#define INV_HALF_PI 0.636619772367
#define INFINITY asfloat(0x7F800000)
#define FLT_SMALL 0.0001
#define FLT_SMALL 1e-8
#define LOG2_E 1.44269504089
#define FLT_EPSILON 1.192092896e-07 // Smallest positive number, such that 1.0 + FLT_EPSILON != 1.0

75
ScriptableRenderPipeline/Core/ShaderLibrary/VolumeRendering.hlsl


// Optical_Depth(x, y) = Integral{x, y}{Extinction(t) dt}
// Transmittance(x, y) = Exp(-Optical_Depth(x, y))
// Transmittance(x, z) = Transmittance(x, y) * Transmittance(y, z)
// Integral{a, b}{Transmittance(0, x) dx} = Transmittance(0, a) * Integral{0, b - a}{Transmittance(a, a + x) dx}
// Integral{a, b}{Transmittance(0, t) * Li(t) dt} = Transmittance(0, a) * Integral{a, b}{Transmittance(0, t - a) * Li(t) dt}.
float OpticalDepthHomogeneous(float extinction, float intervalLength)
float OpticalDepthHomogeneousMedia(float extinction, float intervalLength)
float3 OpticalDepthHomogeneous(float3 extinction, float intervalLength)
float3 OpticalDepthHomogeneousMedia(float3 extinction, float intervalLength)
{
return extinction * intervalLength;
}

return exp(-opticalDepth);
}
// Integral{0, b - a}{Transmittance(a, a + x) dx}.
float TransmittanceIntegralHomogeneous(float extinction, float intervalLength)
float TransmittanceHomogeneousMedia(float extinction, float intervalLength)
{
return Transmittance(OpticalDepthHomogeneousMedia(extinction, intervalLength));
}
float3 TransmittanceHomogeneousMedia(float3 extinction, float intervalLength)
{
return Transmittance(OpticalDepthHomogeneousMedia(extinction, intervalLength));
}
// Integral{a, b}{Transmittance(0, t - a) dt}.
float TransmittanceIntegralHomogeneousMedia(float extinction, float intervalLength)
// Integral{0, b - a}{Transmittance(a, a + x) dx}.
float3 TransmittanceIntegralHomogeneous(float3 extinction, float intervalLength)
// Integral{a, b}{Transmittance(0, t - a) dt}.
float3 TransmittanceIntegralHomogeneousMedia(float3 extinction, float intervalLength)
{
return rcp(extinction) - rcp(extinction) * exp(-extinction * intervalLength);
}

HenyeyGreensteinPhasePartVarying(asymmetry, LdotD);
}
// Samples the interval of homogeneous participating media using the closed-form tracking approach
// (proportionally to the transmittance times the extinction coefficient).
// Returns the offset from the start of the interval and the weight = (extinction * transmittance / pdf).
// Ref: Production Volume Rendering, 3.6.1.
void ImportanceSampleHomogeneousMedia(float extinction, float intervalLength, float rndVal,
out float offset, out float weight)
{
// pdf = extinction * exp(-extinction * t) / (1 - exp(-intervalLength * extinction))
// weight = extinction * exp(-extinction * t) / pdf
// weight = 1 - exp(-intervalLength * extinction)
weight = 1 - exp(-extinction * intervalLength);
offset = log(1 - rndVal * weight) / extinction;
}
// Implements equiangular light sampling.
// Returns the distance from 0 and the reciprocal of the PDF.
// Ref: Importance Sampling of Area Lights in Participating Media.
void ImportanceSamplePunctualLight(float3 lightPosition, float3 rayOrigin, float3 rayDirection,
float tMin, float tMax, float rndVal,
out float dist, out float rcpPdf)
{
float3 originToLight = lightPosition - rayOrigin;
float originToLightProj = dot(originToLight, rayDirection);
float originToLightDistSq = dot(originToLight, originToLight);
float rayToLightDistSq = max(originToLightDistSq - originToLightProj * originToLightProj, FLT_SMALL);
float rayToLightDist = sqrt(rayToLightDistSq);
float a = tMin - originToLightProj;
float b = tMax - originToLightProj;
float d = rayToLightDist;
// TODO: optimize me. :-(
float theta0 = atan(a / d);
float theta1 = atan(b / d);
float theta = lerp(theta0, theta1, rndVal);
float t = d * tan(theta);
dist = originToLightProj + t;
rcpPdf = (theta1 - theta0) * (d + t * tan(theta));
}
return -log(transmittanceColor + 0.00001) / max(atDistance, 0.000001);
return -log(transmittanceColor + FLT_SMALL) / max(atDistance, FLT_SMALL);
}
#ifndef USE_LEGACY_UNITY_SHADER_VARIABLES

// TODO: share this...
#define PRESET_ULTRA 0
#if PRESET_ULTRA
#ifdef PRESET_ULTRA
// E.g. for 1080p: (1920/4)x(1080/4)x(256) = 33,177,600 voxels
#define VBUFFER_TILE_SIZE 4
#define VBUFFER_SLICE_COUNT 256

#define VBUFFER_SLICE_COUNT 128
#endif
#endif // PRESET_ULTRA
float4 GetInScatteredRadianceAndTransmittance(float2 positionSS, float depthVS,
TEXTURE3D(VBufferLighting), SAMPLER3D(linearClampSampler),

2
ScriptableRenderPipeline/HDRenderPipeline/Lighting/Deferred.shader


_VBufferResolutionAndScale.zw,
_VBufferDepthEncodingParams);
// TODO: apply volumetrics after SSS.
// diffuseLighting *= volumetricLighting.a;
diffuseLighting *= volumetricLighting.a;
specularLighting *= volumetricLighting.a;
specularLighting += volumetricLighting.rgb;
#endif

2
ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/Deferred.compute


_VBufferResolutionAndScale.zw,
_VBufferDepthEncodingParams);
// TODO: apply volumetrics after SSS.
// diffuseLighting *= volumetricLighting.a;
diffuseLighting *= volumetricLighting.a;
specularLighting *= volumetricLighting.a;
specularLighting += volumetricLighting.rgb;
#endif

28
ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePassLoop.hlsl


return TILE_SIZE_CLUSTERED;
}
void GetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
float GetLightClusterMinDepthVS(uint2 tileIndex, uint clusterIndex)
uint2 tileIndex = posInput.unTileCoord;
float logBase = g_fClustBase;
if (g_isLogBaseBufferEnabled)
{
logBase = g_logBaseBuffer[tileIndex.y * _NumTileClusteredX + tileIndex.x];
}
return ClusterIdxToZFlex(clusterIndex, logBase, g_isLogBaseBufferEnabled != 0);
}
uint GetLightClusterIndex(uint2 tileIndex, float depthVS)
{
float logBase = g_fClustBase;
if (g_isLogBaseBufferEnabled)
{

int clustIdx = SnapToClusterIdxFlex(posInput.depthVS, logBase, g_isLogBaseBufferEnabled != 0);
return SnapToClusterIdxFlex(depthVS, logBase, g_isLogBaseBufferEnabled != 0);
}
void GetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCategory, out uint start, out uint lightCount)
{
const int idx = ((lightCategory * nrClusters + clustIdx) * _NumTileClusteredY + tileIndex.y) * _NumTileClusteredX + tileIndex.x;
const int idx = ((lightCategory * nrClusters + clusterIndex) * _NumTileClusteredY + tileIndex.y) * _NumTileClusteredX + tileIndex.x;
}
void GetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
{
uint2 tileIndex = posInput.unTileCoord;
uint clusterIndex = GetLightClusterIndex(tileIndex, posInput.depthVS);
GetCountAndStartCluster(tileIndex, clusterIndex, lightCategory, start, lightCount);
}
void GetCountAndStart(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)

218
ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/Resources/VolumetricLighting.compute


// Definitions
//--------------------------------------------------------------------------------------------------
#define PRESET_ULTRA 0
#if PRESET_ULTRA
#ifdef PRESET_ULTRA
// E.g. for 1080p: (1920/4)x(1080/4)x(256) = 33,177,600 voxels
#define VBUFFER_TILE_SIZE 4
#define VBUFFER_SLICE_COUNT 256

#define VBUFFER_SLICE_COUNT 128
#endif
#endif // PRESET_ULTRA
#pragma kernel VolumetricLightingAllLights VolumetricLighting=VolumetricLightingAllLights LIGHTLOOP_SINGLE_PASS
#pragma kernel VolumetricLightingClustered VolumetricLighting=VolumetricLightingClustered LIGHTLOOP_TILE_PASS USE_CLUSTERED_LIGHTLIST

float ratioZtoLen; // ViewSpaceZ
};
float3 GetPointAtDistance(Ray ray, float t)
{
return ray.originWS + t * ray.directionWS;
}
void FillVolumetricLightingBuffer(Ray cameraRay, uint2 voxelCoord, uint2 tileCoord)
void FillVolumetricLightingBuffer(Ray ray, uint2 voxelCoord, uint2 tileCoord)
{
LightLoopContext context;
// ZERO_INITIALIZE(LightLoopContext, context);

float4 depthParams = _VBufferDepthEncodingParams;
float z0 = depthParams.x; // View space Z coordinate of the near plane
float t0 = cameraRay.ratioLenToZ * z0; // Distance to the near plane
float t0 = z0 * ray.ratioLenToZ; // Distance to the near plane
float de = rcp(VBUFFER_SLICE_COUNT); // Log-encoded distance between slices
float3 totalRadiance = 0;

#ifdef LIGHTLOOP_TILE_PASS
// Our voxel is not necessarily completely inside a single light cluster.
uint clusterIndices[2];
float clusterDistances[2];
// TODO: the clustered code could be made faster & simpler.
clusterIndices[0] = GetLightClusterIndex(tileCoord, z0);
clusterDistances[0] = GetLightClusterMinDepthVS(tileCoord, clusterIndices[0]) * ray.ratioLenToZ;
#endif // LIGHTLOOP_TILE_PASS
for (uint s = 0; s < sliceCountHack; s++)
for (uint slice = 0; slice < sliceCountHack; slice++)
float e1 = s * de + de; // (s + 1) / sliceCount
float e1 = slice * de + de; // (slice + 1) / sliceCount
float t1 = cameraRay.ratioLenToZ * z1;
float t1 = ray.ratioLenToZ * z1;
// TODO: low-discrepancy point set.
float t = t0 + 0.5 * dt;
float z = cameraRay.ratioZtoLen * t;
float3 positionWS = cameraRay.originWS + t * cameraRay.directionWS;
// Compute the position of the center of the voxel.
// We will use it for participating media sampling and reprojection.
float tc = t0 + 0.5 * dt;
float3 centerWS = GetPointAtDistance(ray, tc);
// Get volume properties at 't'.
// Sample the participating media at 'tc' (or 'centerWS').
// We consider it to be constant along the interval [t0, t1] (within the voxel).
// TODO: use a low-discrepancy point set.
float rndVal = 0.5;
float tOffset, weight;
ImportanceSampleHomogeneousMedia(extinction, dt, rndVal, tOffset, weight);
float t = t0 + tOffset;
float3 positionWS = GetPointAtDistance(ray, t);
for (uint i = 0; i < _DirectionalLightCount; ++i)
{
// Fetch the light.

float intensity = 1;
float intensity = weight;
float3 color = lightData.color;
[branch] if (lightData.shadowIndex >= 0)

intensity *= shadow;
}
// Note: no global fog attenuation along shadow rays for directional lights.
// Note: no fog attenuation along shadow rays for directional lights.
[branch] if (lightData.cookieIndex >= 0)
{

}
}
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
#ifdef LIGHTLOOP_TILE_PASS
// TODO: the clustered code could be made faster & simpler.
clusterIndices[1] = GetLightClusterIndex(tileCoord, z1);
clusterDistances[1] = GetLightClusterMinDepthVS(tileCoord, clusterIndices[1]) * ray.ratioLenToZ;
// Loop over 1 or 2 light clusters.
for (int cluster = 0; cluster < 2; cluster++)
uint punctualLightCount;
float tMin = max(t0, clusterDistances[cluster]);
float tMax = t1;
#ifdef LIGHTLOOP_TILE_PASS
PositionInputs posInput;
posInput.depthVS = z;
posInput.unTileCoord = tileCoord;
// TODO: do not refetch between consecutive samples if possible.
uint punctualLightStart;
GetCountAndStartCluster(posInput, LIGHTCATEGORY_PUNCTUAL, punctualLightStart, punctualLightCount);
#else
punctualLightCount = _PunctualLightCount;
#endif
if (cluster == 0 && clusterDistances[0] != clusterDistances[1])
{
tMax = min(t1, clusterDistances[1]);
}
#else
float tMin = t0;
float tMax = t1;
#endif // LIGHTLOOP_TILE_PASS
for (uint i = 0; i < punctualLightCount; ++i)
if (featureFlags & LIGHTFEATUREFLAGS_PUNCTUAL)
uint punctualLightCount;
uint punctualLightIndex = FetchIndex(punctualLightStart, i);
uint punctualLightStart;
GetCountAndStartCluster(tileCoord, clusterIndices[cluster], LIGHTCATEGORY_PUNCTUAL,
punctualLightStart, punctualLightCount);
uint punctualLightIndex = i;
#endif
punctualLightCount = _PunctualLightCount;
#endif // LIGHTLOOP_TILE_PASS
// Fetch the light.
LightData lightData = _LightDatas[punctualLightIndex];
int lightType = lightData.lightType;
for (uint i = 0; i < punctualLightCount; ++i)
{
#ifdef LIGHTLOOP_TILE_PASS
uint punctualLightIndex = FetchIndex(punctualLightStart, i);
#else
uint punctualLightIndex = i;
#endif // LIGHTLOOP_TILE_PASS
float3 lightToSample = positionWS - lightData.positionWS;
float distSq = dot(lightToSample, lightToSample);
float dist = sqrt(distSq);
float3 L = lightToSample * -rsqrt(distSq);
float clampedDistSq = max(distSq, 0.5 * dt); // Hack to reduce undersampling
float intensity = GetPunctualShapeAttenuation(lightData, L, clampedDistSq);
float3 color = lightData.color;
// Fetch the light.
LightData lightData = _LightDatas[punctualLightIndex];
int lightType = lightData.lightType;
// TODO...
if (lightType != GPULIGHTTYPE_POINT) continue;
float t, rcpPdf;
ImportanceSamplePunctualLight(lightData.positionWS, ray.originWS, ray.directionWS,
tMin, tMax, rndVal, t, rcpPdf);
float3 positionWS = GetPointAtDistance(ray, t);
// TODO: we could compute this data in ImportanceSamplePunctualLight().
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;
// TODO: heterogeneous media.
intensity *= TransmittanceHomogeneousMedia(extinction, dist);
[branch] if (lightData.shadowIndex >= 0)
{
// TODO: make projector lights cast shadows.
float shadow = GetPunctualShadowAttenuation(context.shadowContext, positionWS,
float3(0, 0, 0), lightData.shadowIndex, float4(L, dist));
intensity *= Transmittance(OpticalDepthHomogeneous(extinction, dist));
intensity *= lerp(1, shadow, lightData.shadowDimmer);
}
[branch] if (lightData.shadowIndex >= 0)
{
// TODO: make projector lights cast shadows.
float shadow = GetPunctualShadowAttenuation(context.shadowContext, positionWS,
float3(0, 0, 0), lightData.shadowIndex, float4(L, dist));
// Projector lights always have 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;
}
intensity *= lerp(1, shadow, lightData.shadowDimmer);
}
// Compute transmittance from 't0' to 't'.
float transmittance = TransmittanceHomogeneousMedia(extinction, t - t0);
// Projector lights always have cookies, so we can perform clipping inside the if().
[branch] if (lightData.cookieIndex >= 0)
{
float4 cookie = EvaluateCookie_Punctual(context, lightData, lightToSample);
intensity *= transmittance * rcpPdf;
color *= scattering;
color *= cookie.rgb;
intensity *= cookie.a;
// Compute the amount of in-scattered radiance.
sampleRadiance += color * intensity;
// Compute the amount of in-scattered radiance.
sampleRadiance += color * intensity;
#ifdef LIGHTLOOP_TILE_PASS
// The voxel is completely inside the light cluster.
if (clusterIndices[0] == clusterIndices[1]) break;
#endif // LIGHTLOOP_TILE_PASS
// We consider the volume to be homogeneous along the interval (within the voxel).
// Integral{a, b}{Transmittance(0, x) dx} = Transmittance(0, a) * Integral{0, b - a}{Transmittance(a, a + x) dx}.
float transmittance = Transmittance(opticalDepth) * TransmittanceIntegralHomogeneous(extinction, dt);
// Compute the transmittance up to the start of the interval.
float transmittance = Transmittance(opticalDepth);
// Compute the optical depth up to the sample.
opticalDepth += extinction * (t - t0);
// Integral{a, b}{Transmittance(0, t) * Li(t) dt} = Transmittance(0, a) * Integral{a, b}{Transmittance(0, t - a) * Li(t) dt}.
// We multiply by the scattering coefficient per light.
totalRadiance += (transmittance * IsotropicPhaseFunction()) * sampleRadiance;
// We consider the lighting (and the volume properties) to be constant along the interval (within the voxel).
totalRadiance += (transmittance * IsotropicPhaseFunction()) * scattering * sampleRadiance;
// Compute the optical depth up to the center of the interval.
opticalDepth += 0.5 * extinction * dt;
// Store the voxel data. TODO: reprojection using camera motion vectors.
_VBufferLighting[uint3(voxelCoord, s)] = float4(totalRadiance, opticalDepth);
// Store the voxel data. TODO: reprojection of 'tc' (or 'centerWS').
_VBufferLighting[uint3(voxelCoord, slice)] = float4(totalRadiance, opticalDepth);
opticalDepth += extinction * (t1 - t);
opticalDepth += 0.5 * extinction * dt;
#ifdef LIGHTLOOP_TILE_PASS
clusterIndices[0] = clusterIndices[1];
clusterDistances[0] = clusterDistances[1];
#endif // LIGHTLOOP_TILE_PASS
}
}

// TODO: use a low-discrepancy point set.
float2 sampleCoord = voxelCoord + 0.5;
// Compute the unnormalized ray direction s.t. its ViewSpaceZ = 1.
// Compute the ray direction s.t. its ViewSpaceZ = 1.
Ray cameraRay;
cameraRay.originWS = GetCurrentViewPosition();
cameraRay.ratioLenToZ = sqrt(dot(dir, dir));
cameraRay.ratioZtoLen = rsqrt(dot(dir, dir));
cameraRay.directionWS = dir * cameraRay.ratioZtoLen;
Ray ray;
ray.originWS = GetCurrentViewPosition();
ray.ratioLenToZ = sqrt(dot(dir, dir));
ray.ratioZtoLen = rsqrt(dot(dir, dir));
ray.directionWS = dir * ray.ratioZtoLen;
FillVolumetricLightingBuffer(cameraRay, voxelCoord, tileCoord);
FillVolumetricLightingBuffer(ray, voxelCoord, tileCoord);
}

2
ScriptableRenderPipeline/HDRenderPipeline/Lighting/Volumetrics/VolumetricLighting.cs


ComputeShader m_VolumetricLightingCS { get { return m_Asset.renderPipelineResources.volumetricLightingCS; } }
float m_VBufferNearPlane = 0.5f; // Distance in meters
float m_VBufferFarPlane = 32.0f; // Distance in meters
float m_VBufferFarPlane = 64.0f; // Distance in meters
RenderTexture[] m_VBufferLighting = null; // Used for even / odd frames
RenderTargetIdentifier[] m_VBufferLightingRT = null;

2
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl


}
#ifdef VOLUMETRIC_LIGHTING_ENABLED
shadow *= Transmittance(OpticalDepthHomogeneous(preLightData.globalFogExtinction, dist));
shadow *= Transmittance(OpticalDepthHomogeneousMedia(preLightData.globalFogExtinction, dist));
#endif
illuminance *= shadow;

2
ScriptableRenderPipeline/HDRenderPipeline/Sky/AtmosphericScattering/AtmosphericScattering.hlsl


if (_AtmosphericScatteringType == FOGTYPE_EXPONENTIAL)
{
float3 fogColor = GetFogColor(posInput);
float fogFactor = _ExpFogDensity * (1.0f - Transmittance(OpticalDepthHomogeneous(1.0f / _ExpFogDistance, posInput.depthVS)));
float fogFactor = _ExpFogDensity * (1.0f - Transmittance(OpticalDepthHomogeneousMedia(1.0f / _ExpFogDistance, posInput.depthVS)));
return float4(fogColor, fogFactor);
}
else if (_AtmosphericScatteringType == FOGTYPE_LINEAR)

正在加载...
取消
保存