浏览代码

Add basic light probe support to volumetric lighting

/main
Evgenii Golubev 7 年前
当前提交
4ab61ec7
共有 5 个文件被更改,包括 145 次插入15 次删除
  1. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs
  2. 97
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/Resources/VolumetricLighting.compute
  3. 12
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VBuffer.hlsl
  4. 47
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs
  5. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderVariables.hlsl

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/HDStringConstants.cs


public static readonly int _Source4 = Shader.PropertyToID("_Source4");
public static readonly int _Result1 = Shader.PropertyToID("_Result1");
public static readonly int _AmbientProbe = Shader.PropertyToID("_AmbientProbe");
public static readonly int _AmbientProbeCoeffs = Shader.PropertyToID("_AmbientProbeCoeffs");
public static readonly int _GlobalFog_Extinction = Shader.PropertyToID("_GlobalFog_Extinction");
public static readonly int _GlobalFog_Scattering = Shader.PropertyToID("_GlobalFog_Scattering");
public static readonly int _GlobalFog_Asymmetry = Shader.PropertyToID("_GlobalFog_Asymmetry");

97
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/Resources/VolumetricLighting.compute


float3 radianceNoPhase;
};
struct ZonalHarmonics
{
float coeffs[3]; // 3 bands
};
struct SphericalHarmonics
{
float coeffs[9]; // 3 bands
};
// RGB SphericalHarmonics
struct LightProbe
{
float3 coeffs[9]; // 3 bands
};
LightProbe GetAmbientProbe()
{
LightProbe probe;
for (int i = 0; i < 9; i++)
{
probe.coeffs[i] = _AmbientProbeCoeffs[i].rgb;
}
return probe;
}
// i = l * (l + 1) + m
// Ref: "Stupid Spherical Harmonics Tricks", p. 28-29.
float EvaluateSphericalHarmonics(int i, float3 d)
{
switch (i)
{
case 0: return rsqrt(4 * PI);
case 1: return -sqrt(0.75 / PI) * d.y;
case 2: return sqrt(0.75 / PI) * d.z;
case 3: return -sqrt(0.75 / PI) * d.x;
case 4: return sqrt(3.75 / PI) * d.x * d.y;
case 5: return -sqrt(3.75 / PI) * d.y * d.z;
case 6: return sqrt(0.3125 / PI) * (3 * Sq(d.z) - 1);
case 7: return -sqrt(3.75 / PI) * d.x * d.z;
case 8: return sqrt(0.9375 / PI) * (Sq(d.x) - Sq(d.y));
default: return -FLT_INF; // Unreachable
}
}
// Ref: "Stupid Spherical Harmonics Tricks", p. 7.
SphericalHarmonics RotateZonalHarmonics(ZonalHarmonics zh, float3 d)
{
SphericalHarmonics sh;
for (int l = 0; l <= 2; l++)
{
float n = sqrt((4 * PI) / (2 * l + 1));
float c = zh.coeffs[l];
float p = n * c;
for (int m = -l; m <= l; m++)
{
int i = l * (l + 1) + m;
sh.coeffs[i] = p * EvaluateSphericalHarmonics(i, d);
}
}
return sh;
}
float3 EvaluateLightProbe(LightProbe probe, float asymmetry, float3 d)
{
float g = asymmetry;
// Represent the Henyey-Greenstein function using the Zonal Harmonics.
ZonalHarmonics zhPhase;
zhPhase.coeffs[0] = 0.5 * sqrt(1 / PI);
zhPhase.coeffs[1] = 0.5 * sqrt(3 / PI) * g;
zhPhase.coeffs[2] = 0.5 * sqrt(5 / PI) * g * g;
// Rotate the coefficients (z -> d).
SphericalHarmonics shPhase = RotateZonalHarmonics(zhPhase, d);
// Evaluate the integral.
float3 radiance = 0;
for (int i = 0; i < 9; i++)
{
radiance += probe.coeffs[i] * shPhase.coeffs[i];
}
return max(0, radiance);
}
// Computes the light integral (in-scattered radiance) within the voxel.
// Multiplication by the scattering coefficient and the phase function is performed outside.
VoxelLighting EvaluateVoxelLighting(LightLoopContext context, uint featureFlags, PositionInputs posInput, float3 centerWS,

#endif
// Sample the light probe.
float3 probeRadiance = _AmbientProbe.rgb * (4 * PI) * TransmittanceIntegralHomogeneousMedium(extinction, dt);
float3 probeRadiance = EvaluateLightProbe(GetAmbientProbe(), asymmetry, ray.centerDirWS) * TransmittanceIntegralHomogeneousMedium(extinction, dt);
totalRadiance += (transmittance * phase) * scattering * (blendedRadiance + probeRadiance);
totalRadiance += transmittance * scattering * (phase * blendedRadiance + probeRadiance);
// Compute the optical depth up to the center of the interval.
opticalDepth += 0.5 * extinction * dt;

12
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VBuffer.hlsl


[branch] if (clampToEdge || isInBounds)
{
#if 0
// We could ignore non-linearity at the cost of accuracy.
// TODO: visually test this option (especially in motion).
#if 1
// Ignore non-linearity (for performance reasons) at the cost of accuracy.
// The results are exact for a stationary camera, but can potentially cause some judder in motion.
float w = d;
#else
// Adjust the texture coordinate for HW trilinear sampling.

float z = linearDepth;
float d = EncodeLogarithmicDepthGeneralized(z, VBufferDepthEncodingParams);
#if 0
// Ignore non-linearity (for performance reasons) at the cost of accuracy.
// The results are exact for a stationary camera, but can potentially cause some judder in motion.
float w = d;
#else
#endif
float2 weights[2], offsets[2];
BiquadraticFilter(1 - fc, weights, offsets); // Inverse-translate the filter centered around 0.5

47
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs


return depthParams;
}
Vector4 GetAmbientLightProbe()
// Undoes coefficient normalization to obtain the canonical values of SH.
SphericalHarmonicsL2 DenormalizeSH(SphericalHarmonicsL2 sh)
{
float sqrtPi = Mathf.Sqrt(Mathf.PI);
float c0 = 1.0f / (2.0f * sqrtPi);
float c1 = Mathf.Sqrt( 3.0f) / ( 3.0f * sqrtPi);
float c2 = Mathf.Sqrt(15.0f) / ( 8.0f * sqrtPi);
float c3 = Mathf.Sqrt( 5.0f) / (16.0f * sqrtPi);
float c4 = 0.5f * c2;
// Compute the inverse of SphericalHarmonicsL2::kNormalizationConstants.
// See SetSHEMapConstants() in "Stupid Spherical Harmonics Tricks". Note that we do not multiply by 3 here.
float[] invNormConsts = { 1.0f / c0, -1.0f / c1, 1.0f / c1, -1.0f / c1, 1.0f / c2, -1.0f / c2, 1.0f / c3, -1.0f / c2, 1.0f / c4 };
for (int i = 0; i < 9; i++)
{
for (int c = 0; c < 3; c++)
{
sh[c, i] *= invNormConsts[i];
}
}
return sh;
}
void SetAmbientLightProbe(CommandBuffer cmd)
Vector4 probe = new Vector4();
SphericalHarmonicsL2 probeSH = DenormalizeSH(RenderSettings.ambientProbe);
SphericalHarmonicsL2 probeSH = RenderSettings.ambientProbe;
probe.x = probeSH[0, 0];
probe.y = probeSH[1, 0];
probe.z = probeSH[2, 0];
Vector4[] coeffs = new Vector4[9];
for (int i = 0; i < 9; i++)
{
coeffs[i].x = probeSH[0, i]; // R
coeffs[i].y = probeSH[1, i]; // G
coeffs[i].z = probeSH[2, i]; // B
coeffs[i].w = 0; // Unused
}
return probe;
cmd.SetGlobalVectorArray(HDShaderIDs._AmbientProbeCoeffs, coeffs);
}
public void PushGlobalParams(HDCamera camera, CommandBuffer cmd)

VBuffer vBuffer = FindVBuffer(camera.GetViewID());
Debug.Assert(vBuffer != null);
SetAmbientLightProbe(cmd);
cmd.SetGlobalVector( HDShaderIDs._AmbientProbe, GetAmbientLightProbe());
}
// Ref: https://en.wikipedia.org/wiki/Close-packing_of_equal_spheres

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/ShaderVariables.hlsl


float2 _TaaFrameRotation; // {x = sin(_TaaFrameIndex * PI/2), y = cos(_TaaFrameIndex * PI/2), z = unused}
uint _TaaFrameIndex; // [0, 7]
// Volumetric lighting.
float4 _AmbientProbe;
float4 _AmbientProbeCoeffs[9]; // 3 bands of SH. TODO: alpha is unused, pack better?
float _GlobalFog_Asymmetry;
float3 _GlobalFog_Scattering;
float _GlobalFog_Extinction;

正在加载...
取消
保存