Skip to content

Commit 91c8b14

Browse files
Merge pull request #17 from EvgeniiG/master
Add the initial impl. of LTC line light Lambertian diffuse shading
2 parents 732a8d0 + 35018bc commit 91c8b14

File tree

4 files changed

+141
-121
lines changed

4 files changed

+141
-121
lines changed

Assets/ScriptableRenderLoop/HDRenderLoop/Material/Lit/Lit.hlsl

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,8 @@ void IntegrateBSDFLineRef(float3 V, float3 positionWS, PreLightData preLightData
814814
specularLighting = float3(0.0, 0.0, 0.0);
815815

816816
const float len = lightData.size.x;
817-
const float3 p0 = lightData.positionWS - lightData.right * (0.5 * len);
818817
const float3 dir = lightData.right;
818+
const float3 p1 = lightData.positionWS - lightData.right * (0.5 * len);
819819
const float dt = len * rcp(sampleCount);
820820
const float off = 0.5 * dt;
821821

@@ -826,7 +826,7 @@ void IntegrateBSDFLineRef(float3 V, float3 positionWS, PreLightData preLightData
826826
{
827827
// Place the sample in the middle of the interval.
828828
float t = off + i * dt;
829-
float3 sPos = p0 + t * dir;
829+
float3 sPos = p1 + t * dir;
830830
float3 unL = sPos - positionWS;
831831
float dist2 = dot(unL, unL);
832832
float3 L = normalize(unL);
@@ -849,7 +849,79 @@ void IntegrateBSDFLineRef(float3 V, float3 positionWS, PreLightData preLightData
849849
}
850850

851851
//-----------------------------------------------------------------------------
852-
// EvaluateBSDF_Area
852+
// EvaluateBSDF_Line | Approximation with Linearly Transformed Cosines
853+
//-----------------------------------------------------------------------------
854+
855+
void EvaluateBSDF_Line( LightLoopContext lightLoopContext,
856+
float3 V, float3 positionWS, PreLightData preLightData,
857+
LightData lightData, BSDFData bsdfData,
858+
out float3 diffuseLighting, out float3 specularLighting)
859+
{
860+
diffuseLighting = float3(0.0, 0.0, 0.0);
861+
specularLighting = float3(0.0, 0.0, 0.0);
862+
863+
float len = lightData.size.x;
864+
float3 dir = lightData.right;
865+
866+
// TODO: precompute half-length. Same as for LTC area lights.
867+
// In fact, why not store both endpoints? Saves us 7 cycles.
868+
float3 p1 = lightData.positionWS - lightData.right * (0.5 * len);
869+
float3 p2 = lightData.positionWS + lightData.right * (0.5 * len);
870+
871+
// Translate both points s.t. the shaded point is at the origin of the coordinate system.
872+
p1 -= positionWS;
873+
p2 -= positionWS;
874+
875+
// Construct an orthonormal basis (local coordinate system) around N.
876+
// TODO: it could be stored in PreLightData. All LTC lights compute it more than once!
877+
float3x3 basis;
878+
basis[0] = normalize(V - bsdfData.normalWS * preLightData.NdotV);
879+
basis[1] = normalize(cross(bsdfData.normalWS, basis[0]));
880+
basis[2] = bsdfData.normalWS;
881+
882+
// Transform (rotate) both endpoints into the local coordinate system (left-handed).
883+
p1 = mul(p1, transpose(basis));
884+
p2 = mul(p2, transpose(basis));
885+
886+
// Terminate the algorithm if both points are below the horizon.
887+
if (p1.z <= 0.0 && p2.z <= 0.0) return;
888+
889+
if (p2.z <= 0.0)
890+
{
891+
// Convention: 'p2' is above the horizon.
892+
swap(p1, p2);
893+
dir = -dir;
894+
}
895+
896+
// Clip the part of the light below the horizon.
897+
if (p1.z <= 0.0)
898+
{
899+
// p = p1 + t * dir; p.z == 0.
900+
float t = -p1.z / dir.z;
901+
p1 = float3(p1.xy + t * dir.xy, 0.0);
902+
903+
// Set the length of the visible part of the light.
904+
len -= t;
905+
}
906+
907+
// Compute the direction to the point on the line orthogonal to 'dir'.
908+
// Its length is the shortest distance to the line.
909+
float3 p0 = p1 - dot(p1, dir) * dir;
910+
float dist = length(p0);
911+
912+
// Compute the parameterization: distances from 'l1' and 'l2' to 'l0'.
913+
float l1 = dot(p1 - p0, dir);
914+
float l2 = l1 + len;
915+
916+
// Integrate the clamped cosine over the line segment.
917+
float irradiance = LineIrradiance(l1, l2, dist, p0.z, dir.z);
918+
919+
// Only Lambertian for now. TODO: Disney Diffuse and GGX.
920+
diffuseLighting = (lightData.diffuseScale * irradiance * INV_PI) * bsdfData.diffuseColor * lightData.color;
921+
}
922+
923+
//-----------------------------------------------------------------------------
924+
// EvaluateBSDF_Area | Approximation with Linearly Transformed Cosines
853925
//-----------------------------------------------------------------------------
854926

855927
void EvaluateBSDF_Area( LightLoopContext lightLoopContext,
@@ -867,10 +939,17 @@ void EvaluateBSDF_Area( LightLoopContext lightLoopContext,
867939
IntegrateGGXAreaRef(V, positionWS, preLightData, lightData, bsdfData, diffuseLighting, specularLighting);
868940
}
869941
#else
942+
if (lightData.lightType == GPULIGHTTYPE_LINE)
943+
{
944+
EvaluateBSDF_Line(lightLoopContext, V, positionWS, preLightData, lightData, bsdfData, diffuseLighting, specularLighting);
945+
return;
946+
}
947+
870948
// TODO: This could be precomputed
871949
float halfWidth = lightData.size.x * 0.5;
872950
float halfHeight = lightData.size.y * 0.5;
873951

952+
// TODO: store 4 points and save 24 cycles.
874953
float3 p0 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight;
875954
float3 p1 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * -halfHeight;
876955
float3 p2 = lightData.positionWS + lightData.right * halfWidth + lightData.up * -halfHeight;
@@ -929,9 +1008,11 @@ void EvaluateBSDF_Area( LightLoopContext lightLoopContext,
9291008
0.0, 1.0, 0.0,
9301009
0.0, 0.0, 1.0};
9311010

932-
ltcValue = LTCEvaluate(V, bsdfData.normalWS, identity3x3, L, lightData.twoSided);
1011+
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1012+
identity3x3);
9331013
#else
934-
ltcValue = LTCEvaluate(V, bsdfData.normalWS, preLightData.ltcXformDisneyDiffuse, L, lightData.twoSided);
1014+
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1015+
preLightData.ltcXformDisneyDiffuse);
9351016
#endif
9361017

9371018
if (ltcValue == 0.0)
@@ -953,7 +1034,8 @@ void EvaluateBSDF_Area( LightLoopContext lightLoopContext,
9531034
float3 fresnelTerm = bsdfData.fresnel0 * preLightData.ltcGGXFresnelMagnitudeDiff
9541035
+ (float3)preLightData.ltcGGXFresnelMagnitude;
9551036

956-
ltcValue = LTCEvaluate(V, bsdfData.normalWS, preLightData.ltcXformGGX, L, lightData.twoSided);
1037+
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided,
1038+
preLightData.ltcXformGGX);
9571039
ltcValue *= lightData.specularScale;
9581040
specularLighting = fresnelTerm * lightData.color * ltcValue;
9591041
}

Assets/ScriptableRenderLoop/ShaderLibrary/AreaLighting.hlsl

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,12 @@ float PolygonRadiance(float4x3 L, bool twoSided)
163163
return twoSided ? abs(sum) : max(sum, 0.0);
164164
}
165165

