Files
Kepler/shaders/physically_based_shading_minimal.shader

206 lines
6.4 KiB
Plaintext
Raw Normal View History

2025-11-17 17:18:43 +01:00
// Struct definitions (unchanged)
struct deferredMaterialData {
vec3 baseColor;
vec3 diffuse;
vec3 specularReflectance;
vec3 normal;
vec3 vertex;
float roughness;
float metallic;
float ambientOcclusion;
float alpha;
float clearcoat;
float clearcoatRoughness;
#if ANISOTROPY == 1
vec3 tangent;
#endif
#if TRANSLUCENT_MATERIAL == 1
float translucency;
float translucencyDistortion;
float translucencyPower;
float translucencyScale;
float translucencyAmbient;
#endif
float geometricRoughness;
};
struct deferredLightData {
vec3 direction;
float length;
float radius;
float inverseFalloff;
int lightType;
vec3 color;
};
// ————————— Optimized BRDF & utilities —————————
float D_GGX(float NoH, float a) {
float a2 = a * a;
float f = (NoH * a2 - NoH) * NoH + 1.0;
return a2 / max(PI * f * f, 1e-5);
}
float D_GGX_Anisotropy(float NoH, vec3 h, vec3 x, vec3 y, float ax, float ay) {
float XoH = dot(x, h), YoH = dot(y, h);
float d = XoH * XoH * (ax*ax) + YoH * YoH * (ay*ay) + NoH * NoH;
return (ax * ay) / max(PI * d * d, 1e-5);
}
vec3 F_Schlick(float VoH, vec3 F0, float f90) {
float pow5 = pow(1.0 - VoH, 5.0);
return F0 + (vec3(f90) - F0) * pow5;
}
float V_SmithGGXCorrelated(float NoV, float NoL, float a) {
float a2 = a * a;
float gv = sqrt(max((-NoV*a2 + NoV) * NoV + a2, 0.0));
float gl = sqrt(max((-NoL*a2 + NoL) * NoL + a2, 0.0));
return 0.5 / max(NoL * gv + NoV * gl, 1e-5);
}
float Fd_Lambert() {
return 1.0 / PI;
}
float F_Schlick_Scalar(float VoH, float F0, float f90) {
float pow5 = pow(1.0 - VoH, 5.0);
return F0 + (f90 - F0) * pow5;
}
vec3 irradianceSH(vec3 n) {
float nx = n.x, ny = n.y, nz = n.z;
float nymnx = ny * nx, nynz = ny * nz, nz2 = nz * nz, nxx_nyy = nx*nx - ny*ny;
return
vec3(0.7545535, 0.7485417, 0.7909225)
+ vec3(-0.08385509, 0.09253634, 0.3227673) * ny
+ vec3(0.3081546, 0.3667994, 0.4667058) * nz
+ vec3(-0.1888876, -0.2774037, -0.3778448) * nx
+ vec3(-0.2527824, -0.3160516, -0.396141) * nymnx
+ vec3(0.07136245, 0.1597891, 0.2905936) * nynz
+ vec3(-0.03104042) * (3.0 * nz2 - 1.0) // all three components identical
+ vec3(-0.1610019, -0.2036495, -0.246641) * (nz * nx)
+ vec3(0.04571093, 0.04812178, 0.04632638) * nxx_nyy;
}
vec2 prefilteredDFGKaris(float NoV, float roughness) {
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
vec4 r = roughness * c0 + c1;
float expTerm = exp2(-9.28 * NoV);
float a004 = min(r.x * r.x, expTerm) * r.x + r.y;
return vec2(-1.04, 1.04) * a004 + r.zw;
}
vec3 decodeEnvironmentMap(vec4 c) {
return c.rgb; // assume linear
}
float getSquareFalloffAttenuation(float dist2, float invFalloff) {
float sf = max(1.0 - (dist2 * invFalloff) * (dist2 * invFalloff), 0.0);
return sf * sf;
}
vec3 fixCubemapLookup(vec3 v, float lod) {
vec3 a = abs(v);
float M = max(max(a.x, a.y), a.z);
float sc = 1.0 - exp2(lod) * (1.0/256.0);
if (a.x != M) v.x *= sc;
if (a.y != M) v.y *= sc;
if (a.z != M) v.z *= sc;
return v;
}
vec3 evaluateSpecularIBL(vec3 r, float roughness) {
float lod = 5.0 * roughness;
return decodeEnvironmentMap(textureLod(reflectionSampler, fixCubemapLookup(r, lod), lod)) * 1.8;
}
float computeSpecularao(float NoV, float ao, float roughness) {
float powe = exp2(-16.0 * roughness - 1.0);
return clamp(pow(NoV + ao, powe) - 1.0 + ao, 0.0, 1.0);
}
vec3 getSpecularDominantDirection(vec3 n, vec3 r, float roughness2) {
float s = 1.0 - roughness2;
return mix(n, r, s * (sqrt(s) + sqrt(roughness2)));
}
// ————————— Lighting functions —————————
vec3 indirectLight(in deferredMaterialData m) {
float rough2 = m.roughness * m.roughness;
float NoV = max(dot(m.normal, m.vertex), 0.0);
vec3 r = reflect(-m.vertex, m.normal);
r = getSpecularDominantDirection(m.normal, r, rough2);
vec3 diff = m.diffuse * irradianceSH(m.normal) * (1.0 - m.metallic) * m.ambientOcclusion;
vec3 spec = evaluateSpecularIBL(r, m.roughness)
* computeSpecularao(NoV, m.ambientOcclusion, m.roughness);
return diff + spec;
}
vec3 directLight(in deferredLightData light, in deferredMaterialData m) {
vec3 n = m.normal;
vec3 v = m.vertex;
vec3 L = normalize(-light.direction);
float att = 1.0;
if (light.lightType == 1) {
float Nl = max(dot(n, L), 0.0);
att = smoothstep(0.9999, 1.0, Nl);
}
float NoL = max(dot(n, L), 0.0), NoV = max(dot(n, v), 0.0);
vec3 diff = m.diffuse * NoL;
vec3 H = normalize(L + v);
float NoH = max(dot(n, H), 0.0), VoH = max(dot(v, H), 0.0);
#if ANISOTROPY == 1
vec3 t = normalize(m.tangent);
vec3 b = normalize(cross(t, n));
float ax = sqrt(m.roughness);
float ay = ax;
float D = D_GGX_Anisotropy(NoH, H, t, b, ax, ay);
#else
float D = D_GGX(NoH, m.roughness * m.roughness);
#endif
float V = V_SmithGGXCorrelated(NoV, NoL, m.roughness * m.roughness);
vec3 F = F_Schlick(VoH, m.specularReflectance, 1.0);
vec3 spec = (D * V) * F / max(4.0 * NoV * NoL, 1e-5);
if (m.clearcoat > 0.0) {
float Cr2 = m.clearcoatRoughness * m.clearcoatRoughness;
float Dc = D_GGX(NoH, Cr2);
float Vc = V_SmithGGXCorrelated(NoV, NoL, Cr2);
float Fc = F_Schlick_Scalar(VoH, 0.04, 1.0);
spec += m.clearcoat * (Dc * Vc * Fc / max(4.0 * NoV * NoL, 1e-5));
}
#if TRANSLUCENT_MATERIAL == 1
vec3 dL = normalize(L + m.translucencyDistortion * vec3(0.1));
float Nt = max(dot(n, dL), 0.0);
float trs = pow(Nt, m.translucencyPower) * m.translucencyScale;
trs = clamp(trs + m.translucencyAmbient, 0.0, 1.0);
diff += m.translucency * trs * light.color;
#endif
return att * light.color * (diff * (1.0 - m.metallic) + spec);
}
// ————————— Main shading entry —————————
vec4 physically_based_shading(deferredLightData light, deferredMaterialData m) {
m.diffuse = m.baseColor * (1.0 - m.metallic);
m.specularReflectance = mix(vec3(0.04), m.baseColor, m.metallic);
m.geometricRoughness = m.roughness * m.roughness;
m.vertex = normalize(cameraPosition - m.vertex);
vec3 color = indirectLight(m) + directLight(light, m);
return vec4(color, m.alpha);
}