From c7b59a5b66edd96024495b7d94641b9a857a97ef Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 11 May 2021 09:09:46 -0400 Subject: graphics: model: Add really rough sketch of PBR lighting model. --- data/shaders/pbr-frag.glsl | 217 +++++++++++++++++++++++++++++++++++++++------ data/shaders/pbr-vert.glsl | 17 +++- 2 files changed, 204 insertions(+), 30 deletions(-) (limited to 'data/shaders') diff --git a/data/shaders/pbr-frag.glsl b/data/shaders/pbr-frag.glsl index 4c569fc..73a6840 100644 --- a/data/shaders/pbr-frag.glsl +++ b/data/shaders/pbr-frag.glsl @@ -1,11 +1,15 @@ // -*- mode: c -*- +// Heavily based upon: https://learnopengl.com/PBR/Lighting + struct Material { vec3 baseColorFactor; bool baseColorTextureEnabled; int baseColorTexcoord; float metallicFactor; float roughnessFactor; + bool metallicRoughnessTextureEnabled; + int metallicRoughnessTexcoord; vec3 normalFactor; bool normalTextureEnabled; int normalTexcoord; @@ -20,10 +24,16 @@ struct Material { }; #ifdef GLSL120 +attribute vec3 fragWorldPos; +attribute vec3 fragCamPos; +attribute vec3 fragNormal; attribute vec2 fragTexcoord0; attribute vec2 fragTexcoord1 attribute vec4 fragColor0; #else +in vec3 fragWorldPos; +in vec3 fragCamPos; +in vec3 fragNormal; in vec2 fragTexcoord0; in vec2 fragTexcoord1; in vec4 fragColor0; @@ -36,10 +46,13 @@ out vec4 fragColor; uniform Material material; uniform bool vertexColored; uniform sampler2D baseColorTexture; +uniform sampler2D metallicRoughnessTexture; uniform sampler2D normalTexture; uniform sampler2D occlusionTexture; uniform sampler2D emissiveTexture; +const float PI = 3.14159265359; + vec4 sampleTexture(sampler2D tex, bool enabled, int texcoord, vec3 factor, vec4 defaultColor) { if(enabled && texcoord == 0) { return texture(tex, fragTexcoord0) * vec4(factor, 1.0); @@ -50,13 +63,53 @@ vec4 sampleTexture(sampler2D tex, bool enabled, int texcoord, vec3 factor, vec4 } } +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(max(1.0 - cosTheta, 0.0), 5.0); +} + +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 / denom; +} + +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; +} + vec4 applyAlpha(vec4 color) { // Apply alpha mode. if(material.alphaMode == 0) { // opaque - return vec4(color.xyz, 1.0); + return vec4(color.rgb, 1.0); } else if(material.alphaMode == 1) { // mask if(color.a >= material.alphaCutoff) { - return vec4(color.xyz, 1.0); + return vec4(color.rgb, 1.0); } else { discard; } @@ -69,35 +122,145 @@ vec4 applyAlpha(vec4 color) { } } -void main(void) { - vec4 finalColor = sampleTexture(baseColorTexture, - material.baseColorTextureEnabled, - material.baseColorTexcoord, - material.baseColorFactor, - vec4(0.0, 0.0, 1.0, 1.0)); +vec2 texcoord(int i) { + if(i == 0) { + return fragTexcoord0; + } else { + return fragTexcoord1; + } +} + +float materialMetallic() { + float m = material.metallicFactor; + + if(material.metallicRoughnessTextureEnabled) { + m *= texture2D(metallicRoughnessTexture, + texcoord(material.metallicRoughnessTexcoord)).b; + } + + return m; +} + +float materialRoughness() { + float r = material.roughnessFactor; + + if(material.metallicRoughnessTextureEnabled) { + r *= texture2D(metallicRoughnessTexture, + texcoord(material.metallicRoughnessTexcoord)).g; + } + + return r; +} + +vec4 materialAlbedo() { + vec4 color = vec4(0.0, 0.0, 1.0, 1.0); + + if(material.baseColorTextureEnabled) { + color = texture2D(baseColorTexture, + texcoord(material.baseColorTexcoord)); + } + + color *= vec4(material.baseColorFactor, 1.0); - // Mix in vertex color, if present. if(vertexColored) { - finalColor *= fragColor0; + color *= fragColor0; + } + + return color; +} + +vec3 materialOcclusion() { + vec3 color = vec3(0.0); + + if(material.occlusionTextureEnabled) { + color = texture2D(occlusionTexture, + texcoord(material.occlusionTexcoord)).rgb; + } + + return color * material.occlusionFactor; +} + +vec3 materialNormal() { + vec3 normal; + + if(material.normalTextureEnabled) { + normal = texture2D(normalTexture, + texcoord(material.normalTexcoord)).rgb * 2.0 - 1.0; + } else { + normal = fragNormal; + } + + return normalize(normal); +} + +struct PointLight { + vec3 position; + vec3 color; +}; + +PointLight lights[1]; +vec3 ambientLightColor = vec3(0.05); + +void main(void) { + // TODO: Support user supplied lights. + lights[0].position = vec3(0.0, 0.2, 2.0); + lights[0].color = vec3(1.0, 1.0, 1.0); + + float metallic = materialMetallic(); + float roughness = materialRoughness(); + vec3 N = materialNormal(); + vec3 V = normalize(-fragCamPos - fragWorldPos); + vec3 albedo = materialAlbedo().rgb; + vec3 ao = materialOcclusion(); + vec3 F0 = mix(vec3(0.4), albedo, metallic); + + // reflectance equation + vec3 Lo = vec3(0.0); + for(int i = 0; i < 1; ++i) + { + PointLight light = lights[i]; + // calculate per-light radiance + vec3 L = normalize(light.position - fragWorldPos); + vec3 H = normalize(V + L); + float distance = length(light.position - fragWorldPos); + float attenuation = 1.0 / (distance * distance); + vec3 radiance = light.color * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; } - // TODO: Actually apply PBR calculations. - finalColor += sampleTexture(normalTexture, - material.normalTextureEnabled, - material.normalTexcoord, - material.normalFactor, - vec4(0.0, 0.0, 0.0, 0.0)) * 0.1; - finalColor += sampleTexture(occlusionTexture, - material.occlusionTextureEnabled, - material.occlusionTexcoord, - material.occlusionFactor, - vec4(0.0, 0.0, 0.0, 0.0)) * 0.1; - finalColor += sampleTexture(emissiveTexture, - material.emissiveTextureEnabled, - material.emissiveTexcoord, - material.emissiveFactor, - vec4(0.0, 0.0, 0.0, 0.0)); - finalColor = applyAlpha(finalColor); + // TODO: Process emissive color properly. + Lo += sampleTexture(emissiveTexture, + material.emissiveTextureEnabled, + material.emissiveTexcoord, + material.emissiveFactor, + vec4(0.0, 0.0, 0.0, 0.0)).xyz; + + // Apply ambient lighting. + vec3 ambient = ambientLightColor * albedo * ao; + vec3 color = ambient + Lo; + + // Apply HDR. + color = color / (color + vec3(1.0)); + color = pow(color, vec3(1.0 / 2.2)); + + // TODO: Preserve alpha channel throughout lighting. + vec4 finalColor = applyAlpha(vec4(color, 1.0)); #ifdef GLSL330 fragColor = finalColor; diff --git a/data/shaders/pbr-vert.glsl b/data/shaders/pbr-vert.glsl index f622988..530c564 100644 --- a/data/shaders/pbr-vert.glsl +++ b/data/shaders/pbr-vert.glsl @@ -2,11 +2,13 @@ #ifdef GLSL330 layout (location = 0) in vec3 position; -layout (location = 1) in vec2 texcoord0; -layout (location = 2) in vec2 texcoord1; -layout (location = 3) in vec4 color0; +layout (location = 1) in vec3 normal; +layout (location = 2) in vec2 texcoord0; +layout (location = 3) in vec2 texcoord1; +layout (location = 4) in vec4 color0; #elif defined(GLSL130) in vec3 position; +in vec3 normal; in vec2 texcoord0; in vec2 texcoord1; in vec4 color0; @@ -18,10 +20,16 @@ attribute vec4 color0; #endif #ifdef GLSL120 +varying vec3 fragWorldPos; +varying vec3 fragCamPos; +varying vec3 fragNormal; varying vec2 fragTexcoord0; varying vec2 fragTexcoord1; varying vec4 fragColor0; #else +out vec3 fragWorldPos; +out vec3 fragCamPos; +out vec3 fragNormal; out vec2 fragTexcoord0; out vec2 fragTexcoord1; out vec4 fragColor0; @@ -32,6 +40,9 @@ uniform mat4 view; uniform mat4 projection; void main(void) { + fragWorldPos = (model * vec4(position.xyz, 1.0)).xyz; + fragCamPos = (view * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + fragNormal = normal; fragTexcoord0 = texcoord0; fragTexcoord1 = texcoord1; fragColor0 = color0; -- cgit v1.2.3