diff options
-rw-r--r-- | chickadee/graphics/model.scm | 45 | ||||
-rw-r--r-- | chickadee/graphics/pbr.scm | 8 | ||||
-rw-r--r-- | data/shaders/pbr-frag.glsl | 138 | ||||
-rw-r--r-- | data/shaders/pbr-vert.glsl | 9 |
4 files changed, 131 insertions, 69 deletions
diff --git a/chickadee/graphics/model.scm b/chickadee/graphics/model.scm index e291f39..2b500c7 100644 --- a/chickadee/graphics/model.scm +++ b/chickadee/graphics/model.scm @@ -32,6 +32,7 @@ #:use-module (chickadee graphics color) #:use-module (chickadee graphics depth) #:use-module (chickadee graphics engine) + #:use-module (chickadee graphics light) #:use-module (chickadee graphics pbr) #:use-module (chickadee graphics phong) #:use-module (chickadee graphics shader) @@ -65,18 +66,28 @@ ;;; (define-record-type <render-state> - (%make-render-state renderer view-matrix model-matrix world-model-matrix) + (%make-render-state renderer view-matrix model-matrix world-model-matrix + camera-position lights ambient-light-color) render-state? (renderer render-state-renderer) (view-matrix render-state-view-matrix) (model-matrix render-state-model-matrix) - (world-model-matrix render-state-world-model-matrix)) + (world-model-matrix render-state-world-model-matrix) + (camera-position render-state-camera-position) + (lights render-state-lights) + (ambient-light-color render-state-ambient-light-color + set-render-state-ambient-light-color!)) + +(define %default-ambient-light-color (make-color 0.1 0.1 0.1 1.0)) (define (make-render-state renderer) (%make-render-state renderer (make-identity-matrix4) (make-identity-matrix4) - (make-identity-matrix4))) + (make-identity-matrix4) + (vec3 0.0 0.0 0.0) + (make-vector %max-lights %disabled-light) + %default-ambient-light-color)) (define (render-state-reset! state) (matrix4-identity! (render-state-view-matrix state)) @@ -95,6 +106,22 @@ matrix (render-state-model-matrix state))) +(define (set-render-state-camera-position! state position) + (vec3-copy! position (render-state-camera-position state))) + +(define (set-render-state-lights! state lights) + (let ((lv (render-state-lights state))) + (let loop ((i 0) + (lights lights)) + (when (< i %max-lights) + (match lights + (() + (vector-set! lv i %disabled-light) + (loop (+ i 1) '())) + ((light . rest) + (vector-set! lv i light) + (loop (+ i 1) rest))))))) + ;;; ;;; Primitive @@ -118,7 +145,10 @@ (shader-apply/pbr (primitive-vertex-array primitive) (primitive-material primitive) (render-state-world-model-matrix state) - (render-state-view-matrix state))) + (render-state-view-matrix state) + (render-state-camera-position state) + (render-state-lights state) + (render-state-ambient-light-color state))) ;;; @@ -190,12 +220,17 @@ (define %depth-test (make-depth-test)) -(define (draw-model model model-matrix view-matrix) +(define* (draw-model model model-matrix view-matrix camera-position #:key + (lights '()) + (ambient-light-color %default-ambient-light-color)) (with-graphics-state ((g:depth-test %depth-test)) (let ((state (model-render-state model))) (render-state-reset! state) (render-state-view-matrix-mult! state view-matrix) (render-state-model-matrix-mult! state model-matrix) + (set-render-state-camera-position! state camera-position) + (set-render-state-lights! state lights) + (set-render-state-ambient-light-color! state ambient-light-color) ;; TODO: Support drawing non-default scenes. (draw-model-node (model-default-scene model) state)))) diff --git a/chickadee/graphics/pbr.scm b/chickadee/graphics/pbr.scm index 836f8ee..0977579 100644 --- a/chickadee/graphics/pbr.scm +++ b/chickadee/graphics/pbr.scm @@ -120,7 +120,8 @@ (load-shader (scope-datadir "shaders/pbr-vert.glsl") (scope-datadir "shaders/pbr-frag.glsl"))) -(define (shader-apply/pbr vertex-array material model-matrix view-matrix) +(define (shader-apply/pbr vertex-array material model-matrix view-matrix + camera-position lights ambient-light-color) (let* ((shader (graphics-variable-ref pbr-shader)) (vattrs (vertex-array-attributes vertex-array)) (sattrs (shader-attributes shader))) @@ -143,4 +144,7 @@ (assv-ref vattrs (attribute-location (hash-ref sattrs "color0")))) - #:material material)))) + #:camera-position camera-position + #:material material + #:ambient-light-color ambient-light-color + #:lights lights)))) 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); } |