113 lines
4.5 KiB
HLSL
113 lines
4.5 KiB
HLSL
#include "shared/pbr.hlsl"
|
|
|
|
|
|
#ifndef __AdjustRoughnessForSpecularAA
|
|
#define __AdjustRoughnessForSpecularAA
|
|
|
|
inline float3 AdjustRoughnessForSpecularAA(float baseRoughness, float specularAA)
|
|
{
|
|
// --- Specular anti-aliasing ---
|
|
// Compute normal variance using screen-space derivatives and increase roughness accordingly.
|
|
// This reduces specular aliasing on silhouettes and high-frequency normalmap regions.
|
|
float3 Nx = ddx(frag.N);
|
|
float3 Ny = ddy(frag.N);
|
|
float normalVar = max(0.0, max(dot(Nx, Nx), dot(Ny, Ny)));
|
|
normalVar *= specularAA;
|
|
// convert roughness -> alpha (energy-preserving), combine variance, then convert back
|
|
float baseR = saturate(baseRoughness);
|
|
float baseR2 = baseR * baseR;
|
|
float adjustedR = sqrt(baseR2 + normalVar);
|
|
return saturate(adjustedR);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
// based on https://github.com/Nadrin/PBR/blob/master/data/shaders/hlsl/pbr.hlsl
|
|
float4 ComputePbr()
|
|
{
|
|
float3 N = frag.N;
|
|
float3 V = frag.Lo;
|
|
float3 Lo = frag.Lo;
|
|
|
|
// Angle between surface normal and outgoing light direction.
|
|
frag.cosLo = abs(dot(N, Lo));
|
|
// frag.cosLo = saturate(dot(N, V)); // not abs
|
|
// float3 Lr = 2.0 * frag.cosLo * N - V;
|
|
|
|
// Specular reflection vector.
|
|
float3 Lr = 2.0 * frag.cosLo * N - Lo;
|
|
// return float4(Lr.xyz,1);
|
|
|
|
// Fresnel reflectance at normal incidence (for metals use albedo color).
|
|
float3 F0 = lerp(Fdielectric, frag.albedo.rgb, frag.Metalness);
|
|
|
|
// Direct lighting calculation for analytical lights.
|
|
float3 directLighting = 0.0;
|
|
for (uint i = 0; i < (uint)ActiveLightCount; ++i)
|
|
{
|
|
float3 Lvec = Lights[i].position - frag.worldPosition;
|
|
float dist = length(Lvec);
|
|
float3 L = Lvec / max(dist, 1e-4); // normalize once
|
|
|
|
float intensity = Lights[i].intensity / (pow(dist / Lights[i].range, Lights[i].decay) + 1);
|
|
|
|
float3 Lradiance = Lights[i].color.rgb * intensity;
|
|
|
|
float NdotV = saturate(dot(N, V));
|
|
float NdotL = saturate(dot(N, L));
|
|
float3 H = normalize(L + V);
|
|
float NdotH = saturate(dot(N, H));
|
|
|
|
float3 F = fresnelSchlick(F0, saturate(dot(H, V)));
|
|
float D = ndfGGX(NdotH, frag.Roughness);
|
|
float G = gaSchlickGGX(NdotL, NdotV, frag.Roughness);
|
|
|
|
float3 kd = lerp(1.0 - F, 0.0, frag.Metalness);
|
|
float3 diffuseBRDF = kd * frag.albedo.rgb;
|
|
float3 specularBRDF = ((F * D * G) / max(Epsilon, 4.0 * NdotL * NdotV)) * Specular;
|
|
|
|
directLighting += (diffuseBRDF + specularBRDF) * Lradiance * NdotL;
|
|
}
|
|
|
|
// Ambient lighting (IBL).
|
|
float3 ambientLighting = 0;
|
|
{
|
|
// Sample diffuse irradiance at normal direction.
|
|
// float3 irradiance = 0;// irradianceTexture.Sample(TexSampler, N).rgb;
|
|
uint width, height, levels;
|
|
PrefilteredSpecular.GetDimensions(0, width, height, levels);
|
|
float3 irradiance = PrefilteredSpecular.SampleLevel(WrappedSampler, N, 0.6 * levels).rgb;
|
|
|
|
// Calculate Fresnel term for ambient lighting.
|
|
// Since we use pre-filtered cubemap(s) and irradiance is coming from many directions
|
|
// use cosLo instead of angle with light's half-vector (cosLh above).
|
|
// See: https://seblagarde.wordpress.com/2011/08/17/hello-world/
|
|
float3 F = fresnelSchlick(F0, frag.cosLo);
|
|
|
|
// Get diffuse contribution factor (as with direct lighting).
|
|
float3 kd = lerp(1.0 - F, 0.0, frag.Metalness);
|
|
|
|
// Irradiance map contains exitant radiance assuming Lambertian BRDF, no need to scale by 1/PI here either.
|
|
float3 diffuseIBL = kd * frag.albedo.rgb * irradiance;
|
|
|
|
// Sample pre-filtered specular reflection environment at correct mipmap level.
|
|
float3 specularIrradiance = PrefilteredSpecular.SampleLevel(WrappedSampler, Lr, frag.Roughness * levels).rgb;
|
|
|
|
// Split-sum approximation factors for Cook-Torrance specular BRDF.
|
|
float2 specularBRDF = BRDFLookup.SampleLevel(ClampedSampler, float2(frag.cosLo, frag.Roughness), 0).rg;
|
|
|
|
// Total specular IBL contribution.
|
|
float3 specularIBL = (F0 * specularBRDF.x + specularBRDF.y) * specularIrradiance;
|
|
ambientLighting = (diffuseIBL + specularIBL) * frag.Occlusion;
|
|
}
|
|
|
|
// Final fragment color.
|
|
float4 litColor = float4(directLighting + ambientLighting, 1.0) * BaseColor * Color;
|
|
|
|
litColor += float4(EmissiveColorMap.Sample(WrappedSampler, frag.uv).rgb * EmissiveColor.rgb, 0);
|
|
litColor.rgb = lerp(litColor.rgb, FogColor.rgb, frag.fog * FogColor.a);
|
|
litColor.a *= frag.albedo.a;
|
|
|
|
return litColor;
|
|
} |