#version 300 es
precision highp float;
// Input variables
in vec3 v_position; // The position of the current fragment in world space
in vec3 v_normal; // The normal vector of the current fragment
in vec2 v_texcoord; // The texture coordinates of the current fragment
// Technical: These are interpolated values passed from the vertex shader
// ELI5: Imagine these as little notes passed to each tiny dot on your screen, telling it where it is and which way it's facing
// Output variable
out vec4 fragColor; // The final color of the fragment
// Technical: This is the color we'll calculate and output for this pixel
// ELI5: This is like choosing the final crayon color for each dot in our picture
// Material properties
uniform vec3 u_albedo; // The base color of the material
uniform float u_metallic; // How metallic the material is (0 to 1)
uniform float u_roughness; // How rough the material is (0 to 1)
// Technical: These define the core properties of our material for PBR calculations
// ELI5: These tell us what the object is made of - its color, if it's like metal, and how smooth or bumpy it is
// Light properties
uniform vec3 u_lightPosition; // The position of the light in world space
uniform vec3 u_lightColor; // The color of the light
// Technical: These define our light source for illumination calculations
// ELI5: This is like saying where we put our lamp and what color it glows
// Camera position
uniform vec3 u_viewPosition; // The position of the camera in world space
// Technical: This is used to calculate view direction for specular reflections
// ELI5: This tells us where we're looking from, like where our eyes are in the scene
// Constants
const float PI = 3.14159265359;
// Technical: Mathematical constant used in various calculations
// ELI5: This is a special number that helps us do math with circles and roundness
// Function to calculate the distribution of microfacets (GGX/Trowbridge-Reitz)
float DistributionGGX(vec3 N, vec3 H, float roughness) {
// Technical: This function models how light scatters on a microscopically rough surface
// ELI5: This helps us figure out how light bounces off tiny bumps we can't see
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
// Technical: We're calculating the probability of microfacets being aligned to reflect light towards the viewer
// ELI5: We're guessing how many tiny mirrors on the surface are pointing the right way to shine light at us
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / max(denom, 0.0000001);
// Technical: The result is the relative surface area of microfacets contributing to reflection
// ELI5: This tells us how much of the bumpy surface is helping to make things look shiny
}
// Function to calculate geometric occlusion (Smith's Schlick-GGX)
float GeometrySchlickGGX(float NdotV, float roughness) {
// Technical: This function models how microfacets shadow and mask each other
// ELI5: This helps us understand how tiny bumps might block light from reaching other bumps
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
// Technical: The result is the proportion of microfacets that are not shadowed or masked
// ELI5: This tells us how much of the surface we can actually see, considering all the tiny hills and valleys
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
// Technical: This combines geometry terms for both view and light directions
// ELI5: This looks at how bumps affect light coming in AND going out
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;
// Technical: The combined geometry term for the BRDF
// ELI5: This gives us one number that tells us how much the bumpiness affects the overall shininess
}
// Function to calculate the Fresnel effect (Schlick approximation)
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
// Technical: This approximates how reflectivity changes with viewing angle
// ELI5: This helps us see how the shininess changes when you look at something from different angles
return F0 + (1.0 - F0) * pow(max(1.0 - cosTheta, 0.0), 5.0);
// Technical: Interpolates between base reflectivity and full reflection based on view angle
// ELI5: This decides how mirror-like the surface looks depending on how you're looking at it
}
void main() {
// Normalize vectors
vec3 N = normalize(v_normal);
vec3 V = normalize(u_viewPosition - v_position);
vec3 L = normalize(u_lightPosition - v_position);
vec3 H = normalize(V + L);
// Technical: Ensuring all direction vectors have unit length for correct calculations
// ELI5: We're making sure all our arrows pointing in different directions are the same size
// Calculate reflectance at normal incidence
vec3 F0 = vec3(0.04);
F0 = mix(F0, u_albedo, u_metallic);
// Technical: Sets base reflectivity, interpolating between dielectric and metallic
// ELI5: This decides how shiny the surface is when you look straight at it, based on if it's metal or not
// Calculate radiance
float distance = length(u_lightPosition - v_position);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = u_lightColor * attenuation;
// Technical: Computes incoming light intensity considering distance falloff
// ELI5: This figures out how bright the light is when it reaches our surface, knowing it gets dimmer farther away
// Calculate Cook-Torrance BRDF
float NDF = DistributionGGX(N, H, u_roughness);
float G = GeometrySmith(N, V, L, u_roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
// Technical: Combines microfacet distribution, geometry, and Fresnel terms
// ELI5: We're putting together all our information about bumpiness, shadowing, and shininess
vec3 nominator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
vec3 specular = nominator / max(denominator, 0.001);
// Technical: Computes the specular BRDF term
// ELI5: This calculates how much light bounces off in a mirror-like way
// Calculate the contribution of diffuse and specular to the final color
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - u_metallic;
// Technical: Balances between specular and diffuse reflection based on Fresnel and metallicness
// ELI5: This decides how much light bounces off directly and how much scatters inside before coming out
float NdotL = max(dot(N, L), 0.0);
// Combine diffuse and specular lighting
vec3 Lo = (kD * u_albedo / PI + specular) * radiance * NdotL;
// Technical: Combines diffuse and specular components with incoming light
// ELI5: We're mixing the scattered light and the mirror-like reflections based on how much light is hitting the surface
// Add ambient lighting
vec3 ambient = vec3(0.03) * u_albedo;
vec3 color = ambient + Lo;
// Technical: Adds a simple ambient term to approximate global illumination
// ELI5: We're adding a little bit of light everywhere to fake the bouncing of light in the real world
// Tone mapping and gamma correction
color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));
// Technical: Applies HDR tone mapping and gamma correction for display
// ELI5: We're adjusting the colors so they look right on your screen, not too bright or too dark
fragColor = vec4(color, 1.0);
// Technical: Sets the final output color with full opacity
// ELI5: We're putting our final crayon color onto the paper, making sure it's not see-through
}
Created
October 7, 2024 21:38
-
-
Save anhedonix/9a93d320d7ed239d1acc9cf7b708901c to your computer and use it in GitHub Desktop.
Fragment shaders detailed explanation
Author
anhedonix
commented
Oct 7, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment