You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
7.0 KiB
164 lines
7.0 KiB
Shader "Hidden/HDRP/PreIntegratedFGD_CookTorrance"
|
|
{
|
|
SubShader
|
|
{
|
|
Tags{ "RenderPipeline" = "HDRenderPipeline" }
|
|
Pass
|
|
{
|
|
ZTest Always Cull Off ZWrite Off
|
|
|
|
HLSLPROGRAM
|
|
|
|
#pragma editor_sync_compilation
|
|
|
|
#pragma vertex Vert
|
|
#pragma fragment Frag
|
|
#pragma target 4.5
|
|
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch switch2
|
|
#define PREFER_HALF 0
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Importance Sampling
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Formulas come from https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/ for the Beckmann normal distribution
|
|
void SampleCookTorranceDir( real2 u,
|
|
real3 V,
|
|
real3x3 localToWorld,
|
|
real roughness,
|
|
out real3 L,
|
|
out real NdotL,
|
|
out real NdotH,
|
|
out real VdotH )
|
|
{
|
|
// Cook-Torrance NDF sampling
|
|
real cosTheta = sqrt(SafeDiv(1.0, 1.0 - (roughness * roughness) * log(1.0 - u.x)));
|
|
real phi = TWO_PI * u.y;
|
|
|
|
real3 localH = SphericalToCartesian(phi, cosTheta);
|
|
|
|
NdotH = cosTheta;
|
|
|
|
real3 localV;
|
|
|
|
localV = mul(V, transpose(localToWorld));
|
|
VdotH = saturate(dot(localV, localH));
|
|
|
|
// Compute { localL = reflect(-localV, localH) }
|
|
real3 localL = -localV + 2.0 * VdotH * localH;
|
|
NdotL = localL.z;
|
|
|
|
L = mul(localL, localToWorld);
|
|
}
|
|
|
|
// weightOverPdf returns the weight (without the Fresnel term) over pdf. Fresnel term must be applied by the caller.
|
|
void ImportanceSampleCookTorrance( real2 u,
|
|
real3 V,
|
|
real3x3 localToWorld,
|
|
real roughness,
|
|
real NdotV,
|
|
out real3 L,
|
|
out real VdotH,
|
|
out real NdotL,
|
|
out real weightOverPdf)
|
|
{
|
|
real NdotH;
|
|
SampleCookTorranceDir(u, V, localToWorld, roughness, L, NdotL, NdotH, VdotH);
|
|
|
|
// Importance sampling weight for each sample
|
|
// pdf = D(H) * (N.H) / (4 * (L.H))
|
|
// Note: the first N.H converts D() to a true PDF, but the PDF is wrt to solid
|
|
// angle dH. Since we integrate over dL, we change the PDF to one over the
|
|
// solid angle measure dL with the Jacobian dH/dL = 1/(4*L.H)
|
|
// weight = fr * (N.L) with fr = F(H) * G(V, L) * D(H) / (4 * (N.L) * (N.V))
|
|
// weight over pdf is:
|
|
// weightOverPdf = F(H) * G(V, L) * (L.H) / ((N.H) * (N.V))
|
|
// weightOverPdf = F(H) * 4 * (N.L) * V(V, L) * (L.H) / (N.H)
|
|
// with V(V, L) = G(V, L) / (4 * (N.L) * (N.V))
|
|
// Reminder: (L.H) == (V.H)
|
|
// F is applied outside the function
|
|
|
|
weightOverPdf = G_CookTorrance(NdotH, NdotV, NdotL, VdotH) * VdotH / (NdotH * NdotV);
|
|
}
|
|
|
|
float4 IntegrateCookTorranceFGD(float3 V, float3 N, float roughness, uint sampleCount = 8192)
|
|
{
|
|
float NdotV = ClampNdotV( dot(N, V) );
|
|
float4 acc = float4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
float3x3 localToWorld = GetLocalFrame(N); //TODO: N not needed, we use a frame aligned to N, should use k_identity3x3
|
|
|
|
for (uint i = 0; i < sampleCount; ++i)
|
|
{
|
|
float2 u = Hammersley2d(i, sampleCount);
|
|
|
|
float VdotH;
|
|
float NdotL;
|
|
float weightOverPdf;
|
|
|
|
float3 L; // Unused
|
|
ImportanceSampleCookTorrance( u, V, localToWorld, roughness, NdotV,
|
|
L, VdotH, NdotL, weightOverPdf);
|
|
|
|
if (NdotL > 0.0)
|
|
{
|
|
// Integral{BSDF * <N,L> dw} =
|
|
// Integral{(F0 + (1 - F0) * (1 - <V,H>)^5) * (BSDF / F) * <N,L> dw} =
|
|
// (1 - F0) * Integral{(1 - <V,H>)^5 * (BSDF / F) * <N,L> dw} + F0 * Integral{(BSDF / F) * <N,L> dw}=
|
|
// (1 - F0) * x + F0 * y = lerp(x, y, F0)
|
|
acc.x += weightOverPdf * pow( 1 - VdotH, 5 );
|
|
acc.y += weightOverPdf;
|
|
}
|
|
}
|
|
|
|
acc /= sampleCount;
|
|
|
|
return float4(acc.xy, 1.0, 0.0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Pre-Integration
|
|
// ----------------------------------------------------------------------------
|
|
|
|
struct Attributes
|
|
{
|
|
uint vertexID : SV_VertexID;
|
|
};
|
|
|
|
struct Varyings
|
|
{
|
|
float4 positionCS : SV_POSITION;
|
|
float2 texCoord : TEXCOORD0;
|
|
};
|
|
|
|
Varyings Vert(Attributes input)
|
|
{
|
|
Varyings output;
|
|
|
|
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
|
|
output.texCoord = GetFullScreenTriangleTexCoord(input.vertexID);
|
|
|
|
return output;
|
|
}
|
|
|
|
float4 Frag(Varyings input) : SV_Target
|
|
{
|
|
// These coordinate sampling must match the decoding in GetPreIntegratedDFG in lit.hlsl, i.e here we use perceptualRoughness, must be the same in shader
|
|
float NdotV = input.texCoord.x;
|
|
float perceptualRoughness = input.texCoord.y;
|
|
float3 V = float3(sqrt(1 - NdotV * NdotV), 0, NdotV);
|
|
float3 N = float3(0.0, 0.0, 1.0);
|
|
|
|
float4 preFGD = IntegrateCookTorranceFGD(V, N, PerceptualRoughnessToRoughness(perceptualRoughness));
|
|
|
|
return float4(preFGD.xyz, 1.0);
|
|
}
|
|
|
|
ENDHLSL
|
|
}
|
|
}
|
|
Fallback Off
|
|
}
|