|
|
|
|
|
|
// Aniso |
|
|
|
float TdotV; |
|
|
|
float BdotV; |
|
|
|
|
|
|
|
|
|
|
|
// image based lighting |
|
|
|
// These variables aim to be use with EvaluateBSDF_Env |
|
|
|
float3 iblNormalWS; // Normal to be use with image based lighting |
|
|
|
float3 iblR; // Reflection vector, same as above. |
|
|
|
float3 iblDirWS; // Dominant specular direction, used for IBL in EvaluateBSDF_Env() |
|
|
|
|
|
|
|
float3 specularFGD; // Store preconvole BRDF for both specular and diffuse |
|
|
|
float diffuseFGD; |
|
|
|
|
|
|
|
|
|
|
preLightData.ggxLambdaV = GetSmithJointGGXLambdaV(preLightData.NdotV, bsdfData.roughness); |
|
|
|
|
|
|
|
float iblNdotV = preLightData.NdotV; |
|
|
|
float iblNdotV = preLightData.NdotV; |
|
|
|
float3 iblNormalWS = bsdfData.normalWS; |
|
|
|
|
|
|
|
// Check if we precompute anisotropy too |
|
|
|
|
|
|
preLightData.anisoGGXLambdaV = GetSmithJointGGXAnisoLambdaV(preLightData.TdotV, preLightData.BdotV, preLightData.NdotV, bsdfData.roughnessT, bsdfData.roughnessB); |
|
|
|
// Tangent = highlight stretch (anisotropy) direction. Bitangent = grain (brush) direction. |
|
|
|
iblNormalWS = GetAnisotropicModifiedNormal(bsdfData.bitangentWS, bsdfData.normalWS, V, bsdfData.anisotropy); |
|
|
|
|
|
|
|
|
|
|
|
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NDotV) and use iblNormalWS |
|
|
|
// into function like GetSpecularDominantDir(). However modified normal is just a hack. The goal is just to stretch a cubemap, no accuracy here. |
|
|
|
// With this in mind and for performance reasons we chose to only use modified normal to calculate R. |
|
|
|
|
|
|
// We need to take into account the modified normal for faking anisotropic here. |
|
|
|
preLightData.iblR = reflect(-V, iblNormalWS); |
|
|
|
|
|
|
|
// We need to take into account the modified normal for faking anisotropic here. |
|
|
|
float3 iblR = reflect(-V, iblNormalWS); |
|
|
|
preLightData.iblDirWS = GetSpecularDominantDir(bsdfData.normalWS, iblR, bsdfData.roughness); |
|
|
|
|
|
|
|
// #if SHADERPASS == SHADERPASS_GBUFFER |
|
|
|
// preLightData.ambientOcclusion = LOAD_TEXTURE2D(_AmbientOcclusion, coord.unPositionSS).x; |
|
|
|
|
|
|
|
|
|
|
float3 unL = positionWS - lightData.positionWS; |
|
|
|
|
|
|
|
// Pick the axis along which to expand the fade-out sphere into an ellipsoid. |
|
|
|
// Pick the major axis of the ellipsoid. |
|
|
|
// We define the ellipsoid s.t. r1 = r, r2 = (r + len / 2). |
|
|
|
// We define the ellipsoid s.t. r1 = (r + len / 2), r2 = r3 = r. |
|
|
|
// TODO: This could be precomputed. |
|
|
|
float radius = rsqrt(lightData.invSqrAttenuationRadius); |
|
|
|
float invAspectRatio = radius / (radius + (0.5 * len)); |
|
|
|
|
|
|
// EvaluateBSDF_Area - Approximation with Linearly Transformed Cosines |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
|
// #define ELLIPSOIDAL_ATTENUATION |
|
|
|
|
|
|
|
void EvaluateBSDF_Area(LightLoopContext lightLoopContext, |
|
|
|
float3 V, float3 positionWS, |
|
|
|
PreLightData preLightData, LightData lightData, BSDFData bsdfData, |
|
|
|
|
|
|
|
|
|
|
float3 unL = positionWS - lightData.positionWS; |
|
|
|
|
|
|
|
// Pick the axis along which to expand the fade-out sphere into an ellipsoid. |
|
|
|
float3 axis = (halfWidth >= halfHeight) ? lightData.right : lightData.up; |
|
|
|
// Rotate the light direction into the light space. |
|
|
|
float3x3 lightToWorld = float3x3(lightData.right, lightData.up, lightData.forward); |
|
|
|
unL = mul(unL, transpose(lightToWorld)); |
|
|
|
// We define the ellipsoid s.t. r1 = r, r2 = (r + |w - h| / 2). |
|
|
|
// Define the dimensions of the attenuation volume. |
|
|
|
float radius = rsqrt(lightData.invSqrAttenuationRadius); |
|
|
|
float invAspectRatio = radius / (radius + abs(halfWidth - halfHeight)); |
|
|
|
float radius = rsqrt(lightData.invSqrAttenuationRadius); |
|
|
|
float3 invHalfDim = rcp(float3(radius + halfWidth, |
|
|
|
radius + halfHeight, |
|
|
|
radius)); |
|
|
|
float intensity = GetEllipsoidalDistanceAttenuation(unL, lightData.invSqrAttenuationRadius, |
|
|
|
axis, invAspectRatio); |
|
|
|
#ifdef ELLIPSOIDAL_ATTENUATION |
|
|
|
// The attenuation volume is an axis-aligned ellipsoid s.t. |
|
|
|
// r1 = (r + w / 2), r2 = (r + h / 2), r3 = r. |
|
|
|
float intensity = GetEllipsoidalDistanceAttenuation(unL, invHalfDim); |
|
|
|
#else |
|
|
|
// The attenuation volume is an axis-aligned box s.t. |
|
|
|
// hX = (r + w / 2), hY = (r + h / 2), hZ = r. |
|
|
|
float intensity = GetBoxDistanceAttenuation(unL, invHalfDim); |
|
|
|
#endif |
|
|
|
|
|
|
|
// Terminate if the shaded point is too far away. |
|
|
|
if (intensity == 0.0) return; |
|
|
|
|
|
|
|
|
|
|
// TODO: test the strech from Tomasz |
|
|
|
// float shrinkedRoughness = AnisotropicStrechAtGrazingAngle(bsdfData.roughness, bsdfData.perceptualRoughness, NdotV); |
|
|
|
|
|
|
|
// Note: As explain in GetPreLightData we use normalWS and not iblNormalWS here (in case of anisotropy) |
|
|
|
float3 rayWS = GetSpecularDominantDir(bsdfData.normalWS, preLightData.iblR, bsdfData.roughness); |
|
|
|
|
|
|
|
float3 R = rayWS; |
|
|
|
weight = float2(1.0, 1.0); |
|
|
|
|
|
|
|
// In Unity the cubemaps are capture with the localToWorld transform of the component. |
|
|
|
|
|
|
|
// In Unity the cubemaps are capture with the localToWorld transform of the component. |
|
|
|
|
|
|
|
|
|
|
|
float3 R = preLightData.iblDirWS; |
|
|
|
float3x3 worldToLocal = transpose(float3x3(lightData.right, lightData.up, lightData.forward)); // worldToLocal assume no scaling |
|
|
|
float3 positionLS = positionWS - lightData.positionWS; |
|
|
|
positionLS = mul(positionLS, worldToLocal).xyz - lightData.offsetLS; // We want to calculate the intersection from the center of the bounding box. |
|
|
|
|
|
|
float3 rayLS = mul(rayWS, worldToLocal); |
|
|
|
float3 dirLS = mul(preLightData.iblDirWS, worldToLocal); |
|
|
|
float dist = BoxRayIntersectSimple(positionLS, rayLS, -boxOuterDistance, boxOuterDistance); |
|
|
|
float dist = BoxRayIntersectSimple(positionLS, dirLS, -boxOuterDistance, boxOuterDistance); |
|
|
|
R = (positionWS + dist * rayWS) - lightData.positionWS; |
|
|
|
|
|
|
|
R = (positionWS + dist * preLightData.iblDirWS) - lightData.positionWS; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
float3 rayLS = mul(rayWS, worldToLocal); |
|
|
|
float3 dirLS = mul(preLightData.iblDirWS, worldToLocal); |
|
|
|
float dist = SphereRayIntersectSimple(positionLS, rayLS, sphereOuterDistance); |
|
|
|
float dist = SphereRayIntersectSimple(positionLS, dirLS, sphereOuterDistance); |
|
|
|
R = (positionWS + dist * rayWS) - lightData.positionWS; |
|
|
|
R = (positionWS + dist * preLightData.iblDirWS) - lightData.positionWS; |
|
|
|
weight.y = 1.0; |
|
|
|
|
|
|
|
if (lightData.envShapeType == ENVSHAPETYPE_SPHERE) |
|
|
|
{ |
|
|
|
float distFade = max(length(positionLS) - lightData.innerDistance.x, 0.0); |
|
|
|