top of page

Physically Based Shaders (GLSL)

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


bottom of page