mirror of
https://github.com/maxartz15/VolumetricLighting.git
synced 2024-11-14 02:35:36 +01:00
224 lines
5.5 KiB
HLSL
224 lines
5.5 KiB
HLSL
// Based on 'Real-Time Polygonal-Light Shading with Linearly Transformed Cosines'
|
|
// https://labs.unity.com/article/real-time-polygonal-light-shading-linearly-transformed-cosines
|
|
|
|
#if AREA_LIGHT_ENABLE_DIFFUSE
|
|
sampler2D _TransformInv_Diffuse;
|
|
#endif
|
|
sampler2D _TransformInv_Specular;
|
|
sampler2D _AmpDiffAmpSpecFresnel;
|
|
float4x4 _LightVerts;
|
|
|
|
half IntegrateEdge(half3 v1, half3 v2)
|
|
{
|
|
half d = dot(v1,v2);
|
|
half theta = acos(max(-0.9999, dot(v1,v2)));
|
|
half theta_sintheta = theta / sin(theta);
|
|
return theta_sintheta * (v1.x*v2.y - v1.y*v2.x);
|
|
}
|
|
|
|
// Baum's equation
|
|
// Expects non-normalized vertex positions
|
|
half PolygonRadiance(half4x3 L)
|
|
{
|
|
// detect clipping config
|
|
uint config = 0;
|
|
if (L[0].z > 0) config += 1;
|
|
if (L[1].z > 0) config += 2;
|
|
if (L[2].z > 0) config += 4;
|
|
if (L[3].z > 0) config += 8;
|
|
|
|
|
|
// The fifth vertex for cases when clipping cuts off one corner.
|
|
// Due to a compiler bug, copying L into a vector array with 5 rows
|
|
// messes something up, so we need to stick with the matrix + the L4 vertex.
|
|
half3 L4 = L[3];
|
|
|
|
// This switch is surprisingly fast. Tried replacing it with a lookup array of vertices.
|
|
// Even though that replaced the switch with just some indexing and no branches, it became
|
|
// way, way slower - mem fetch stalls?
|
|
|
|
// clip
|
|
uint n = 0;
|
|
switch(config)
|
|
{
|
|
case 0: // clip all
|
|
break;
|
|
|
|
case 1: // V1 clip V2 V3 V4
|
|
n = 3;
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
L[2] = -L[3].z * L[0] + L[0].z * L[3];
|
|
break;
|
|
|
|
case 2: // V2 clip V1 V3 V4
|
|
n = 3;
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
break;
|
|
|
|
case 3: // V1 V2 clip V3 V4
|
|
n = 4;
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
L[3] = -L[3].z * L[0] + L[0].z * L[3];
|
|
break;
|
|
|
|
case 4: // V3 clip V1 V2 V4
|
|
n = 3;
|
|
L[0] = -L[3].z * L[2] + L[2].z * L[3];
|
|
L[1] = -L[1].z * L[2] + L[2].z * L[1];
|
|
break;
|
|
|
|
case 5: // V1 V3 clip V2 V4: impossible
|
|
break;
|
|
|
|
case 6: // V2 V3 clip V1 V4
|
|
n = 4;
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
L[3] = -L[3].z * L[2] + L[2].z * L[3];
|
|
break;
|
|
|
|
case 7: // V1 V2 V3 clip V4
|
|
n = 5;
|
|
L4 = -L[3].z * L[0] + L[0].z * L[3];
|
|
L[3] = -L[3].z * L[2] + L[2].z * L[3];
|
|
break;
|
|
|
|
case 8: // V4 clip V1 V2 V3
|
|
n = 3;
|
|
L[0] = -L[0].z * L[3] + L[3].z * L[0];
|
|
L[1] = -L[2].z * L[3] + L[3].z * L[2];
|
|
L[2] = L[3];
|
|
break;
|
|
|
|
case 9: // V1 V4 clip V2 V3
|
|
n = 4;
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
L[2] = -L[2].z * L[3] + L[3].z * L[2];
|
|
break;
|
|
|
|
case 10: // V2 V4 clip V1 V3: impossible
|
|
break;
|
|
|
|
case 11: // V1 V2 V4 clip V3
|
|
n = 5;
|
|
L[3] = -L[2].z * L[3] + L[3].z * L[2];
|
|
L[2] = -L[2].z * L[1] + L[1].z * L[2];
|
|
break;
|
|
|
|
case 12: // V3 V4 clip V1 V2
|
|
n = 4;
|
|
L[1] = -L[1].z * L[2] + L[2].z * L[1];
|
|
L[0] = -L[0].z * L[3] + L[3].z * L[0];
|
|
break;
|
|
|
|
case 13: // V1 V3 V4 clip V2
|
|
n = 5;
|
|
L[3] = L[2];
|
|
L[2] = -L[1].z * L[2] + L[2].z * L[1];
|
|
L[1] = -L[1].z * L[0] + L[0].z * L[1];
|
|
break;
|
|
|
|
case 14: // V2 V3 V4 clip V1
|
|
n = 5;
|
|
L4 = -L[0].z * L[3] + L[3].z * L[0];
|
|
L[0] = -L[0].z * L[1] + L[1].z * L[0];
|
|
break;
|
|
|
|
case 15: // V1 V2 V3 V4
|
|
n = 4;
|
|
break;
|
|
}
|
|
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
// normalize
|
|
L[0] = normalize(L[0]);
|
|
L[1] = normalize(L[1]);
|
|
L[2] = normalize(L[2]);
|
|
if(n == 3)
|
|
L[3] = L[0];
|
|
else
|
|
{
|
|
L[3] = normalize(L[3]);
|
|
if (n == 4)
|
|
L4 = L[0];
|
|
else
|
|
L4 = normalize(L4);
|
|
}
|
|
|
|
// integrate
|
|
half sum = 0;
|
|
sum += IntegrateEdge(L[0], L[1]);
|
|
sum += IntegrateEdge(L[1], L[2]);
|
|
sum += IntegrateEdge(L[2], L[3]);
|
|
if(n >= 4)
|
|
sum += IntegrateEdge(L[3], L4);
|
|
if(n == 5)
|
|
sum += IntegrateEdge(L4, L[0]);
|
|
|
|
sum *= 0.15915; // 1/2pi
|
|
|
|
return max(0, sum);
|
|
}
|
|
|
|
half TransformedPolygonRadiance(half4x3 L, half2 uv, sampler2D transformInv, half amplitude)
|
|
{
|
|
// Get the inverse LTC matrix M
|
|
half3x3 Minv = 0;
|
|
Minv._m22 = 1;
|
|
Minv._m00_m02_m11_m20 = tex2D(transformInv, uv);
|
|
|
|
// Transform light vertices into diffuse configuration
|
|
half4x3 LTransformed = mul(L, Minv);
|
|
|
|
// Polygon radiance in transformed configuration - specular
|
|
return PolygonRadiance(LTransformed) * amplitude;
|
|
}
|
|
|
|
half3 CalculateLight (half3 position, half3 diffColor, half3 specColor, half oneMinusRoughness, half3 N,
|
|
half3 lightPos, half3 lightColor)
|
|
{
|
|
#if AREA_LIGHT_SHADOWS
|
|
half shadow = Shadow(position);
|
|
if (shadow == 0.0)
|
|
return 0;
|
|
#endif
|
|
// TODO: larger and smaller values cause artifacts - why?
|
|
oneMinusRoughness = clamp(oneMinusRoughness, 0.01, 0.93);
|
|
half roughness = 1 - oneMinusRoughness;
|
|
half3 V = normalize(_WorldSpaceCameraPos - position);
|
|
|
|
// Construct orthonormal basis around N, aligned with V
|
|
half3x3 basis;
|
|
basis[0] = normalize(V - N * dot(V, N));
|
|
basis[1] = normalize(cross(N, basis[0]));
|
|
basis[2] = N;
|
|
|
|
// Transform light vertices into that space
|
|
half4x3 L;
|
|
L = _LightVerts - half4x3(position, position, position, position);
|
|
L = mul(L, transpose(basis));
|
|
|
|
// UVs for sampling the LUTs
|
|
half theta = acos(dot(V, N));
|
|
half2 uv = half2(roughness, theta/1.57);
|
|
|
|
half3 AmpDiffAmpSpecFresnel = tex2D(_AmpDiffAmpSpecFresnel, uv).rgb;
|
|
|
|
half3 result = 0;
|
|
#if AREA_LIGHT_ENABLE_DIFFUSE
|
|
half diffuseTerm = TransformedPolygonRadiance(L, uv, _TransformInv_Diffuse, AmpDiffAmpSpecFresnel.x);
|
|
result = diffuseTerm * diffColor;
|
|
#endif
|
|
|
|
half specularTerm = TransformedPolygonRadiance(L, uv, _TransformInv_Specular, AmpDiffAmpSpecFresnel.y);
|
|
half fresnelTerm = specColor + (1.0 - specColor) * AmpDiffAmpSpecFresnel.z;
|
|
result += specularTerm * fresnelTerm * UNITY_PI;
|
|
|
|
#if AREA_LIGHT_SHADOWS
|
|
result *= shadow;
|
|
#endif
|
|
|
|
return result * lightColor;
|
|
} |