您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
238 行
10 KiB
238 行
10 KiB
// No guard header!
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// EvaluateBSDF_Env - Reference
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Ref: Moving Frostbite to PBR (Appendix A)
|
|
float3 IntegrateLambertIBLRef( EnvLightData lightData, BSDFData bsdfData,
|
|
UNITY_ARGS_ENV(_EnvTextures),
|
|
uint sampleCount = 2048)
|
|
{
|
|
float3 N = bsdfData.normalWS;
|
|
float3 acc = float3(0.0, 0.0, 0.0);
|
|
// Add some jittering on Hammersley2d
|
|
float2 randNum = InitRandom(N.xy * 0.5 + 0.5);
|
|
|
|
float3 tangentX, tangentY;
|
|
GetLocalFrame(N, tangentX, tangentY);
|
|
|
|
for (uint i = 0; i < sampleCount; ++i)
|
|
{
|
|
float2 u = Hammersley2d(i, sampleCount);
|
|
u = frac(u + randNum + 0.5);
|
|
|
|
float3 L;
|
|
float NdotL;
|
|
float weightOverPdf;
|
|
ImportanceSampleLambert(u, N, tangentX, tangentY, L, NdotL, weightOverPdf);
|
|
|
|
if (NdotL > 0.0)
|
|
{
|
|
float4 val = UNITY_SAMPLE_ENV_LOD(_EnvTextures, L, lightData, 0);
|
|
|
|
// diffuse Albedo is apply here as describe in ImportanceSampleLambert function
|
|
acc += bsdfData.diffuseColor * Lambert() * weightOverPdf * val.rgb;
|
|
}
|
|
}
|
|
|
|
return acc / sampleCount;
|
|
}
|
|
|
|
float3 IntegrateDisneyDiffuseIBLRef(float3 V, EnvLightData lightData, BSDFData bsdfData,
|
|
UNITY_ARGS_ENV(_EnvTextures),
|
|
uint sampleCount = 2048)
|
|
{
|
|
float3 N = bsdfData.normalWS;
|
|
float NdotV = dot(N, V);
|
|
float3 acc = float3(0.0, 0.0, 0.0);
|
|
// Add some jittering on Hammersley2d
|
|
float2 randNum = InitRandom(N.xy * 0.5 + 0.5);
|
|
|
|
float3 tangentX, tangentY;
|
|
GetLocalFrame(N, tangentX, tangentY);
|
|
|
|
for (uint i = 0; i < sampleCount; ++i)
|
|
{
|
|
float2 u = Hammersley2d(i, sampleCount);
|
|
u = frac(u + randNum + 0.5);
|
|
|
|
float3 L;
|
|
float NdotL;
|
|
float weightOverPdf;
|
|
// for Disney we still use a Cosine importance sampling, true Disney importance sampling imply a look up table
|
|
ImportanceSampleLambert(u, N, tangentX, tangentY, L, NdotL, weightOverPdf);
|
|
|
|
if (NdotL > 0.0)
|
|
{
|
|
float3 H = normalize(L + V);
|
|
float LdotH = dot(L, H);
|
|
// Note: we call DisneyDiffuse that require to multiply by Albedo / PI. Divide by PI is already taken into account
|
|
// in weightOverPdf of ImportanceSampleLambert call.
|
|
float disneyDiffuse = DisneyDiffuse(NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
|
|
|
|
// diffuse Albedo is apply here as describe in ImportanceSampleLambert function
|
|
float4 val = UNITY_SAMPLE_ENV_LOD(_EnvTextures, L, lightData, 0);
|
|
acc += bsdfData.diffuseColor * disneyDiffuse * weightOverPdf * val.rgb;
|
|
}
|
|
}
|
|
|
|
return acc / sampleCount;
|
|
}
|
|
|
|
// Ref: Moving Frostbite to PBR (Appendix A)
|
|
float3 IntegrateSpecularGGXIBLRef( float3 V, EnvLightData lightData, BSDFData bsdfData,
|
|
UNITY_ARGS_ENV(_EnvTextures),
|
|
uint sampleCount = 2048)
|
|
{
|
|
float3 N = bsdfData.normalWS;
|
|
float NdotV = saturate(dot(N, V));
|
|
float3 acc = float3(0.0, 0.0, 0.0);
|
|
|
|
// Add some jittering on Hammersley2d
|
|
float2 randNum = InitRandom(V.xy * 0.5 + 0.5);
|
|
|
|
float3 tangentX, tangentY;
|
|
GetLocalFrame(N, tangentX, tangentY);
|
|
|
|
for (uint i = 0; i < sampleCount; ++i)
|
|
{
|
|
float2 u = Hammersley2d(i, sampleCount);
|
|
u = frac(u + randNum + 0.5);
|
|
|
|
float VdotH;
|
|
float NdotL;
|
|
float3 L;
|
|
float weightOverPdf;
|
|
|
|
// GGX BRDF
|
|
ImportanceSampleGGX(u, V, N, tangentX, tangentY, bsdfData.roughness, NdotV,
|
|
L, VdotH, NdotL, weightOverPdf);
|
|
|
|
if (NdotL > 0.0)
|
|
{
|
|
// Fresnel component is apply here as describe in ImportanceSampleGGX function
|
|
float3 FweightOverPdf = F_Schlick(bsdfData.fresnel0, VdotH) * weightOverPdf;
|
|
|
|
float4 val = UNITY_SAMPLE_ENV_LOD(_EnvTextures, L, lightData, 0);
|
|
|
|
acc += FweightOverPdf * val.rgb;
|
|
}
|
|
}
|
|
|
|
return acc / sampleCount;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// EvaluateBSDF_Env
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// _preIntegratedFGD and _CubemapLD are unique for each BRDF
|
|
void EvaluateBSDF_Env( float3 V, float3 positionWS, PreLightData prelightData, EnvLightData lightData, BSDFData bsdfData,
|
|
UNITY_ARGS_ENV(_EnvTextures),
|
|
out float4 diffuseLighting,
|
|
out float4 specularLighting)
|
|
{
|
|
#ifdef LIT_DISPLAY_REFERENCE
|
|
|
|
specularLighting.rgb = IntegrateSpecularGGXIBLRef(V, lightData, bsdfData, UNITY_PASS_ENV(_EnvTextures));
|
|
specularLighting.a = 1.0;
|
|
|
|
/*
|
|
#ifdef DIFFUSE_LAMBERT_BRDF
|
|
diffuseLighting.rgb = IntegrateLambertIBLRef(lightData, bsdfData, UNITY_PASS_ENV(_EnvTextures));
|
|
#else
|
|
diffuseLighting.rgb = IntegrateDisneyDiffuseIBLRef(V, lightData, bsdfData, UNITY_PASS_ENV(_EnvTextures));
|
|
#endif
|
|
diffuseLighting.a = 1.0;
|
|
*/
|
|
diffuseLighting = float4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
#else
|
|
// TODO: factor this code in common, so other material authoring don't require to rewrite everything,
|
|
// also think about how such a loop can handle 2 cubemap at the same time as old unity. Macro can allow to do that
|
|
// but we need to have UNITY_SAMPLE_ENV_LOD replace by a true function instead that is define by the lighting arcitecture.
|
|
// Also not sure how to deal with 2 intersection....
|
|
// Box and sphere are related to light property (but we have also distance based roughness etc...)
|
|
|
|
// 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;
|
|
float weight = 1.0;
|
|
|
|
// In this code we redefine a bit the behavior of the reflcetion proble. We separate the projection volume (the proxy of the scene) form the influence volume (what pixel on the screen is affected)
|
|
|
|
// 1. First determine the projection volume
|
|
|
|
// In Unity the cubemaps are capture with the localToWorld transform of the component.
|
|
// This mean that location and oritention 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.
|
|
|
|
if (lightData.envShapeType == ENVSHAPETYPE_BOX)
|
|
{
|
|
float3 rayLS = mul(rayWS, worldToLocal);
|
|
float3 boxOuterDistance = lightData.innerDistance + float3(lightData.blendDistance, lightData.blendDistance, lightData.blendDistance);
|
|
float dist = BoxRayIntersectSimple(positionLS, rayLS, -boxOuterDistance, boxOuterDistance);
|
|
|
|
// 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 + dist * rayWS) - lightData.positionWS;
|
|
|
|
// TODO: add distance based roughness
|
|
}
|
|
else if (lightData.envShapeType == ENVSHAPETYPE_SPHERE)
|
|
{
|
|
float3 rayLS = mul(rayWS, worldToLocal);
|
|
float sphereOuterDistance = lightData.innerDistance.x + lightData.blendDistance;
|
|
float dist = SphereRayIntersectSimple(positionLS, rayLS, sphereOuterDistance);
|
|
|
|
R = (positionWS + dist * rayWS) - lightData.positionWS;
|
|
}
|
|
|
|
// 2. Apply the influence volume (Box volume is used for culling whatever the influence shape)
|
|
// TODO: In the future we could have an influence volume inside the projection volume (so with a different transform, in this case we will need another transform)
|
|
if (lightData.envShapeType == ENVSHAPETYPE_SPHERE)
|
|
{
|
|
float distFade = max(length(positionLS) - lightData.innerDistance.x, 0.0);
|
|
weight = saturate(1.0 - distFade / max(lightData.blendDistance, 0.0001)); // avoid divide by zero
|
|
}
|
|
else // ENVSHAPETYPE_BOX or ENVSHAPETYPE_NONE
|
|
{
|
|
// Calculate falloff value, so reflections on the edges of the volume would gradually blend to previous reflection.
|
|
float distFade = DistancePointBox(positionLS, -lightData.innerDistance, lightData.innerDistance);
|
|
weight = saturate(1.0 - distFade / max(lightData.blendDistance, 0.0001)); // avoid divide by zero
|
|
}
|
|
|
|
// Smooth weighting
|
|
weight = smoothstep01(weight);
|
|
|
|
// TODO: we must always perform a weight calculation as due to tiled rendering we need to smooth out cubemap at boundaries.
|
|
// So goal is to split into two category and have an option to say if we parallax correct or not.
|
|
|
|
// TODO: compare current Morten version: offline cubemap with a particular remap + the bias in perceptualRoughnessToMipmapLevel
|
|
// to classic remap like unreal/Frobiste. The function GetSpecularDominantDir can result in a better matching in this case
|
|
// We let GetSpecularDominantDir currently as it still an improvement but not as good as it could be
|
|
float mip = perceptualRoughnessToMipmapLevel(bsdfData.perceptualRoughness);
|
|
float4 preLD = UNITY_SAMPLE_ENV_LOD(_EnvTextures, R, lightData, mip);
|
|
specularLighting.rgb = preLD.rgb * prelightData.specularFGD;
|
|
|
|
// Apply specular occlusion on it
|
|
specularLighting.rgb *= bsdfData.specularOcclusion;
|
|
specularLighting.a = weight;
|
|
|
|
diffuseLighting = float4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
#endif
|
|
}
|
|
|