pbr materials
pbr math
reflectance:
this is the main equation that a cook-torrance pbr system, all the other equations lead back to this one at some point
πΏπβ‘(π,ππ) =πΏπβ‘(π,ππ) +β«Ξ©ππβ‘(π,ππ,ππ)β’πΏπβ‘(π,ππ)β’(π§β ππ)β’πβ’ππ
this equation essentially just determines what light does after hitting a surface with specific parameters
we approximate the integral with two different light interactions:
- diffuse (β’ππ·): any light that penetrates the surface, scatters internally, and re-emerges
- specular (ππβ’): light that reflects directly off the micro-surface
in the code itself, the total outgoing radiance ($L_o$) is calculated with:
vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL;
cook-torrance brdf:
the specular component is calculated using the cook-torrance microfacet brdf:
β
β’## πcook-torrance =π·β
πΊβ
πΉ / 4β’(πβ
π)β’(πβ
π)β’
β
where π·β’, πΊ, and β’πΉ represent:
-
normal distribution (π·):
the NDF statistical distribution calculates how many microfacets are aligned with the halfway vector ($β’π=π+π / abs(π+πβ’))
π·GGXβ‘(π,π,πΌ) = πΌ^2 / πβ’((πβ π)2β’(πΌ2β1)+1)2β’
```glsl
float DistributionGGX(vec3 N, vec3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / max(denom, 0.000001);
}
```
-
geometry (πΊ): smiths schlick-ggx
microfacets can cast shadows and obscure light from the camera too, so the geometry function calculates the self shading factor using smiths method:
ββββ
πΊβ‘(π,π,π,π) =πΊ1β‘(π,π,π) β πΊ1β‘(π,π,π)
βββββ
where πβ’ maps roughness for direct lighting using β’π = (roughness+1)^2 / 8:
```glsl
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
```
-
fresnel equation (πΉβ‘): fresnel-schlick approximation
the fresnel factor calculates the percentage of light reflected versus absorbed based on the viewing angle (β’π):
βββ
πΉβ‘(π,πΉ0) =πΉ0 +(1βπΉ0)β’(1βcosβ‘π)^5
βββββ
nonmetals reflect around 4% of light (πΉ0 =0.04), while metals (conductors) use their tinted base albedo color as πΉ0:
```glsl
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
// lower, inside main():
vec3 F0 = mix(vec3(0.04), albedo, metallic);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
```
other stuff:
a surface cant reflect more light than it recieves, so a simple equation is used to make sure this doesnt happen:
βββββββ
β’## π€π =(1βπ€π) β
(1βmetallic)
βββββ
right now, all the material data is pulled from a .mtl file
i also added bindless ssbo storage so it doesnt explode when you have a lot of models
next, i might keep working on the custom persistent ui system
Comments 0
No comments yet. Be the first!
Sign in to join the conversation.