166-
float LTCEvaluate(float3 V, float3 N, float3x3 minV, float4x3 L, bool twoSided)
166+
float LTCEvaluate(float4x3 L, float3 V, float3 N, float NdotV, bool twoSided, float3x3 minV)
167167
{
168168
// Construct local orthonormal basis around N, aligned with N
169+
// TODO: it could be stored in PreLightData. All LTC lights compute it more than once!
169170
float3x3 basis;
170-
basis[0] = normalize(V - N * dot(V, N));
171+
basis[0] = normalize(V - N * NdotV);
171172
basis[1] = normalize(cross(N, basis[0]));
172173
basis[2] = N;
173174

@@ -179,4 +180,31 @@ float LTCEvaluate(float3 V, float3 N, float3x3 minV, float4x3 L, bool twoSided)
179180
return PolygonRadiance(L, twoSided);
180181
}
181182

183+
float LineFpo(float rcpD, float rcpDL, float l)
184+
{
185+
// Compute: l / d / (d * d + l * l) + 1.0 / (d * d) * atan(l / d).
186+
return l * rcpDL + rcpD * rcpD * atan(l * rcpD);
187+
}
188+
189+
float LineFwt(float sqL, float rcpDL)
190+
{
191+
// Compute: l * l / d / (d * d + l * l).
192+
return sqL * rcpDL;
193+
}
194+
195+
// Computes the integral of the clamped cosine over the line segment.
196+
// 'dist' is the shortest distance to the line. 'l1' and 'l2' define the integration interval.
197+
float LineIrradiance(float l1, float l2, float dist, float pointZ, float tangentZ)
198+
{
199+
float sqD = dist * dist;
200+
float sqL1 = l1 * l1;
201+
float sqL2 = l2 * l2;
202+
float rcpD = rcp(dist);
203+
float rcpDL1 = rcpD * rcp(sqD + sqL1);
204+
float rcpDL2 = rcpD * rcp(sqD + sqL2);
205+
float intP0 = LineFpo(rcpD, rcpDL2, l2) - LineFpo(rcpD, rcpDL1, l1);
206+
float intWt = LineFwt(sqL2, rcpDL2) - LineFwt(sqL1, rcpDL1);
207+
return intP0 * pointZ + intWt * tangentZ;
208+
}
209+
182210
#endif // UNITY_AREA_LIGHTING_INCLUDED

Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,26 @@ float4 Max3(float4 a, float4 b, float4 c)
128128
}
129129
#endif // INTRINSIC_MINMAX3
130130

