;;; Chickadee Game Toolkit ;;; Copyright © 2019, 2021 David Thompson ;;; ;;; Chickadee is free software: you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published ;;; by the Free Software Foundation, either version 3 of the License, ;;; or (at your option) any later version. ;;; ;;; Chickadee is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program. If not, see ;;; . ;;; Commentary: ;; ;; Simple forward rendered Phong lighting model. ;; ;;; Code: (define-module (chickadee graphics phong) #:use-module (chickadee math vector) #:use-module (chickadee graphics color) #:use-module (chickadee graphics engine) #:use-module (chickadee graphics shader) #:use-module (chickadee graphics texture) #:use-module (srfi srfi-9) #:export (make-phong-material phong-material? phong-material-name phong-material-ambient phong-material-ambient-map phong-material-use-ambient-map? phong-material-diffuse phong-material-diffuse-map phong-material-use-diffuse-map? phong-material-specular phong-material-specular-map? phong-material-use-specular-map? phong-material-specular-exponent phong-material-bump-map phong-material-use-bump-map? default-phong-material load-phong-shader shader-apply/phong)) ;;; ;;; Phong Material ;;; (define-shader-type make-phong-material phong-material? (local-field name phong-material-name) (float-vec3 ambient phong-material-ambient) (local-field ambient-map phong-material-ambient-map) (bool use-ambient-map phong-material-use-ambient-map?) (float-vec3 diffuse phong-material-diffuse) (local-field diffuse-map phong-material-diffuse-map) (bool use-diffuse-map phong-material-use-diffuse-map?) (float-vec3 specular phong-material-specular) (local-field specular-map phong-material-specular-map) (bool use-specular-map phong-material-use-specular-map?) (float shininess phong-material-shininess) (local-field bump-map phong-material-bump-map) (bool use-bump-map phong-material-use-bump-map?)) (define default-phong-material (make-phong-material #:name "default" #:ambient (vec3 0.5 0.5 0.5) #:ambient-map null-texture #:use-ambient-map #f #:diffuse (vec3 0.8 0.8 0.8) #:diffuse-map null-texture #:use-diffuse-map #f #:specular (vec3 0.3 0.3 0.3) #:specular-map null-texture #:use-specular-map #f #:shininess 32.0 #:bump-map null-texture #:use-bump-map #f)) ;;; ;;; Lights ;;; (define-shader-type 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 ;;; (define-graphics-variable phong-shader (strings->shader " #ifdef GLSL330 layout (location = 0) in vec3 position; layout (location = 1) in vec2 texcoord; layout (location = 2) in vec3 normal; #elif defined(GLSL130) in vec3 position; in vec2 texcoord; in vec3 normal; #elif defined(GLSL120) attribute vec3 position; attribute vec2 texcoord; attribute vec3 normal; #endif uniform mat4 model; uniform mat4 view; uniform mat4 projection; #ifdef GLSL120 varying vec3 fragNorm; varying vec2 fragTex; #else out vec3 fragNorm; out vec2 fragTex; #endif void main() { gl_Position = projection * view * model * vec4(position, 1.0); // TODO: Calculate normal matrix on CPU fragNorm = normalize(model * vec4(normal, 1.0)).xyz; fragTex = texcoord; } " " struct Material { vec3 ambient; sampler2D ambientMap; bool useAmbientMap; vec3 diffuse; sampler2D diffuseMap; bool useDiffuseMap; vec3 specular; sampler2D specularMap; bool useSpecularMap; float shininess; sampler2D bumpMap; bool useBumpMap; }; struct DirectionalLight { vec3 direction; vec3 ambient; vec3 diffuse; vec3 specular; }; #ifdef GLSL120 varying vec3 fragNorm; varying vec2 fragTex; #else in vec3 fragNorm; in vec2 fragTex; #endif #ifdef GLSL330 out vec4 fragColor; #endif uniform Material material; uniform DirectionalLight directionalLight; 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; #endif } else { baseAmbientColor = vec3(1.0, 1.0, 1.0); } 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; } else { baseDiffuseColor = vec3(1.0, 1.0, 1.0); } if(material.useSpecularMap) { #ifdef GLSL330 baseSpecularColor = texture(material.specularMap, fragTex).xyz; #else baseSpecularColor = texture2D(material.specularMap, fragTex).xyz; #endif } else { baseSpecularColor = vec3(1.0, 1.0, 1.0); } 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 specularColor = specularFactor * baseSpecularColor * material.specular; #ifdef GLSL330 fragColor = vec4(ambientColor + diffuseColor + specularColor, 1.0); #else gl_FragColor = vec4(ambientColor + diffuseColor + specularColor, 1.0); #endif } ")) (define (shader-apply/phong vertex-array material model-matrix view-matrix) (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)) (g:texture-2 (phong-material-specular-map material)) (g:texture-3 (phong-material-bump-map material))) (shader-apply shader vertex-array #:model model-matrix #:view view-matrix #:projection (current-projection) #:material material #:directional-light default-directional-light))))