From f88da6fb6d50568d8d1889cca3f9ed32c4451a06 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Wed, 12 May 2021 20:49:48 -0400 Subject: graphics: model: Add support for dynamic lights in PBR models. --- data/shaders/pbr-frag.glsl | 138 +++++++++++++++++++++++++++------------------ data/shaders/pbr-vert.glsl | 9 +-- 2 files changed, 85 insertions(+), 62 deletions(-) (limited to 'data/shaders') diff --git a/data/shaders/pbr-frag.glsl b/data/shaders/pbr-frag.glsl index 73a6840..b025bb7 100644 --- a/data/shaders/pbr-frag.glsl +++ b/data/shaders/pbr-frag.glsl @@ -13,7 +13,6 @@ struct Material { vec3 normalFactor; bool normalTextureEnabled; int normalTexcoord; - vec3 occlusionFactor; bool occlusionTextureEnabled; int occlusionTexcoord; vec3 emissiveFactor; @@ -23,16 +22,23 @@ struct Material { float alphaCutoff; }; +struct Light { + bool enabled; + int type; + vec3 position; + vec3 direction; + vec4 color; + float cutOff; +}; + #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; @@ -44,7 +50,10 @@ out vec4 fragColor; #endif uniform Material material; +uniform Light lights[4]; +uniform vec4 ambientLightColor; uniform bool vertexColored; +uniform vec3 cameraPosition; uniform sampler2D baseColorTexture; uniform sampler2D metallicRoughnessTexture; uniform sampler2D normalTexture; @@ -53,16 +62,6 @@ 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); - } else if(enabled && texcoord == 1) { - return texture(tex, fragTexcoord1) * vec4(factor, 1.0); - } else { - return defaultColor; - } -} - vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(max(1.0 - cosTheta, 0.0), 5.0); @@ -130,12 +129,19 @@ vec2 texcoord(int i) { } } +vec4 sRGBtoLinear(vec4 srgb) { + return vec4(pow(srgb.r, 2.2), + pow(srgb.g, 2.2), + pow(srgb.b, 2.2), + srgb.a); +} + float materialMetallic() { float m = material.metallicFactor; if(material.metallicRoughnessTextureEnabled) { - m *= texture2D(metallicRoughnessTexture, - texcoord(material.metallicRoughnessTexcoord)).b; + m *= texture(metallicRoughnessTexture, + texcoord(material.metallicRoughnessTexcoord)).b; } return m; @@ -145,8 +151,8 @@ float materialRoughness() { float r = material.roughnessFactor; if(material.metallicRoughnessTextureEnabled) { - r *= texture2D(metallicRoughnessTexture, - texcoord(material.metallicRoughnessTexcoord)).g; + r *= texture(metallicRoughnessTexture, + texcoord(material.metallicRoughnessTexcoord)).g; } return r; @@ -156,8 +162,9 @@ vec4 materialAlbedo() { vec4 color = vec4(0.0, 0.0, 1.0, 1.0); if(material.baseColorTextureEnabled) { - color = texture2D(baseColorTexture, - texcoord(material.baseColorTexcoord)); + vec4 texColor = texture(baseColorTexture, + texcoord(material.baseColorTexcoord)); + color = sRGBtoLinear(texColor); } color *= vec4(material.baseColorFactor, 1.0); @@ -169,23 +176,33 @@ vec4 materialAlbedo() { return color; } -vec3 materialOcclusion() { - vec3 color = vec3(0.0); +vec4 materialEmissive() { + vec4 color = vec4(0.0); - if(material.occlusionTextureEnabled) { - color = texture2D(occlusionTexture, - texcoord(material.occlusionTexcoord)).rgb; + if(material.emissiveTextureEnabled) { + vec4 texColor = texture(emissiveTexture, + texcoord(material.emissiveTexcoord)); + color = sRGBtoLinear(texColor); } - return color * material.occlusionFactor; + return color * vec4(material.emissiveFactor, 1.0); +} + +vec3 materialOcclusion() { + if(material.occlusionTextureEnabled) { + return vec3(texture(occlusionTexture, + texcoord(material.occlusionTexcoord)).r); + } else { + return vec3(1.0); + } } vec3 materialNormal() { vec3 normal; if(material.normalTextureEnabled) { - normal = texture2D(normalTexture, - texcoord(material.normalTexcoord)).rgb * 2.0 - 1.0; + normal = texture(normalTexture, + texcoord(material.normalTexcoord)).rgb * 2.0 - 1.0; } else { normal = fragNormal; } @@ -193,38 +210,52 @@ vec3 materialNormal() { 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 V = normalize(cameraPosition - fragWorldPos); + vec4 rawAlbedo = materialAlbedo(); + vec3 albedo = rawAlbedo.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) + for(int i = 0; i < 4; ++i) { - PointLight light = lights[i]; + Light light = lights[i]; + + if(!light.enabled) { + continue; + } + + vec3 L; + vec3 radiance; + // calculate per-light radiance - vec3 L = normalize(light.position - fragWorldPos); + if(light.type == 0) { // point light + L = normalize(light.position - fragWorldPos); + float distance = length(light.position - fragWorldPos); + float attenuation = 1.0 / (distance * distance); + radiance = light.color.rgb * attenuation; + } else if(light.type == 1) { // directional light + L = normalize(-light.direction); + radiance = light.color.rgb; + } else if(light.type == 2) { // spotlight + L = normalize(light.position - fragWorldPos); + float theta = dot(L, normalize(-light.direction)); + + if(theta > light.cutOff) { + float distance = length(light.position - fragWorldPos); + float attenuation = 1.0 / (distance * distance); + radiance = light.color.rgb * attenuation; + } else { + continue; + } + } + 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); @@ -244,23 +275,18 @@ void main(void) { Lo += (kD * albedo / PI + specular) * radiance * NdotL; } - // TODO: Process emissive color properly. - Lo += sampleTexture(emissiveTexture, - material.emissiveTextureEnabled, - material.emissiveTexcoord, - material.emissiveFactor, - vec4(0.0, 0.0, 0.0, 0.0)).xyz; + // Add emissive color. + Lo += materialEmissive().rgb; // Apply ambient lighting. - vec3 ambient = ambientLightColor * albedo * ao; + vec3 ambient = ambientLightColor.xyz * 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)); + vec4 finalColor = applyAlpha(vec4(color, rawAlbedo.a)); #ifdef GLSL330 fragColor = finalColor; diff --git a/data/shaders/pbr-vert.glsl b/data/shaders/pbr-vert.glsl index 530c564..bf43e81 100644 --- a/data/shaders/pbr-vert.glsl +++ b/data/shaders/pbr-vert.glsl @@ -21,14 +21,12 @@ attribute vec4 color0; #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; @@ -40,11 +38,10 @@ 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; + fragWorldPos = vec3(model * vec4(position, 1.0)); + fragNormal = mat3(model) * normal; fragTexcoord0 = texcoord0; fragTexcoord1 = texcoord1; fragColor0 = color0; - gl_Position = projection * view * model * vec4(position.xyz, 1.0); + gl_Position = projection * view * vec4(fragWorldPos, 1.0); } -- cgit v1.2.3