|
|
|
|
|
|
#define ENVMAP_FEATURE_PERFACEINFLUENCE |
|
|
|
#define ENVMAP_FEATURE_INFLUENCENORMAL |
|
|
|
#define ENVMAP_FEATURE_PERFACEFADE |
|
|
|
|
|
|
|
// SurfaceData is define in Lit.cs which generate Lit.cs.hlsl |
|
|
|
// SurfaceData is define in Lit.cs which generate Lit.cs.hlsl |
|
|
|
#include "../../Lighting/VolumeProjection.hlsl" |
|
|
|
#include "../SubsurfaceScattering/SubsurfaceScattering.hlsl" |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
// In Unity the cubemaps are capture with the localToWorld transform of the component. |
|
|
|
// This mean that location and orientation matter. So after intersection of proxy volume we need to convert back to world. |
|
|
|
|
|
|
|
// CAUTION: localToWorld is the transform use to convert the cubemap capture point to world space (mean it include the offset) |
|
|
|
// the center of the bounding box is thus in locals space: positionLS - offsetLS |
|
|
|
// We use this formulation as it is the one of legacy unity that was using only AABB box. |
|
|
|
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. |
|
|
|
float3x3 worldToLS = WorldToLightSpace(lightData); |
|
|
|
float3 positionLS = WorldToLightPosition(lightData, worldToLS, positionWS); |
|
|
|
float3x3 worldToLS = EnvLightData_GetWorldToLocal(lightData); |
|
|
|
float3 positionLS = EnvLightData_WorldToLocalPosition(lightData, worldToLS, positionWS); |
|
|
|
|
|
|
|
// Projection and influence share the shape |
|
|
|
// Projection and influence share the space |
|
|
|
float3x3 worldToPS = worldToLS; |
|
|
|
float3 positionPS = positionLS; |
|
|
|
|
|
|
|
|
|
|
// 1. First process the projection |
|
|
|
float3 dirPS = mul(R, worldToPS); |
|
|
|
|
|
|
|
float projectionDistance = EnvProjData_Sphere_Project(projData, dirPS, positionPS); |
|
|
|
float projectionDistance = IntersectSphereProxy(projData, dirPS, positionPS); |
|
|
|
// We can reuse dist calculate in LS directly in WS as there is no scaling. Also the offset is already include in lightData.positionWS |
|
|
|
R = (positionWS + projectionDistance * R) - lightData.positionWS; |
|
|
|
|
|
|
|
|
|
|
dirPS = mul(coatR, worldToPS); |
|
|
|
projectionDistance = EnvProjData_Sphere_Project(projData, dirPS, positionPS); |
|
|
|
projectionDistance = IntersectSphereProxy(projData, dirPS, positionPS); |
|
|
|
// 2. Process the position influence |
|
|
|
float lengthPositionLS = length(positionLS); |
|
|
|
float sphereInfluenceDistance = lightData.influenceExtents.x - lightData.blendDistancePositive.x; |
|
|
|
float distFade = max(lengthPositionLS - sphereInfluenceDistance, 0.0); |
|
|
|
float alpha = saturate(1.0 - distFade / max(lightData.blendDistancePositive.x, 0.0001)); // avoid divide by zero |
|
|
|
|
|
|
|
#if defined(ENVMAP_FEATURE_INFLUENCENORMAL) |
|
|
|
// 3. Process the normal influence |
|
|
|
float insideInfluenceNormalVolume = lengthPositionLS <= (lightData.influenceExtents.x - lightData.blendNormalDistancePositive.x) ? 1.0 : 0.0; |
|
|
|
float insideWeight = InfluenceFadeNormalWeight(bsdfData.normalWS, normalize(positionWS - lightData.positionWS)); |
|
|
|
alpha *= insideInfluenceNormalVolume ? 1.0 : insideWeight; |
|
|
|
#endif |
|
|
|
|
|
|
|
weight = alpha; |
|
|
|
// 2. Process the influence |
|
|
|
float3 dirLS = dirPS; // Projection and influence share the space |
|
|
|
weight = InfluenceSphereWeight(lightData, bsdfData, positionWS, positionLS, dirLS); |
|
|
|
float3 dirLS = mul(R, worldToLocal); |
|
|
|
float3 boxOuterDistance = lightData.influenceExtents; |
|
|
|
float projectionDistance = BoxRayIntersectSimple(positionLS, dirLS, -boxOuterDistance, boxOuterDistance); |
|
|
|
projectionDistance = max(projectionDistance, lightData.minProjectionDistance); // Setup projection to infinite if requested (mean no projection shape) |
|
|
|
|
|
|
|
float3 dirPS = mul(R, worldToPS); |
|
|
|
float projectionDistance = IntersectBoxProxy(projData, dirPS, positionPS); |
|
|
|
// No need to normalize for fetching cubemap |
|
|
|
// We can reuse dist calculate in LS directly in WS as there is no scaling. Also the offset is already include in lightData.positionWS |
|
|
|
R = (positionWS + projectionDistance * R) - lightData.positionWS; |
|
|
|
|
|
|
// Test again for clear code |
|
|
|
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT)) |
|
|
|
{ |
|
|
|
dirLS = mul(coatR, worldToLocal); |
|
|
|
projectionDistance = BoxRayIntersectSimple(positionLS, dirLS, -boxOuterDistance, boxOuterDistance); |
|
|
|
projectionDistance = max(projectionDistance, lightData.minProjectionDistance); // Setup projection to infinite if requested (mean no projection shape) |
|
|
|
dirPS = mul(coatR, worldToPS); |
|
|
|
projectionDistance = IntersectBoxProxy(projData, dirPS, positionPS); |
|
|
|
// 2. Process the position influence |
|
|
|
// Calculate falloff value, so reflections on the edges of the volume would gradually blend to previous reflection. |
|
|
|
|
|
|
|
#if defined(ENVMAP_FEATURE_PERFACEINFLUENCE) || defined(ENVMAP_FEATURE_INFLUENCENORMAL) || defined(ENVMAP_FEATURE_PERFACEFADE) |
|
|
|
// Distance to each cube face |
|
|
|
float3 negativeDistance = boxOuterDistance + positionLS; |
|
|
|
float3 positiveDistance = boxOuterDistance - positionLS; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if defined(ENVMAP_FEATURE_PERFACEINFLUENCE) |
|
|
|
// Influence falloff for each face |
|
|
|
float3 negativeFalloff = negativeDistance / max(0.0001, lightData.blendDistanceNegative); |
|
|
|
float3 positiveFalloff = positiveDistance / max(0.0001, lightData.blendDistancePositive); |
|
|
|
|
|
|
|
// Fallof is the min for all faces |
|
|
|
float influenceFalloff = min( |
|
|
|
min(min(negativeFalloff.x, negativeFalloff.y), negativeFalloff.z), |
|
|
|
min(min(positiveFalloff.x, positiveFalloff.y), positiveFalloff.z)); |
|
|
|
|
|
|
|
float alpha = saturate(influenceFalloff); |
|
|
|
#else |
|
|
|
float distFace = DistancePointBox(positionLS, -lightData.influenceExtents + lightData.blendDistancePositive.x, lightData.influenceExtents - lightData.blendDistancePositive.x); |
|
|
|
float alpha = saturate(1.0 - distFace / max(lightData.blendDistancePositive.x, 0.0001)); |
|
|
|
#endif |
|
|
|
|
|
|
|
#if defined(ENVMAP_FEATURE_INFLUENCENORMAL) |
|
|
|
// 3. Process the normal influence |
|
|
|
// Calculate a falloff value to discard normals pointing outward the center of the environment light |
|
|
|
float3 belowPositiveInfluenceNormalVolume = positiveDistance / max(0.0001, lightData.blendNormalDistancePositive); |
|
|
|
float3 aboveNegativeInfluenceNormalVolume = negativeDistance / max(0.0001, lightData.blendNormalDistanceNegative); |
|
|
|
float insideInfluenceNormalVolume = all(belowPositiveInfluenceNormalVolume >= 1.0) && all(aboveNegativeInfluenceNormalVolume >= 1.0) ? 1.0 : 0; |
|
|
|
float insideWeight = InfluenceFadeNormalWeight(bsdfData.normalWS, normalize(positionWS - lightData.positionWS)); |
|
|
|
alpha *= insideInfluenceNormalVolume ? 1.0 : insideWeight; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if defined(ENVMAP_FEATURE_PERFACEFADE) |
|
|
|
// 4. Fade specific cubemap faces |
|
|
|
// For each axes (both positive and negative ones), we want to fade from the center of one face to another |
|
|
|
// So we normalized the sample direction (R) and use its component to fade for each axis |
|
|
|
// We consider R.x as cos(X) and then fade as angle from 60°(=acos(1/2)) to 75°(=acos(1/4)) |
|
|
|
// For positive axes: axisFade = (R - 1/4) / (1/2 - 1/4) |
|
|
|
// <=> axisFace = 4 * R - 1; |
|
|
|
R = normalize(R); |
|
|
|
float3 faceFade = saturate((4 * R - 1) * lightData.boxSideFadePositive) + saturate((-4 * R - 1) * lightData.boxSideFadeNegative); |
|
|
|
alpha *= saturate(faceFade.x + faceFade.y + faceFade.z); |
|
|
|
#endif |
|
|
|
|
|
|
|
weight = alpha; |
|
|
|
// 2. Process the influence |
|
|
|
float3 dirLS = dirPS; // Projection and influence share the space |
|
|
|
weight = InfluenceBoxWeight(lightData, bsdfData, positionWS, positionLS, dirLS); |
|
|
|
} |
|
|
|
|
|
|
|
// Smooth weighting |
|
|
|