Physically Based Shaders (GLSL)
- Morgan Herrmann
- Apr 17, 2024
- 2 min read
While Monte Carlo path tracing gives us highly accurate results, estimating the Light Transport Integral offers efficiency benefits over path tracing, particularly in real-time rendering. Leveraging a deterministic microfacet BSDF model enables realistic material representation, including properties like plasticness, metallicness, and surface roughness--creating renders that are both visually accurate and computationally efficient.
// The following is a PBR fragment shader, an example scene with 4 point lights.
// Input variables representing fragment position and normal
in vec4 fs_Pos;
in vec3 fs_Nor;
// Uniform variables representing camera position, material properties, and ambient occlusion
uniform vec3 cam_pos;
uniform vec3 albedo; // Surface color
uniform float metallic; // Metallic property of the material
uniform float roughness; // Roughness property of the material
uniform float ao; // Ambient occlusion factor
// Output variable representing final color
out vec4 out_color;
// Define positions and colors of four point lights
const vec3 light_pos[4] = {a, b, c, d};
const vec3 light_color[4] = {ac, bc, cc, dc};
void main() {
vec3 Lo = vec3(0.0); // Initialize radiance at the fragment to zero
for(int i = 0; i < 4; ++i) { // Loop through each point light
vec3 irradiance = light_color[i]; // Get the color of the light
vec3 l_pos = light_pos[i]; // Get the position of the light
// Compute light falloff based on distance
vec3 diff = l_pos - fs_Pos;
float falloff = 1.0 / dot(diff, diff);
irradiance *= falloff; // Apply falloff to irradiance
// Compute incident and outgoing light directions
vec3 wi = normalize(diff);
vec3 wo = normalize(cam_pos - fs_Pos);
vec3 wh = normalize(wo + wi);
// Compute Fresnel term
vec3 R = mix(vec3(0.04), albedo, metallic);
vec3 F = fresnel(max(dot(fs_Nor, wo), 0.0), R);
// Compute geometry and distribution terms
float G = geom(wo, wi, fs_Nor, roughness);
float D = distribFunc(fs_Nor, wh, roughness);
// Compute Cook-Torrance specular term
vec3 f_cook_torrance = D * G * F / (4.f * dot(fs_Nor, wo) * dot(fs_Nor, wi));
// Compute diffuse and specular components
vec3 ks = F;
vec3 kd = vec3(1.0) - ks;
kd *= (1.0 - metallic);
// Compute Lambertian diffuse term
vec3 f_lambert = albedo * 0.31831015504887652430775499030746; // albedo / PI
// Combine diffuse and specular components
vec3 f = kd * f_lambert + f_cook_torrance;
// Accumulate radiance from this light
Lo += f * irradiance * abs(dot(wi, fs_Nor));
}
// Apply Reinhard tone mapping
Lo = reinhard(Lo);
// Apply gamma correction
Lo = gammaCorrect(Lo);
// Output final color
out_color = vec4(Lo, 1.0);
}
Comentarios