浏览代码

Factor out the SH code

/main
Evgenii Golubev 7 年前
当前提交
7552aebb
共有 3 个文件被更改,包括 161 次插入138 次删除
  1. 142
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/Volumetrics/VolumetricLighting.cs
  2. 146
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/SphericalHarmonics.cs
  3. 11
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/SphericalHarmonics.cs.meta

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


return depthParams;
}
public struct ZonalHarmonicsL2
{
public float[] coeffs;
}
public static ZonalHarmonicsL2 GetHenyeyGreensteinPhaseFunction(float asymmetry)
{
float g = asymmetry;
var zh = new ZonalHarmonicsL2();
zh.coeffs = new float[3];
zh.coeffs[0] = 0.5f * Mathf.Sqrt(1.0f / Mathf.PI);
zh.coeffs[1] = 0.5f * Mathf.Sqrt(3.0f / Mathf.PI) * g;
zh.coeffs[2] = 0.5f * Mathf.Sqrt(5.0f / Mathf.PI) * g * g;
return zh;
}
public static ZonalHarmonicsL2 GetCornetteShanksPhaseFunction(float asymmetry)
{
float g = asymmetry;
var zh = new ZonalHarmonicsL2();
zh.coeffs = new float[3];
zh.coeffs[0] = 0.282095f;
zh.coeffs[1] = 0.293162f * g * (4.0f + (g * g)) / (2.0f + (g * g));
zh.coeffs[2] = (0.126157f + 1.44179f * (g * g) + 0.324403f * (g * g) * (g * g)) / (2.0f + (g * g));
return zh;
}
// Ref: "Stupid Spherical Harmonics Tricks", p. 6.
public static SphericalHarmonicsL2 Convolve(SphericalHarmonicsL2 sh, ZonalHarmonicsL2 zh)
{
for (int l = 0; l <= 2; l++)
{
float n = Mathf.Sqrt((4.0f * Mathf.PI) / (2 * l + 1));
float k = zh.coeffs[l];
float p = n * k;
for (int m = -l; m <= l; m++)
{
int i = l * (l + 1) + m;
for (int c = 0; c < 3; c++)
{
sh[c, i] *= p;
}
}
}
return sh;
}
// Undoes coefficient rescaling due to the convolution with the clamped cosine kernel
// to obtain the canonical values of SH.
public static SphericalHarmonicsL2 UndoCosineRescaling(SphericalHarmonicsL2 sh)
{
const float c0 = 0.28209479177387814347f; // 1/2 * sqrt(1/Pi)
const float c1 = 0.32573500793527994772f; // 1/3 * sqrt(3/Pi)
const float c2 = 0.27313710764801976764f; // 1/8 * sqrt(15/Pi)
const float c3 = 0.07884789131313000151f; // 1/16 * sqrt(5/Pi)
const float c4 = 0.13656855382400988382f; // 1/16 * sqrt(15/Pi)
// Compute the inverse of SphericalHarmonicsL2::kNormalizationConstants.
// See SetSHEMapConstants() in "Stupid Spherical Harmonics Tricks".
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 c = 0; c < 3; c++)
{
for (int i = 0; i < 9; i++)
{
sh[c, i] *= invNormConsts[i];
}
}
return sh;
}
// Premultiplies the SH with the polynomial coefficients of SH basis functions,
// which avoids using any constants during SH evaluation.
// The resulting evaluation takes the form:
// (c_0 - c_6) + c_1 y + c_2 z + c_3 x + c_4 x y + c_5 y z + c_6 (3 z^2) + c_7 x z + c_8 (x^2 - y^2)
public static SphericalHarmonicsL2 PremultiplyCoefficients(SphericalHarmonicsL2 sh)
{
const float k0 = 0.28209479177387814347f; // {0, 0} : 1/2 * sqrt(1/Pi)
const float k1 = 0.48860251190291992159f; // {1, 0} : 1/2 * sqrt(3/Pi)
const float k2 = 1.09254843059207907054f; // {2,-2} : 1/2 * sqrt(15/Pi)
const float k3 = 0.31539156525252000603f; // {2, 0} : 1/4 * sqrt(5/Pi)
const float k4 = 0.54627421529603953527f; // {2, 2} : 1/4 * sqrt(15/Pi)
float[] ks = { k0, -k1, k1, -k1, k2, -k2, k3, -k2, k4 };
for (int c = 0; c < 3; c++)
{
for (int i = 0; i < 9; i++)
{
sh[c, i] *= ks[i];
}
}
return sh;
}
SphericalHarmonicsL2 probeSH = UndoCosineRescaling(RenderSettings.ambientProbe);
ZonalHarmonicsL2 phaseZH = GetCornetteShanksPhaseFunction(asymmetry);
SphericalHarmonicsL2 finalSH = PremultiplyCoefficients(Convolve(probeSH, phaseZH));
// Pack coefficients so that we can use Peter-Pike Sloan's shader code.
// See SetSHEMapConstants() in "Stupid Spherical Harmonics Tricks".
Vector4[] coeffs = new Vector4[7];
SphericalHarmonicsL2 probeSH = SphericalHarmonicMath.UndoCosineRescaling(RenderSettings.ambientProbe);
ZonalHarmonicsL2 phaseZH = ZonalHarmonicsL2.GetCornetteShanksPhaseFunction(asymmetry);
SphericalHarmonicsL2 finalSH = SphericalHarmonicMath.PremultiplyCoefficients(SphericalHarmonicMath.Convolve(probeSH, phaseZH));
// Constant + linear
for (int c = 0; c < 3; c++)
{
coeffs[c].x = finalSH[c, 3];
coeffs[c].y = finalSH[c, 1];
coeffs[c].z = finalSH[c, 2];
coeffs[c].w = finalSH[c, 0] - finalSH[c, 6];
}
// Quadratic (4/5)
for (int c = 0; c < 3; c++)
{
coeffs[3 + c].x = finalSH[c, 4];
coeffs[3 + c].y = finalSH[c, 5];
coeffs[3 + c].z = finalSH[c, 6] * 3.0f;
coeffs[3 + c].w = finalSH[c, 7];
}
// Quadratic (5)
coeffs[6].x = finalSH[0, 8];
coeffs[6].y = finalSH[1, 8];
coeffs[6].z = finalSH[2, 8];
coeffs[6].w = 1.0f;
cmd.SetGlobalVectorArray(HDShaderIDs._AmbientProbeCoeffs, coeffs);
cmd.SetGlobalVectorArray(HDShaderIDs._AmbientProbeCoeffs, SphericalHarmonicMath.PackCoefficients(finalSH));
}
float CornetteShanksPhasePartConstant(float asymmetry)

