201 lines
7.0 KiB
HLSL

// Altered UnityGI calculations for higher quality light probe sampling.
#ifndef SCSS_UNITYGI_INCLUDED
#define SCSS_UNITYGI_INCLUDED
/* http://www.geomerics.com/wp-content/uploads/2015/08/CEDEC_Geomerics_ReconstructingDiffuseLighting1.pdf */
float shEvaluateDiffuseL1Geomerics_local(float L0, float3 L1, float3 n)
{
// average energy
// Add max0 to fix an issue caused by probes having a negative ambient component (???)
// I'm not sure how normal that is but this can't handle it
float R0 = max(L0, 0);
// avg direction of incoming light
float3 R1 = 0.5f * L1;
// directional brightness
float lenR1 = length(R1);
// linear angle between normal and direction 0-1
float q = dot(normalize(R1), n) * 0.5 + 0.5;
q = saturate(q); // Thanks to ScruffyRuffles for the bug identity.
// power for q
// lerps from 1 (linear) to 3 (cubic) based on directionality
float p = 1.0f + 2.0f * lenR1 / R0;
// dynamic range constant
// should vary between 4 (highly directional) and 0 (ambient)
float a = (1.0f - lenR1 / R0) / (1.0f + lenR1 / R0);
return R0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p));
}
// SH Convolution Functions
// https://github.com/lukis101/VRCUnityStuffs/tree/master/SH
// Code adapted from https://blog.selfshadow.com/2012/01/07/righting-wrap-part-2/
///////////////////////////
float3 GeneralWrapSH(float fA) // original unoptimized
{
// Normalization factor for our model.
float norm = 0.5 * (2 + fA) / (1 + fA);
float4 t = float4(2 * (fA + 1), fA + 2, fA + 3, fA + 4);
return norm * float3(t.x / t.y, 2 * t.x / (t.y * t.z),
t.x * (fA * fA - t.x + 5) / (t.y * t.z * t.w));
}
float3 GeneralWrapSHOpt(float fA)
{
const float4 t0 = float4(-0.047771, -0.129310, 0.214438, 0.279310);
const float4 t1 = float4( 1.000000, 0.666667, 0.250000, 0.000000);
float3 r;
r.xyz = saturate(t0.xxy * fA + t0.yzw);
r.xyz = -r * fA + t1.xyz;
return r;
}
float3 GreenWrapSHOpt(float fW)
{
const float4 t0 = float4(0.0, 1.0 / 4.0, -1.0 / 3.0, -1.0 / 2.0);
const float4 t1 = float4(1.0, 2.0 / 3.0, 1.0 / 4.0, 0.0);
float3 r;
r.xyz = t0.xxy * fW + t0.xzw;
r.xyz = r.xyz * fW + t1.xyz;
return r;
}
float3 ShadeSH9_wrapped(float3 normal, float3 conv)
{
float3 x0, x1, x2;
conv *= float3(1, 1.5, 4); // Undo pre-applied cosine convolution
//conv *= _Bands.xyz; // debugging
// Constant (L0)
// Band 0 has constant part from 6th kernel (band 1) pre-applied, but ignore for performance
x0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
// Linear (L1) polynomial terms
x1.r = (dot(unity_SHAr.xyz, normal));
x1.g = (dot(unity_SHAg.xyz, normal));
x1.b = (dot(unity_SHAb.xyz, normal));
// 4 of the quadratic (L2) polynomials
float4 vB = normal.xyzz * normal.yzzx;
x2.r = dot(unity_SHBr, vB);
x2.g = dot(unity_SHBg, vB);
x2.b = dot(unity_SHBb, vB);
// Final (5th) quadratic (L2) polynomial
float vC = normal.x * normal.x - normal.y * normal.y;
x2 += unity_SHC.rgb * vC;
return x0 * conv.x + x1 * conv.y + x2 * conv.z;
}
float3 ShadeSH9_wrappedCorrect(float3 normal, float3 conv)
{
const float3 cosconv_inv = float3(1, 1.5, 4); // Inverse of the pre-applied cosine convolution
float3 x0, x1, x2;
conv *= cosconv_inv; // Undo pre-applied cosine convolution
//conv *= _Bands.xyz; // debugging
// Constant (L0)
x0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
// Remove the constant part from L2 and add it back with correct convolution
float3 otherband = float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z) / 3.0;
x0 = (x0 + otherband) * conv.x - otherband * conv.z;
// Linear (L1) polynomial terms
x1.r = (dot(unity_SHAr.xyz, normal));
x1.g = (dot(unity_SHAg.xyz, normal));
x1.b = (dot(unity_SHAb.xyz, normal));
// 4 of the quadratic (L2) polynomials
float4 vB = normal.xyzz * normal.yzzx;
x2.r = dot(unity_SHBr, vB);
x2.g = dot(unity_SHBg, vB);
x2.b = dot(unity_SHBb, vB);
// Final (5th) quadratic (L2) polynomial
float vC = normal.x * normal.x - normal.y * normal.y;
x2 += unity_SHC.rgb * vC;
return x0 + x1 * conv.y + x2 * conv.z;
}
bool isReflectionProbeActive()
{
#ifndef SHADER_TARGET_SURFACE_ANALYSIS // Required to use GetDimensions
float height, width;
unity_SpecCube0.GetDimensions(width, height);
return !(height * width < 32);
#endif
return 1;
}
inline UnityGI UnityGlobalIllumination_SCSS (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn)
{
UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld);
#if defined(SAMPLE_SH_NONLINEAR)
float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w)
+ float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z) / 3.0;
float3 nonLinearSH = float3(0,0,0);
nonLinearSH.r = shEvaluateDiffuseL1Geomerics_local(L0.r, unity_SHAr.xyz, normalWorld);
nonLinearSH.g = shEvaluateDiffuseL1Geomerics_local(L0.g, unity_SHAg.xyz, normalWorld);
nonLinearSH.b = shEvaluateDiffuseL1Geomerics_local(L0.b, unity_SHAb.xyz, normalWorld);
nonLinearSH = max(nonLinearSH, 0);
o_gi.indirect.diffuse += nonLinearSH * occlusion;
#endif
o_gi.indirect.specular = isReflectionProbeActive()
? UnityGI_IndirectSpecular(data, occlusion, glossIn)
: o_gi.indirect.diffuse;
return o_gi;
}
UnityGI GetUnityGI(float3 lightColor, float3 lightDirection, float3 normalDirection,float3 viewDirection,
float3 viewReflectDirection, float attenuation, float occlusion, float roughness, float3 worldPos){
UnityLight light;
light.color = lightColor;
light.dir = lightDirection;
light.ndotl = max(0.0h,dot( normalDirection, lightDirection));
UnityGIInput d = (UnityGIInput) 0;
d.light = light;
d.worldPos = worldPos;
d.worldViewDir = viewDirection;
d.atten = attenuation;
d.ambient = 0.0h;
d.boxMax[0] = unity_SpecCube0_BoxMax;
d.boxMin[0] = unity_SpecCube0_BoxMin;
d.probePosition[0] = unity_SpecCube0_ProbePosition;
d.probeHDR[0] = unity_SpecCube0_HDR;
d.boxMax[1] = unity_SpecCube1_BoxMax;
d.boxMin[1] = unity_SpecCube1_BoxMin;
d.probePosition[1] = unity_SpecCube1_ProbePosition;
d.probeHDR[1] = unity_SpecCube1_HDR;
Unity_GlossyEnvironmentData ugls_en_data;
ugls_en_data.roughness = roughness;
ugls_en_data.reflUVW = viewReflectDirection;
UnityGI gi = UnityGlobalIllumination_SCSS(d, occlusion, normalDirection, ugls_en_data );
return gi;
}
half3 BetterSH9 (half4 normal) {
float3 indirect;
float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w)
+ float3(unity_SHBr.z, unity_SHBg.z, unity_SHBb.z) / 3.0;
indirect.r = shEvaluateDiffuseL1Geomerics_local(L0.r, unity_SHAr.xyz, normal);
indirect.g = shEvaluateDiffuseL1Geomerics_local(L0.g, unity_SHAg.xyz, normal);
indirect.b = shEvaluateDiffuseL1Geomerics_local(L0.b, unity_SHAb.xyz, normal);
indirect = max(0, indirect);
indirect += SHEvalLinearL2(normal);
return indirect;
}
float3 BetterSH9(float3 normal)
{
return BetterSH9(float4(normal, 1));
}
#endif // SCSS_UNITYGI_INCLUDED