summaryrefslogtreecommitdiff
path: root/data/shaders/pbr-frag.glsl
diff options
context:
space:
mode:
authorDavid Thompson <dthompson2@worcester.edu>2021-05-11 09:09:46 -0400
committerDavid Thompson <dthompson2@worcester.edu>2021-05-11 09:09:46 -0400
commitc7b59a5b66edd96024495b7d94641b9a857a97ef (patch)
tree95d003f177bb7825b31984e609ed6f7eb2a9756a /data/shaders/pbr-frag.glsl
parent685368435ca9962ca402f2bcf2f50c8dc850065a (diff)
graphics: model: Add really rough sketch of PBR lighting model.
Diffstat (limited to 'data/shaders/pbr-frag.glsl')
-rw-r--r--data/shaders/pbr-frag.glsl217
1 files changed, 190 insertions, 27 deletions
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;