|
|
|
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
|
|
|
|
const float len = lightData.size.x; |
|
|
|
const float3 p0 = lightData.positionWS - lightData.right * (0.5 * len); |
|
|
|
const float3 p1 = lightData.positionWS - lightData.right * (0.5 * len); |
|
|
|
const float dt = len * rcp(sampleCount); |
|
|
|
const float off = 0.5 * dt; |
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
// Place the sample in the middle of the interval. |
|
|
|
float t = off + i * dt; |
|
|
|
float3 sPos = p0 + t * dir; |
|
|
|
float3 sPos = p1 + t * dir; |
|
|
|
float3 unL = sPos - positionWS; |
|
|
|
float dist2 = dot(unL, unL); |
|
|
|
float3 L = normalize(unL); |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// EvaluateBSDF_Area |
|
|
|
// EvaluateBSDF_Line | Approximation with Linearly Transformed Cosines |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
|
void EvaluateBSDF_Line( LightLoopContext lightLoopContext, |
|
|
|
float3 V, float3 positionWS, PreLightData preLightData, |
|
|
|
LightData lightData, BSDFData bsdfData, |
|
|
|
out float3 diffuseLighting, out float3 specularLighting) |
|
|
|
{ |
|
|
|
diffuseLighting = float3(0.0, 0.0, 0.0); |
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
|
|
|
|
float len = lightData.size.x; |
|
|
|
float3 dir = lightData.right; |
|
|
|
|
|
|
|
// TODO: precompute half-length. Same as for LTC area lights. |
|
|
|
// In fact, why not store both endpoints? Saves us 7 cycles. |
|
|
|
float3 p1 = lightData.positionWS - lightData.right * (0.5 * len); |
|
|
|
float3 p2 = lightData.positionWS + lightData.right * (0.5 * len); |
|
|
|
|
|
|
|
// Translate both points s.t. the shaded point is at the origin of the coordinate system. |
|
|
|
p1 -= positionWS; |
|
|
|
p2 -= positionWS; |
|
|
|
|
|
|
|
// Construct an orthonormal basis (local coordinate system) around N. |
|
|
|
// TODO: it could be stored in PreLightData. All LTC lights compute it more than once! |
|
|
|
float3x3 basis; |
|
|
|
basis[0] = normalize(V - bsdfData.normalWS * preLightData.NdotV); |
|
|
|
basis[1] = normalize(cross(bsdfData.normalWS, basis[0])); |
|
|
|
basis[2] = bsdfData.normalWS; |
|
|
|
|
|
|
|
// Transform (rotate) both endpoints into the local coordinate system (left-handed). |
|
|
|
p1 = mul(p1, transpose(basis)); |
|
|
|
p2 = mul(p2, transpose(basis)); |
|
|
|
|
|
|
|
// Terminate the algorithm if both points are below the horizon. |
|
|
|
if (p1.z <= 0.0 && p2.z <= 0.0) return; |
|
|
|
|
|
|
|
if (p2.z <= 0.0) |
|
|
|
{ |
|
|
|
// Convention: 'p2' is above the horizon. |
|
|
|
swap(p1, p2); |
|
|
|
dir = -dir; |
|
|
|
} |
|
|
|
|
|
|
|
// Clip the part of the light below the horizon. |
|
|
|
if (p1.z <= 0.0) |
|
|
|
{ |
|
|
|
// p = p1 + t * dir; p.z == 0. |
|
|
|
float t = -p1.z / dir.z; |
|
|
|
p1 = float3(p1.xy + t * dir.xy, 0.0); |
|
|
|
|
|
|
|
// Set the length of the visible part of the light. |
|
|
|
len -= t; |
|
|
|
} |
|
|
|
|
|
|
|
// Compute the direction to the point on the line orthogonal to 'dir'. |
|
|
|
// Its length is the shortest distance to the line. |
|
|
|
float3 p0 = p1 - dot(p1, dir) * dir; |
|
|
|
float dist = length(p0); |
|
|
|
|
|
|
|
// Compute the parameterization: distances from 'l1' and 'l2' to 'l0'. |
|
|
|
float l1 = dot(p1 - p0, dir); |
|
|
|
float l2 = l1 + len; |
|
|
|
|
|
|
|
// Integrate the clamped cosine over the line segment. |
|
|
|
float irradiance = LineIrradiance(l1, l2, dist, p0.z, dir.z); |
|
|
|
|
|
|
|
// Only Lambertian for now. TODO: Disney Diffuse and GGX. |
|
|
|
diffuseLighting = (lightData.diffuseScale * irradiance * INV_PI) * bsdfData.diffuseColor * lightData.color; |
|
|
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// EvaluateBSDF_Area | Approximation with Linearly Transformed Cosines |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
|
void EvaluateBSDF_Area( LightLoopContext lightLoopContext, |
|
|
|
|
|
|
IntegrateGGXAreaRef(V, positionWS, preLightData, lightData, bsdfData, diffuseLighting, specularLighting); |
|
|
|
} |
|
|
|
#else |
|
|
|
if (lightData.lightType == GPULIGHTTYPE_LINE) |
|
|
|
{ |
|
|
|
EvaluateBSDF_Line(lightLoopContext, V, positionWS, preLightData, lightData, bsdfData, diffuseLighting, specularLighting); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: store 4 points and save 24 cycles. |
|
|
|
float3 p0 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight; |
|
|
|
float3 p1 = lightData.positionWS + lightData.right * -halfWidth + lightData.up * -halfHeight; |
|
|
|
float3 p2 = lightData.positionWS + lightData.right * halfWidth + lightData.up * -halfHeight; |
|
|
|
|
|
|
0.0, 1.0, 0.0, |
|
|
|
0.0, 0.0, 1.0}; |
|
|
|
|
|
|
|
ltcValue = LTCEvaluate(V, bsdfData.normalWS, identity3x3, L, lightData.twoSided); |
|
|
|
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided, |
|
|
|
identity3x3); |
|
|
|
ltcValue = LTCEvaluate(V, bsdfData.normalWS, preLightData.ltcXformDisneyDiffuse, L, lightData.twoSided); |
|
|
|
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided, |
|
|
|
preLightData.ltcXformDisneyDiffuse); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (ltcValue == 0.0) |
|
|
|
|
|
|
float3 fresnelTerm = bsdfData.fresnel0 * preLightData.ltcGGXFresnelMagnitudeDiff |
|
|
|
+ (float3)preLightData.ltcGGXFresnelMagnitude; |
|
|
|
|
|
|
|
ltcValue = LTCEvaluate(V, bsdfData.normalWS, preLightData.ltcXformGGX, L, lightData.twoSided); |
|
|
|
ltcValue = LTCEvaluate(L, V, bsdfData.normalWS, preLightData.NdotV, lightData.twoSided, |
|
|
|
preLightData.ltcXformGGX); |
|
|
|
ltcValue *= lightData.specularScale; |
|
|
|
specularLighting = fresnelTerm * lightData.color * ltcValue; |
|
|
|
} |
|
|
|