146
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/SphericalHarmonics.cs


using System;
using UnityEngine;
using UnityEngine.Rendering;
public struct ZonalHarmonicsL2
{
public float[] coeffs; // Must have the size of 3
public static ZonalHarmonicsL2 GetHenyeyGreensteinPhaseFunction(float asymmetry)
{
float g = asymmetry;
var zh = new ZonalHarmonicsL2();
zh.coeffs = new float[3];
zh.coeffs[0] = 0.5f * Mathf.Sqrt(1.0f / Mathf.PI);
zh.coeffs[1] = 0.5f * Mathf.Sqrt(3.0f / Mathf.PI) * g;
zh.coeffs[2] = 0.5f * Mathf.Sqrt(5.0f / Mathf.PI) * g * g;
return zh;
}
public static ZonalHarmonicsL2 GetCornetteShanksPhaseFunction(float asymmetry)
{
float g = asymmetry;
var zh = new ZonalHarmonicsL2();
zh.coeffs = new float[3];
zh.coeffs[0] = 0.282095f;
zh.coeffs[1] = 0.293162f * g * (4.0f + (g * g)) / (2.0f + (g * g));
zh.coeffs[2] = (0.126157f + 1.44179f * (g * g) + 0.324403f * (g * g) * (g * g)) / (2.0f + (g * g));
return zh;
}
}
public class SphericalHarmonicMath
{
// Ref: "Stupid Spherical Harmonics Tricks", p. 6.
public static SphericalHarmonicsL2 Convolve(SphericalHarmonicsL2 sh, ZonalHarmonicsL2 zh)
{
for (int l = 0; l <= 2; l++)
{
float n = Mathf.Sqrt((4.0f * Mathf.PI) / (2 * l + 1));
float k = zh.coeffs[l];
float p = n * k;
for (int m = -l; m <= l; m++)
{
int i = l * (l + 1) + m;
for (int c = 0; c < 3; c++)
{
sh[c, i] *= p;
}
}
}
return sh;
}
// Undoes coefficient rescaling due to the convolution with the clamped cosine kernel
// to obtain the canonical values of SH.
public static SphericalHarmonicsL2 UndoCosineRescaling(SphericalHarmonicsL2 sh)
{
const float c0 = 0.28209479177387814347f; // 1/2 * sqrt(1/Pi)
const float c1 = 0.32573500793527994772f; // 1/3 * sqrt(3/Pi)
const float c2 = 0.27313710764801976764f; // 1/8 * sqrt(15/Pi)
const float c3 = 0.07884789131313000151f; // 1/16 * sqrt(5/Pi)
const float c4 = 0.13656855382400988382f; // 1/16 * sqrt(15/Pi)
// Compute the inverse of SphericalHarmonicsL2::kNormalizationConstants.
// See SetSHEMapConstants() in "Stupid Spherical Harmonics Tricks".
float[] invNormConsts = { 1/c0, -1/c1, 1/c1, -1/c1, 1/c2, -1/c2, 1/c3, -1/c2, 1/c4 };
for (int c = 0; c < 3; c++)
{
for (int i = 0; i < 9; i++)
{
sh[c, i] *= invNormConsts[i];
}
}
return sh;
}
// Premultiplies the SH with the polynomial coefficients of SH basis functions,
// which avoids using any constants during SH evaluation.
// The resulting evaluation takes the form:
// (c_0 - c_6) + c_1 y + c_2 z + c_3 x + c_4 x y + c_5 y z + c_6 (3 z^2) + c_7 x z + c_8 (x^2 - y^2)
public static SphericalHarmonicsL2 PremultiplyCoefficients(SphericalHarmonicsL2 sh)
{
const float k0 = 0.28209479177387814347f; // {0, 0} : 1/2 * sqrt(1/Pi)
const float k1 = 0.48860251190291992159f; // {1, 0} : 1/2 * sqrt(3/Pi)
const float k2 = 1.09254843059207907054f; // {2,-2} : 1/2 * sqrt(15/Pi)
const float k3 = 0.31539156525252000603f; // {2, 0} : 1/4 * sqrt(5/Pi)
const float k4 = 0.54627421529603953527f; // {2, 2} : 1/4 * sqrt(15/Pi)
float[] ks = { k0, -k1, k1, -k1, k2, -k2, k3, -k2, k4 };
for (int c = 0; c < 3; c++)
{
for (int i = 0; i < 9; i++)
{
sh[c, i] *= ks[i];
}
}
return sh;
}
// Packs coefficients so that we can use Peter-Pike Sloan's shader code.
// Does not perform premultiplication with coefficients of SH basis functions.
// See SetSHEMapConstants() in "Stupid Spherical Harmonics Tricks".
public static Vector4[] PackCoefficients(SphericalHarmonicsL2 sh)
{
Vector4[] coeffs = new Vector4[7];
// Constant + linear
for (int c = 0; c < 3; c++)
{
coeffs[c].x = sh[c, 3];
coeffs[c].y = sh[c, 1];
coeffs[c].z = sh[c, 2];
coeffs[c].w = sh[c, 0] - sh[c, 6];
}
// Quadratic (4/5)
for (int c = 0; c < 3; c++)
{
coeffs[3 + c].x = sh[c, 4];
coeffs[3 + c].y = sh[c, 5];
coeffs[3 + c].z = sh[c, 6] * 3.0f;
coeffs[3 + c].w = sh[c, 7];
}
// Quadratic (5)
coeffs[6].x = sh[0, 8];
coeffs[6].y = sh[1, 8];
coeffs[6].z = sh[2, 8];
coeffs[6].w = 1.0f;
return coeffs;
}
}

11
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Lighting/SphericalHarmonics.cs.meta


fileFormatVersion: 2
guid: 0ae2a6cdcae867941b675151d66a41d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存