浏览代码

Merge pull request #567 from EvgeniiG/view_refl_normal

Introduce a more efficient way of handling back-facing normals
/Yibing-Project-2
GitHub 7 年前
当前提交
ab700e17
共有 6 个文件被更改,包括 72 次插入57 次删除
  1. 11
      ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl
  2. 45
      ScriptableRenderPipeline/Core/ShaderLibrary/CommonLighting.hlsl
  3. 58
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl
  4. 7
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitData.hlsl
  5. 6
      ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitReference.hlsl
  6. 2
      ScriptableRenderPipeline/HDRenderPipeline/SceneSettings/Resources/DrawTransmittanceGraph.shader

11
ScriptableRenderPipeline/Core/ShaderLibrary/Common.hlsl


#define HALF_PI 1.57079632679
#define INV_HALF_PI 0.636619772367
#define INFINITY asfloat(0x7F800000)
#define FLT_SMALL 0.0001
#define LOG2_E 1.44269504089
#define FLT_EPSILON 1.192092896e-07 // Smallest positive number, such that 1.0 + FLT_EPSILON != 1.0

#define HFLT_MIN 0.00006103515625 // 2^14 it is the same for 10, 11 and 16bit float. ref: https://www.khronos.org/opengl/wiki/Small_Float_Formats
#define SMALL_FLT_VALUE 0.0001
// General constant
float DegToRad(float deg)
{

return saturate(x * FLT_MAX) * 2.0 - 1.0;
}
// Orthonormalize the basis vectors using the Gram-Schmidt process.
// We assume that the length of the surface normal is sufficiently close to 1.
// return orthonormalize tangent
// Orthonormalizes the tangent frame using the Gram-Schmidt process.
// We assume that both the tangent and the normal are normalized.
// Returns the new tangent (the normal is unaffected).
float3 Orthonormalize(float3 tangent, float3 normal)
{
return normalize(tangent - dot(tangent, normal) * normal);

45
ScriptableRenderPipeline/Core/ShaderLibrary/CommonLighting.hlsl


// Helper functions
//-----------------------------------------------------------------------------
// 'NdotV' can become negative for visible pixels due to the perspective projection, normal mapping and decals.
// This can produce visible artifacts under specular lighting, both direct (overly dark/bright pixels) and indirect (incorrect cubemap direction).
// One way of avoiding these artifacts is to limit the value of 'NdotV' to a small positive number,
// and calculate the reflection vector for the cubemap fetch using a normal shifted into view.
float3 GetViewShiftedNormal(float3 N, float3 V, float NdotV, float minNdotV)
// Inputs: normalized normal and view vectors.
// Outputs: front-facing normal, and the new non-negative value of the cosine of the view angle.
// Important: call Orthonormalize() on the tangent and recompute the bitangent afterwards.
float3 GetViewReflectedNormal(float3 N, float3 V, out float NdotV)
if (NdotV < minNdotV)
{
// We do not renormalize the normal to save a few clock cycles.
// The magnitude difference is typically negligible, and the normal is only used to compute
// the reflection vector for the IBL cube map fetch (which does not depend on the magnitude).
N += (-NdotV + minNdotV) * V;
}
// Fragments of front-facing geometry can have back-facing normals due to interpolation,
// normal mapping and decals. This can cause visible artifacts from both direct (negative or
// extremely high values) and indirect (incorrect lookup direction) lighting.
// There are several ways to avoid this problem. To list a few:
//
// 1. Setting { NdotV = max(<N,V>, SMALL_VALUE) }. This effectively removes normal mapping
// from the affected fragments, making the surface appear flat.
//
// 2. Setting { NdotV = abs(<N,V>) }. This effectively reverses the convexity of the surface.
// It also reduces light leaking from non-shadow-casting lights. Note that 'NdotV' can still
// be 0 in this case.
//
// It's important to understand that simply changing the value of the cosine is insufficient.
// For one, it does not solve the incorrect lookup direction problem, since the normal itself
// is not modified. There is a more insidious issue, however. 'NdotV' is a constituent element
// of the mathematical system describing the relationships between different vectors - and
// not just normal and view vectors, but also light vectors, half vectors, tangent vectors, etc.
// Changing only one angle (or its cosine) leaves the system in an inconsistent state, where
// certain relationships can take on different values depending on whether 'NdotV' is used
// in the calculation or not. Therefore, it is important to change the normal (or another
// vector) in order to leave the system in a consistent state.
//
// We choose to follow the conceptual approach (2) by reflecting the normal around the
// (<N,V> = 0) boundary if necessary, as it allows us to preserve some normal mapping details.
NdotV = dot(N, V);
N = (NdotV >= 0) ? N : (N - 2 * NdotV * V);
NdotV = abs(NdotV);
return N;
}