131+
void swap(inout float a, inout float b)
132+
{
133+
float t = a; a = b; b = t;
134+
}
135+
136+
void swap(inout float2 a, inout float2 b)
137+
{
138+
float2 t = a; a = b; b = t;
139+
}
140+
141+
void swap(inout float3 a, inout float3 b)
142+
{
143+
float3 t = a; a = b; b = t;
144+
}
145+
146+
void swap(inout float4 a, inout float4 b)
147+
{
148+
float4 t = a; a = b; b = t;
149+
}
150+
131151
#ifndef INTRINSIC_CUBEMAP_FACE_ID
132152
// TODO: implement this. Is the reference implementation of cubemapID provide by AMD the reverse of our ?
133153
/*

Assets/TestScenes/HDTest/HDRenderLoopTest.unity

Lines changed: 3 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,10 +2736,10 @@ MonoBehaviour:
27362736
shadowDimmer: 1
27372737
affectDiffuse: 1
27382738
affectSpecular: 1
2739-
archetype: 0
2739+
archetype: 2
27402740
isDoubleSided: 1
27412741
areaLightLength: 16
2742-
areaLightWidth: 2
2742+
areaLightWidth: 0
27432743
--- !u!108 &710116622
27442744
Light:
27452745
m_ObjectHideFlags: 0
@@ -2888,8 +2888,6 @@ GameObject:
28882888
- component: {fileID: 740109900}
28892889
- component: {fileID: 740109899}
28902890
- component: {fileID: 740109898}
2891-
- component: {fileID: 740109897}
2892-
- component: {fileID: 740109896}
28932891
m_Layer: 0
28942892
m_Name: LightQuad
28952893
m_TagString: Untagged
@@ -2910,59 +2908,6 @@ Transform:
29102908
m_Father: {fileID: 710116623}
29112909
m_RootOrder: 0
29122910
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
2913-
--- !u!114 &740109896
2914-
MonoBehaviour:
2915-
m_ObjectHideFlags: 0
2916-
m_PrefabParentObject: {fileID: 0}
2917-
m_PrefabInternal: {fileID: 0}
2918-
m_GameObject: {fileID: 740109894}
2919-
m_Enabled: 1
2920-
m_EditorHideFlags: 0
2921-
m_Script: {fileID: 11500000, guid: 7a68c43fe1f2a47cfa234b5eeaa98012, type: 3}
2922-
m_Name:
2923-
m_EditorClassIdentifier:
2924-
shadowResolution: 512
2925-
shadowDimmer: 1
2926-
affectDiffuse: 1
2927-
affectSpecular: 1
2928-
archetype: 2
2929-
isDoubleSided: 1
2930-
areaLightLength: 16
2931-
areaLightWidth: 2
2932-
--- !u!108 &740109897
2933-
Light:
2934-
m_ObjectHideFlags: 0
2935-
m_PrefabParentObject: {fileID: 0}
2936-
m_PrefabInternal: {fileID: 0}
2937-
m_GameObject: {fileID: 740109894}
2938-
m_Enabled: 1
2939-
serializedVersion: 7
2940-
m_Type: 2
2941-
m_Color: {r: 1, g: 1, b: 1, a: 1}
2942-
m_Intensity: 1
2943-
m_Range: 10
2944-
m_SpotAngle: 30
2945-
m_CookieSize: 10
2946-
m_Shadows:
2947-
m_Type: 0
2948-
m_Resolution: -1
2949-
m_CustomResolution: -1
2950-
m_Strength: 1
2951-
m_Bias: 0.05
2952-
m_NormalBias: 0.4
2953-
m_NearPlane: 0.2
2954-
m_Cookie: {fileID: 0}
2955-
m_DrawHalo: 0
2956-
m_Flare: {fileID: 0}
2957-
m_RenderMode: 0
2958-
m_CullingMask:
2959-
serializedVersion: 2
2960-
m_Bits: 4294967295
2961-
m_Lightmapping: 4
2962-
m_AreaSize: {x: 1, y: 1}
2963-
m_BounceIntensity: 1
2964-
m_ShadowRadius: 0
2965-
m_ShadowAngle: 0
29662911
--- !u!23 &740109898
29672912
MeshRenderer:
29682913
m_ObjectHideFlags: 0
@@ -3574,7 +3519,7 @@ MonoBehaviour:
35743519
shadowDimmer: 1
35753520
affectDiffuse: 1
35763521
affectSpecular: 1
3577-
archetype: 0
3522+
archetype: 1
35783523
isDoubleSided: 1
35793524
areaLightLength: 16
35803525
areaLightWidth: 2
@@ -3791,8 +3736,6 @@ GameObject:
37913736
- component: {fileID: 925222277}
37923737
- component: {fileID: 925222276}
37933738
- component: {fileID: 925222275}
3794-
- component: {fileID: 925222279}
3795-
- component: {fileID: 925222278}
37963739
m_Layer: 0
37973740
m_Name: LightQuad
37983741
m_TagString: Untagged
@@ -3866,59 +3809,6 @@ MeshFilter:
38663809
m_PrefabInternal: {fileID: 0}
38673810
m_GameObject: {fileID: 925222273}
38683811
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
3869-
--- !u!114 &925222278
3870-
MonoBehaviour:
3871-
m_ObjectHideFlags: 0
3872-
m_PrefabParentObject: {fileID: 0}
3873-
m_PrefabInternal: {fileID: 0}
3874-
m_GameObject: {fileID: 925222273}
3875-
m_Enabled: 1
3876-
m_EditorHideFlags: 0
3877-
m_Script: {fileID: 11500000, guid: 7a68c43fe1f2a47cfa234b5eeaa98012, type: 3}
3878-
m_Name:
3879-
m_EditorClassIdentifier:
3880-
shadowResolution: 512
3881-
shadowDimmer: 1
3882-
affectDiffuse: 1
3883-
affectSpecular: 1
3884-
archetype: 1
3885-
isDoubleSided: 1
3886-
areaLightLength: 16
3887-
areaLightWidth: 2
3888-
--- !u!108 &925222279
3889-
Light:
3890-
m_ObjectHideFlags: 0
3891-
m_PrefabParentObject: {fileID: 0}
3892-
m_PrefabInternal: {fileID: 0}
3893-
m_GameObject: {fileID: 925222273}
3894-
m_Enabled: 1
3895-
serializedVersion: 7
3896-
m_Type: 2
3897-
m_Color: {r: 1, g: 1, b: 1, a: 1}
3898-
m_Intensity: 1
3899-
m_Range: 10
3900-
m_SpotAngle: 30
3901-
m_CookieSize: 10
3902-
m_Shadows:
3903-
m_Type: 0
3904-
m_Resolution: -1
3905-
m_CustomResolution: -1
3906-
m_Strength: 1
3907-
m_Bias: 0.05
3908-
m_NormalBias: 0.4
3909-
m_NearPlane: 0.2
3910-
m_Cookie: {fileID: 0}
3911-
m_DrawHalo: 0
3912-
m_Flare: {fileID: 0}
3913-
m_RenderMode: 0
3914-
m_CullingMask:
3915-
serializedVersion: 2
3916-
m_Bits: 4294967295
3917-
m_Lightmapping: 4
3918-
m_AreaSize: {x: 1, y: 1}
3919-
m_BounceIntensity: 1
3920-
m_ShadowRadius: 0
3921-
m_ShadowAngle: 0
39223812
--- !u!1 &928571527
39233813
GameObject:
39243814
m_ObjectHideFlags: 0

0 commit comments

Comments
 (0)