diff options
-rw-r--r-- | chickadee/graphics/model.scm | 5 | ||||
-rw-r--r-- | chickadee/graphics/phong.scm | 29 | ||||
-rw-r--r-- | data/shaders/phong-frag.glsl | 174 | ||||
-rw-r--r-- | data/shaders/phong-vert.glsl | 6 |
4 files changed, 148 insertions, 66 deletions
diff --git a/chickadee/graphics/model.scm b/chickadee/graphics/model.scm index 930687a..8636652 100644 --- a/chickadee/graphics/model.scm +++ b/chickadee/graphics/model.scm @@ -140,7 +140,10 @@ (shader-apply/phong (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))) (define (draw-primitive/pbr primitive state) (shader-apply/pbr (primitive-vertex-array primitive) diff --git a/chickadee/graphics/phong.scm b/chickadee/graphics/phong.scm index 372cdb6..f52f10b 100644 --- a/chickadee/graphics/phong.scm +++ b/chickadee/graphics/phong.scm @@ -26,6 +26,7 @@ #:use-module (chickadee math vector) #:use-module (chickadee graphics color) #:use-module (chickadee graphics engine) + #:use-module (chickadee graphics light) #:use-module (chickadee graphics shader) #:use-module (chickadee graphics texture) #:use-module (srfi srfi-9) @@ -87,27 +88,6 @@ ;;; -;;; Lights -;;; - -(define-shader-type <directional-light> - make-directional-light - directional-light? - (float-vec3 direction directional-light-direction) - (float-vec3 ambient directional-light-ambient) - (float-vec3 diffuse directional-light-diffuse) - (float-vec3 specular directional-light-specular) - (float shininess directional-light-shininess)) - -(define default-directional-light - (make-directional-light #:direction (vec3 0.0 0.0 -1.0) - #:ambient (vec3 0.1 0.1 0.1) - #:diffuse (vec3 1.0 1.0 1.0) - #:specular (vec3 0.5 0.5 0.5) - #:shininess 32.0)) - - -;;; ;;; Phong Shader ;;; @@ -115,7 +95,8 @@ (load-shader (scope-datadir "shaders/phong-vert.glsl") (scope-datadir "shaders/phong-frag.glsl"))) -(define (shader-apply/phong vertex-array material model-matrix view-matrix) +(define (shader-apply/phong vertex-array material model-matrix view-matrix + camera-position lights ambient-light-color) (let ((shader (graphics-variable-ref phong-shader))) (with-graphics-state ((g:texture-0 (phong-material-ambient-map material)) (g:texture-1 (phong-material-diffuse-map material)) @@ -126,4 +107,6 @@ #:view view-matrix #:projection (current-projection) #:material material - #:directional-light default-directional-light)))) + ;; #:camera-position camera-position + #:ambient-light-color ambient-light-color + #:lights lights)))) diff --git a/data/shaders/phong-frag.glsl b/data/shaders/phong-frag.glsl index ad324b3..84e9feb 100644 --- a/data/shaders/phong-frag.glsl +++ b/data/shaders/phong-frag.glsl @@ -15,17 +15,23 @@ struct Material { bool useBumpMap; }; -struct DirectionalLight { +struct Light { + bool enabled; + int type; + vec3 position; vec3 direction; - vec3 ambient; - vec3 diffuse; - vec3 specular; + vec4 color; + float cutOff; }; +#define MAX_LIGHTS 4 + #ifdef GLSL120 +varying vec3 fragWorldPos; varying vec3 fragNorm; varying vec2 fragTex; #else +in vec3 fragWorldPos; in vec3 fragNorm; in vec2 fragTex; #endif @@ -35,55 +41,143 @@ out vec4 fragColor; #endif uniform Material material; -uniform DirectionalLight directionalLight; +uniform Light lights[MAX_LIGHTS]; +uniform vec3 cameraPosition; +uniform vec4 ambientLightColor; -void main() { - vec3 baseAmbientColor; - vec3 baseDiffuseColor; - vec3 baseSpecularColor; - if(material.useAmbientMap) { -#ifdef GLSL330 - baseAmbientColor = texture(material.ambientMap, fragTex).xyz; -#else - baseAmbientColor = texture2D(material.ambientMap, fragTex).xyz; +const float GAMMA = 2.2; + +#ifndef GLSL330 +// Compatibility shim for older GLSL versions. +vec2 texture(sampler2D tex, vec2 coord) { + return texture2D(tex, coord); +} #endif + +float posDot(vec3 v1, vec3 v2) { + return max(dot(v1, v2), 0.0); +} + +vec3 gammaCorrect(vec3 color) { + return pow(color, vec3(1.0 / GAMMA)); +} + +vec3 toneMap(vec3 color) { + return color / (color + vec3(1.0)); +} + +vec3 lightDirection(Light light) { + if(light.type == 0 || light.type == 2) { // point and spot lights + return normalize(light.position - fragWorldPos); + } else if(light.type == 1) { // directional light + return normalize(-light.direction); + } + + return vec3(0.0); // should never be reached. +} + +vec3 lightAttenuate(Light light) { + float distance = length(light.position - fragWorldPos); + float attenuation = 1.0 / (distance * distance); + return light.color.rgb * attenuation; +} + +vec3 lightRadiance(Light light, vec3 direction) { + if(light.type == 0) { // point light + return lightAttenuate(light); + } else if(light.type == 1) { // directional light + return light.color.rgb; + } else if(light.type == 2) { // spot light + float theta = dot(direction, normalize(-light.direction)); + // Spot lights only shine light in a specific conical area. + // They have no effect outside of that area. + if(theta > light.cutOff) { + // Feather out the light as it approaches the edge of its cone. + float intensity = (theta - light.cutOff) / (1.0 - light.cutOff); + return lightAttenuate(light) * intensity; + } else { + return vec3(0.0); + } + } + + return vec3(0.0); // should never be reached. +} + +vec3 materialAmbient() { + if(material.useAmbientMap) { + return texture(material.ambientMap, fragTex).rgb * material.ambient; } else { - baseAmbientColor = vec3(1.0, 1.0, 1.0); + return material.ambient; } +} + +vec3 materialDiffuse() { if(material.useDiffuseMap) { - // discard transparent fragments. -#ifdef GLSL330 vec4 color = texture(material.diffuseMap, fragTex); -#else - vec4 color = texture2D(material.diffuseMap, fragTex); -#endif - if(color.a == 0.0) { discard; } - baseDiffuseColor = color.xyz; + // discard transparent fragments. + if(color.a == 0.0) { + discard; + } + return color.rgb * material.diffuse; } else { - baseDiffuseColor = vec3(1.0, 1.0, 1.0); + return material.diffuse; } +} + +vec3 materialSpecular() { if(material.useSpecularMap) { -#ifdef GLSL330 - baseSpecularColor = texture(material.specularMap, fragTex).xyz; -#else - baseSpecularColor = texture2D(material.specularMap, fragTex).xyz; -#endif + return texture(material.specularMap, fragTex).rgb * material.specular; } else { - baseSpecularColor = vec3(1.0, 1.0, 1.0); + return material.specular; } - vec3 ambientColor = material.ambient * baseAmbientColor * baseDiffuseColor; - vec3 lightDir = normalize(-directionalLight.direction); - float diffuseFactor = max(dot(lightDir, fragNorm), 0.0); - vec3 diffuseColor = diffuseFactor * baseDiffuseColor * material.diffuse; - vec3 reflectDir = reflect(-lightDir, fragNorm); - float specularFactor = 0; - if(material.shininess > 0) { - specularFactor = pow(max(dot(lightDir, reflectDir), 0.0), material.shininess); +} + +vec3 materialNormal() { + if(material.useBumpMap) { + return normalize(texture(material.bumpMap, fragTex).xyz * 2.0 - 1.0); + } else { + return fragNorm; } - vec3 specularColor = specularFactor * baseSpecularColor * material.specular; +} + +void main() { + vec3 ambientOcclusion = materialAmbient(); + vec3 baseDiffuseColor = materialDiffuse(); + vec3 baseSpecularColor = materialSpecular(); + vec3 normal = materialNormal(); + vec3 color = vec3(0.0); + + // Apply direct lighting. + for(int i = 0; i < MAX_LIGHTS; ++i) { + Light light = lights[i]; + + if(!light.enabled) { + continue; + } + + vec3 lightDir = lightDirection(light); + vec3 radiance = lightRadiance(light, lightDir); + float diffuseFactor = posDot(lightDir, normal); + vec3 reflectDir = reflect(-lightDir, normal); + vec3 diffuseColor = baseDiffuseColor * diffuseFactor; + float specularFactor = 0; + if(material.shininess > 0) { + specularFactor = pow(posDot(lightDir, reflectDir), material.shininess); + } + vec3 specularColor = baseSpecularColor * specularFactor; + color += (diffuseColor + specularColor) * radiance; + } + + // Apply ambient lighting. + vec3 ambientColor = baseDiffuseColor * ambientOcclusion * ambientLightColor.rgb; + color += ambientColor; + + // Apply gamma correction and HDR tone mapping to get the final + // color. + vec4 finalColor = vec4(toneMap(gammaCorrect(color)), 1.0); #ifdef GLSL330 - fragColor = vec4(ambientColor + diffuseColor + specularColor, 1.0); + fragColor = finalColor; #else - gl_FragColor = vec4(ambientColor + diffuseColor + specularColor, 1.0); + gl_FragColor = finalColor; #endif } diff --git a/data/shaders/phong-vert.glsl b/data/shaders/phong-vert.glsl index 51461fa..9b72eb3 100644 --- a/data/shaders/phong-vert.glsl +++ b/data/shaders/phong-vert.glsl @@ -15,9 +15,11 @@ attribute vec3 normal; #endif #ifdef GLSL120 +varying vec3 fragWorldPos; varying vec3 fragNorm; varying vec2 fragTex; #else +out vec3 fragWorldPos; out vec3 fragNorm; out vec2 fragTex; #endif @@ -27,8 +29,8 @@ uniform mat4 view; uniform mat4 projection; void main() { - gl_Position = projection * view * model * vec4(position, 1.0); - // TODO: Calculate normal matrix on CPU + fragWorldPos = vec3(model * vec4(position, 1.0)); fragNorm = normalize(model * vec4(normal, 1.0)).xyz; fragTex = texcoord; + gl_Position = projection * view * vec4(fragWorldPos, 1.0); } |