58
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl


float4 _TransmissionTints[SSS_N_PROFILES]; // RGB = 1/4 * color, A = unused
CBUFFER_END
// General constant
#define MIN_N_DOT_V SMALL_FLT_VALUE // The minimum value of 'NdotV'
//-----------------------------------------------------------------------------
// Helper for cheap screen space raycasting
//-----------------------------------------------------------------------------

struct PreLightData
{
// General
float NdotV; // Geometric version (could be negative)
float NdotV;
// GGX
float partLambdaV;

{
PreLightData preLightData;
float3 N;
float NdotV;
N = bsdfData.coatNormalWS;
NdotV = saturate(dot(N, V));
preLightData.coatNdotV = NdotV;
preLightData.coatNdotV = dot(bsdfData.coatNormalWS, V);
// In the case of IBL we want shift a bit the normal that are not toward the viewver to reduce artifact
float3 coatIblNormalWS = GetViewShiftedNormal(bsdfData.coatNormalWS, V, preLightData.coatNdotV, MIN_N_DOT_V); // Use non-clamped NdotV
preLightData.coatIblDirWS = reflect(-V, coatIblNormalWS);
float coatNdotV = max(preLightData.coatNdotV, MIN_N_DOT_V); // Use the modified (clamped) version
preLightData.coatIblDirWS = reflect(-V, N);
float theta = FastACosPos(coatNdotV);
float theta = FastACosPos(NdotV);
float2 uv = LTC_LUT_OFFSET + LTC_LUT_SCALE * float2(0.0, theta * INV_HALF_PI); // Use Roughness of 0.0 for clearCoat roughness
// Get the inverse LTC matrix for GGX

preLightData.ltcClearCoatFresnelTerm = preLightData.coatFresnel0 * ltcClearCoatFresnelMagnitudeDiff + ltcClearCoatFresnelMagnitude;
// TODO: Convert the area light with respect to Fresnel transmission
float3 N = bsdfData.coatNormalWS; // TODO : check with Laurentb
preLightData.refractV = ClearCoatTransform(V, bsdfData.coatNormalWS, ieta);
preLightData.refractV = ClearCoatTransform(V, N, ieta);
preLightData.NdotV = dot(bsdfData.normalWS, V); // Store the unaltered (geometric) version
N = bsdfData.normalWS;
NdotV = saturate(dot(N, V));
preLightData.NdotV = NdotV;
// In the case of IBL we want shift a bit the normal that are not toward the viewver to reduce artifact
float3 iblNormalWS = GetViewShiftedNormal(bsdfData.normalWS, V, preLightData.NdotV, MIN_N_DOT_V); // Use non-clamped NdotV
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
// GGX aniso
if (bsdfData.materialId == MATERIALID_LIT_ANISO && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_ANISO))

float3 grainDirWS = (bsdfData.anisotropy >= 0) ? bsdfData.bitangentWS : bsdfData.tangentWS;
// Reduce stretching for (perceptualRoughness < 0.2).
float stretch = abs(bsdfData.anisotropy) * saturate(5 * bsdfData.perceptualRoughness);
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NdotV) and use iblNormalWS
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NdotV) and use 'anisoIblNormalWS'
float3 anisoIblNormalWS = GetAnisotropicModifiedNormal(grainDirWS, iblNormalWS, V, stretch);
float3 anisoIblNormalWS = GetAnisotropicModifiedNormal(grainDirWS, N, V, stretch);
iblR = reflect(-V, anisoIblNormalWS);
}
else // GGX iso

preLightData.partLambdaV = GetSmithJointGGXPartLambdaV(NdotV, bsdfData.roughness);
iblR = reflect(-V, iblNormalWS);
iblR = reflect(-V, N);
}
float reflectivity;

float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
float shininess = Sqr(preLightData.ieta) * (2.0 / Sqr(roughness) - 2.0);
roughness = sqrt(2.0 / (shininess + 2.0));
preLightData.iblDirWS = GetSpecularDominantDir(iblNormalWS, iblR, roughness, NdotV);
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, roughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(RoughnessToPerceptualRoughness(roughness));
}
else

