0.00
60.0 fps

Custom PBR Lighting 2.0

Trying to make the cheapest PBR model

Log in to post a comment.

#version 300 es
precision highp float;

uniform float iTime;
uniform vec2  iResolution;

uniform vec3 baseColor; // value=.75,0,0
uniform vec3 specularColor; // value=.1,.1,.09
uniform vec3 ambientColor; // value=0,0,.1
uniform vec3 lightColor; // value=1,1,1
uniform float lightAzimuth; // value=0.69, min=0, max=6.28318530718, step=0.03141592653
uniform float lightZenith; // value=1.10, min=0, max=3.14159265359, step=0.03141592653
uniform float roughness; // value=0.1, min=0, max=1, step=0.01

in vec2 vScreen; // screen coords
out vec4 fragColor;

// GLOBAL VARIABLES
vec3 cameraPos = vec3(0.0, 0.0, 3.0);
vec4 sphere = vec4(0.0, 0.0, 0.0, 0.5); // x, y, z, radius
vec3 fragPos;
vec3 viewDir;
vec3 lightDir;

vec3 fresnelSchlick(float cosTheta, vec3 F0) {
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

vec4 customPBR(vec3 normal, vec3 lightDir, vec3 viewDir) {
    vec3 N = normalize(normal);
    vec3 L = normalize(lightDir);
    vec3 V = normalize(viewDir);
    vec3 H = normalize(L + V);
    
    float NdotL = dot(N, L);
    vec3 R = reflect(-L, N);
    float VdotR = dot(V, R);
    
    // power–law specular highlight distribution
    float cosTheta = VdotR * 0.5 + 0.5; // increase positive value range
    float alpha2 = pow(roughness, 4.0);
    float numerator = 1.0 / (alpha2 + 0.0000001) + 1.0;
    float specularFactor = numerator / 6.28318530718 * pow(cosTheta, numerator - 2.0);
    
    if(NdotL < 0.0) {
        specularFactor = 0.0;
    }
    NdotL = max(NdotL, 0.0);
    
    // Adjust Fresnel with roughness
    vec3 fresnel = fresnelSchlick(NdotL, specularColor);
    
    // scale light intensity based on the projected area size
    vec3 inLight = lightColor * NdotL;
    
    // Split incoming light into specular and diffuse components
    vec3 specularLight = inLight * fresnel * specularFactor + ambientColor * specularColor;
    vec3 transmittedLight = inLight * (1.0 - fresnel) + ambientColor * (1.0 - specularColor);
    
    // Split transmitted light into scattered and absorbed
    vec3 diffuseLight = transmittedLight * baseColor;
    
    vec3 color = diffuseLight + specularLight;
    return vec4(color, 1.0);
}

// https://www.shadertoy.com/view/4d2XWV by Inigo Quilez
float sphereIntersect(vec3 ro, vec3 rd, vec4 sph) {
    vec3 oc = ro - sph.xyz;            // Vector from sphere center to ray origin
    float b = dot(oc, rd);              // Projection of oc onto rd
    float c = dot(oc, oc) - sph.w * sph.w; // Squared distance from ro to sphere surface minus radius²
    float h = b * b - c;                // Discriminant of the quadratic equation
    if (h < 0.0) return -1.0;           // No intersection if the discriminant is negative
    return -b - sqrt(h);                // Return the smallest positive intersection distance
}


vec4 sphereShaders(vec4 color) {
    // RAY CALCULATIONS
    vec3 rd = -viewDir;
    float d = sphereIntersect(cameraPos, rd, sphere);
    if(d < 0.0) return color;
    
    vec3 normal = cameraPos + d * rd;
    return customPBR(normal, lightDir, viewDir);
}

vec4 backgroundShaders(vec4 color) {
    if(dot(-viewDir, lightDir) > 0.999)
        return vec4(vec3(lightColor), 1.0);
    
    return vec4(vec3(0.1), 1.0);
}

void main() {
    // SETUP
    fragPos = vec3(vScreen, 0.0);
    viewDir = normalize(cameraPos - fragPos);
    lightDir = vec3(
        sin(lightZenith) * cos(lightAzimuth),
        cos(lightZenith),
        sin(lightZenith) * sin(lightAzimuth)
    );
    
    // SHADER PASSES
    vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
    color = mix(sphereShaders(color),       color, color.a);
    color = mix(backgroundShaders(color),   color, color.a);
    fragColor = color;
}