iblPerceptualRoughness = bsdfData.perceptualRoughness;
}
preLightData.iblDirWS = GetSpecularDominantDir(iblNormalWS, iblR, iblRoughness, NdotV);
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, iblRoughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(iblPerceptualRoughness);
}

preLightData.ltcTransformSpecular._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0);
// Construct a right-handed view-dependent orthogonal basis around the normal
preLightData.orthoBasisViewNormal[0] = normalize(V - bsdfData.normalWS * preLightData.NdotV);
preLightData.orthoBasisViewNormal[2] = bsdfData.normalWS;
preLightData.orthoBasisViewNormal[0] = normalize(V - N * NdotV);
preLightData.orthoBasisViewNormal[2] = N;
preLightData.orthoBasisViewNormal[1] = normalize(cross(preLightData.orthoBasisViewNormal[2], preLightData.orthoBasisViewNormal[0]));
float3 ltcMagnitude = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_MULTI_GGX_FRESNEL_DISNEY_DIFFUSE_INDEX, 0).rgb;

float NdotL = saturate(dot(bsdfData.coatNormalWS, L));
float NdotV = preLightData.coatNdotV;
float LdotV = dot(L, V);
float invLenLV = rsqrt(abs(2 * LdotV + 2));
float invLenLV = rsqrt(max(2 * LdotV + 2, FLT_SMALL));
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);

float NdotL = saturate(dot(bsdfData.normalWS, L)); // Must have the same value without the clamp
float NdotV = preLightData.NdotV; // Get the unaltered (geometric) version
float LdotV = dot(L, V);
float invLenLV = rsqrt(max(2 * LdotV + 2, SMALL_FLT_VALUE)); // invLenLV = rcp(length(L + V)) - caution about the case where V and L are opposite, it can happen, use max to avoid this
float invLenLV = rsqrt(max(2 * LdotV + 2, FLT_SMALL)); // invLenLV = rcp(length(L + V)) - caution about the case where V and L are opposite, it can happen, use max to avoid this
NdotV = max(NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
F *= F_Schlick(bsdfData.fresnel0, LdotH);

illuminance = Lambert() * wrappedNdotL;
#else
float tNdotL = saturate(-NdotL);
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float NdotV = preLightData.NdotV;
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL;
#endif

illuminance = Lambert() * wrappedNdotL;
#else
float tNdotL = saturate(-NdotL);
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float NdotV = preLightData.NdotV;
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL;
#endif

7
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitData.hlsl


float3 bentNormalWS;
float alpha = GetSurfaceData(input, layerTexCoord, surfaceData, normalTS, bentNormalTS);
GetNormalWS(input, V, normalTS, surfaceData.normalWS);
// Ensure that the normal is front-facing.
float NdotV;
surfaceData.normalWS = GetViewReflectedNormal(surfaceData.normalWS, V, NdotV);
// Use bent normal to sample GI if available
#ifdef _BENTNORMALMAP
GetNormalWS(input, V, bentNormalTS, bentNormalWS);

// If we have bent normal and ambient occlusion, process a specular occlusion
surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAO(V, bentNormalWS, surfaceData);
#elif defined(_MASKMAP)
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(dot(surfaceData.normalWS, V), surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(NdotV, surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
#else
surfaceData.specularOcclusion = 1.0;
#endif

6
ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitReference.hlsl


uint sampleCount = 4096)
{
float3x3 localToWorld = float3x3(bsdfData.tangentWS, bsdfData.bitangentWS, bsdfData.normalWS);
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float NdotV = preLightData.NdotV;
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d

localToWorld = GetLocalFrame(bsdfData.normalWS);
}
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float3 acc = float3(0.0, 0.0, 0.0);
float NdotV = preLightData.NdotV;
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d
float2 randNum = InitRandom(V.xy * 0.5 + 0.5);

2
ScriptableRenderPipeline/HDRenderPipeline/SceneSettings/Resources/DrawTransmittanceGraph.shader


// Include
//-------------------------------------------------------------------------------------
#include "../../../Core/ShaderLibrary/CommonMaterial.hlsl"
#include "../../../Core/ShaderLibrary/CommonMaterial.hlsl"
#define USE_LEGACY_UNITY_MATRIX_VARIABLES
#include "../../ShaderVariables.hlsl"

正在加载...
取消
保存