From 8a1e3bc8b26fb79ce11516eb116504124ebea824 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 3 Nov 2017 18:49:25 -0700 Subject: [PATCH] Add some basic Phong shading to the monument --- demo/client/src/3d-demo.ts | 116 +++++++++++++++++-------- shaders/gles2/demo-3d-monument.fs.glsl | 27 +++++- shaders/gles2/demo-3d-monument.vs.glsl | 9 +- 3 files changed, 110 insertions(+), 42 deletions(-) diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts index e90b964a..c2d4ba4e 100644 --- a/demo/client/src/3d-demo.ts +++ b/demo/client/src/3d-demo.ts @@ -28,7 +28,8 @@ import {BUILTIN_FONT_URI, ExpandedMeshData} from "./text"; import {calculatePixelRectForGlyph, GlyphStore, Hint, PathfinderFont} from "./text"; import {SimpleTextLayout, TextFrame, TextRun, UnitMetrics} from "./text"; import {TextRenderContext, TextRenderer} from './text-renderer'; -import {assert, FLOAT32_SIZE, panic, PathfinderError, Range, unwrapNull} from "./utils"; +import {assert, FLOAT32_SIZE, panic, PathfinderError, Range, UINT16_SIZE} from "./utils"; +import {unwrapNull} from "./utils"; import {DemoView, Timings} from "./view"; const TEXT_AVAILABLE_WIDTH: number = 150000; @@ -63,7 +64,12 @@ const MONUMENT_SCALE: glmatrix.vec3 = (TEXT_AVAILABLE_WIDTH * 0.5 + TEXT_PADDING) * TEXT_SCALE[2]); const TEXT_COLOR: Uint8Array = new Uint8Array([0xf2, 0xf8, 0xf8, 0xff]); -const MONUMENT_COLOR: number[] = [0x70 / 0xff, 0x80 / 0xff, 0x80 / 0xff]; + +const AMBIENT_COLOR: glmatrix.vec3 = glmatrix.vec3.clone([0.063, 0.063, 0.063]); +const DIFFUSE_COLOR: glmatrix.vec3 = glmatrix.vec3.clone([0.356, 0.264, 0.136]); +const SPECULAR_COLOR: glmatrix.vec3 = glmatrix.vec3.clone([0.490, 0.420, 0.324]); + +const MONUMENT_SHININESS: number = 32.0; const CUBE_VERTEX_POSITIONS: Float32Array = new Float32Array([ -1.0, -1.0, -1.0, // 0 @@ -85,6 +91,15 @@ const CUBE_INDICES: Uint16Array = new Uint16Array([ 4, 5, 6, 6, 5, 7, // top ]); +const MONUMENT_NORMALS: glmatrix.vec4[] = [ + glmatrix.vec4.clone([ 0.0, -1.0, 0.0, 1.0]), + glmatrix.vec4.clone([ 0.0, 0.0, -1.0, 1.0]), + glmatrix.vec4.clone([-1.0, 0.0, 0.0, 1.0]), + glmatrix.vec4.clone([ 1.0, 0.0, 0.0, 1.0]), + glmatrix.vec4.clone([ 0.0, 0.0, 1.0, 1.0]), + glmatrix.vec4.clone([ 0.0, 1.0, 0.0, 1.0]), +]; + const ANTIALIASING_STRATEGIES: AntialiasingStrategyTable = { none: NoAAStrategy, ssaa: SSAAStrategy, @@ -393,22 +408,20 @@ class ThreeDRenderer extends Renderer { constructor(renderContext: ThreeDView) { super(renderContext); + const gl = renderContext.gl; + this.camera = new PerspectiveCamera(renderContext.canvas, { innerCollisionExtent: MONUMENT_SCALE[0], }); this.camera.onChange = () => renderContext.setDirty(); - this.cubeVertexPositionBuffer = unwrapNull(renderContext.gl.createBuffer()); - renderContext.gl.bindBuffer(renderContext.gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer); - renderContext.gl.bufferData(renderContext.gl.ARRAY_BUFFER, - CUBE_VERTEX_POSITIONS, - renderContext.gl.STATIC_DRAW); + this.cubeVertexPositionBuffer = unwrapNull(gl.createBuffer()); + gl.bindBuffer(gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, CUBE_VERTEX_POSITIONS, gl.STATIC_DRAW); - this.cubeIndexBuffer = unwrapNull(renderContext.gl.createBuffer()); - renderContext.gl.bindBuffer(renderContext.gl.ELEMENT_ARRAY_BUFFER, this.cubeIndexBuffer); - renderContext.gl.bufferData(renderContext.gl.ELEMENT_ARRAY_BUFFER, - CUBE_INDICES, - renderContext.gl.STATIC_DRAW); + this.cubeIndexBuffer = unwrapNull(gl.createBuffer()); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.cubeIndexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, CUBE_INDICES, gl.STATIC_DRAW); } attachMeshes(expandedMeshes: PathfinderMeshData[]) { @@ -597,31 +610,33 @@ class ThreeDRenderer extends Renderer { } private drawMonument(): void { - const gl = this.renderContext.gl; + const renderContext = this.renderContext; + const gl = renderContext.gl; // Set up the cube VBO. const monumentProgram = this.renderContext.shaderPrograms.demo3DMonument; - this.renderContext.gl.useProgram(monumentProgram.program); - this.renderContext.gl.bindBuffer(this.renderContext.gl.ARRAY_BUFFER, - this.cubeVertexPositionBuffer); - this.renderContext.gl.bindBuffer(this.renderContext.gl.ELEMENT_ARRAY_BUFFER, - this.cubeIndexBuffer); - this.renderContext.gl.vertexAttribPointer(monumentProgram.attributes.aPosition, - 3, - this.renderContext.gl.FLOAT, - false, - 0, - 0); - this.renderContext.gl.enableVertexAttribArray(monumentProgram.attributes.aPosition); + gl.useProgram(monumentProgram.program); + gl.bindBuffer(gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.cubeIndexBuffer); + gl.vertexAttribPointer(monumentProgram.attributes.aPosition, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(monumentProgram.attributes.aPosition); // Set uniforms for the monument. - const transform = this.calculateWorldTransform(MONUMENT_TRANSLATION, MONUMENT_SCALE); - gl.uniformMatrix4fv(monumentProgram.uniforms.uTransform, false, transform); - gl.uniform4f(monumentProgram.uniforms.uColor, - MONUMENT_COLOR[0], - MONUMENT_COLOR[1], - MONUMENT_COLOR[2], - 1.0); + const projection = this.calculateProjectionTransform(); + const modelview = this.calculateModelviewTransform(MONUMENT_TRANSLATION, MONUMENT_SCALE); + gl.uniformMatrix4fv(monumentProgram.uniforms.uProjection, false, projection); + gl.uniformMatrix4fv(monumentProgram.uniforms.uModelview, false, modelview); + const cameraModelview = this.calculateCameraModelviewTransform(); + const lightPosition = glmatrix.vec4.clone([-1750.0, -700.0, 1750.0, 1.0]); + glmatrix.vec4.transformMat4(lightPosition, lightPosition, cameraModelview); + gl.uniform3f(monumentProgram.uniforms.uLightPosition, + lightPosition[0] / lightPosition[3], + lightPosition[1] / lightPosition[3], + lightPosition[2] / lightPosition[3]); + gl.uniform3fv(monumentProgram.uniforms.uAmbientColor, AMBIENT_COLOR); + gl.uniform3fv(monumentProgram.uniforms.uDiffuseColor, DIFFUSE_COLOR); + gl.uniform3fv(monumentProgram.uniforms.uSpecularColor, SPECULAR_COLOR); + gl.uniform1f(monumentProgram.uniforms.uShininess, MONUMENT_SHININESS); // Set state for the monument. gl.enable(gl.DEPTH_TEST); @@ -630,8 +645,19 @@ class ThreeDRenderer extends Renderer { gl.disable(gl.SCISSOR_TEST); gl.disable(gl.BLEND); - // Draw the monument! - gl.drawElements(gl.TRIANGLES, CUBE_INDICES.length, gl.UNSIGNED_SHORT, 0); + // Loop over each face. + for (let face = 0; face < 6; face++) { + // Set the uniforms for this face. + const normal = glmatrix.vec4.clone(MONUMENT_NORMALS[face]); + glmatrix.vec4.transformMat4(normal, normal, this.camera.rotationMatrix); + gl.uniform3f(monumentProgram.uniforms.uNormal, + normal[0] / normal[3], + normal[1] / normal[3], + normal[2] / normal[3]); + + // Draw the face! + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, face * 6 * UINT16_SIZE); + } } private drawDistantGlyphs(): void { @@ -760,9 +786,7 @@ class ThreeDRenderer extends Renderer { this.renderContext.vertexArrayObjectExt.bindVertexArrayOES(null); } - private calculateWorldTransform(modelviewTranslation: glmatrix.vec3, - modelviewScale: glmatrix.vec3): - glmatrix.mat4 { + private calculateProjectionTransform(): glmatrix.mat4 { const canvas = this.renderContext.canvas; const projection = glmatrix.mat4.create(); glmatrix.mat4.perspective(projection, @@ -770,12 +794,30 @@ class ThreeDRenderer extends Renderer { canvas.width / canvas.height, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); + return projection; + } + private calculateCameraModelviewTransform(): glmatrix.mat4 { const modelview = glmatrix.mat4.create(); glmatrix.mat4.mul(modelview, modelview, this.camera.rotationMatrix); glmatrix.mat4.translate(modelview, modelview, this.camera.translation); + return modelview; + } + + private calculateModelviewTransform(modelviewTranslation: glmatrix.vec3, + modelviewScale: glmatrix.vec3): + glmatrix.mat4 { + const modelview = this.calculateCameraModelviewTransform(); glmatrix.mat4.translate(modelview, modelview, modelviewTranslation); glmatrix.mat4.scale(modelview, modelview, modelviewScale); + return modelview; + } + + private calculateWorldTransform(modelviewTranslation: glmatrix.vec3, + modelviewScale: glmatrix.vec3): + glmatrix.mat4 { + const projection = this.calculateProjectionTransform(); + const modelview = this.calculateModelviewTransform(modelviewTranslation, modelviewScale); const transform = glmatrix.mat4.create(); glmatrix.mat4.mul(transform, projection, modelview); diff --git a/shaders/gles2/demo-3d-monument.fs.glsl b/shaders/gles2/demo-3d-monument.fs.glsl index 66697d1e..5d4ad431 100644 --- a/shaders/gles2/demo-3d-monument.fs.glsl +++ b/shaders/gles2/demo-3d-monument.fs.glsl @@ -10,9 +10,30 @@ precision mediump float; -uniform vec4 uColor; +uniform vec3 uLightPosition; +uniform vec3 uAmbientColor; +uniform vec3 uDiffuseColor; +uniform vec3 uSpecularColor; +uniform float uShininess; + +uniform vec3 uNormal; + +varying vec3 vPosition; void main() { - // TODO(pcwalton): Lighting. - gl_FragColor = uColor; + vec3 normal = normalize(uNormal); + vec3 lightDirection = normalize(uLightPosition - vPosition); + + float lambertian = max(dot(lightDirection, normal), 0.0); + float specular = 0.0; + + if (lambertian > 0.0) { + vec3 viewDirection = normalize(-vPosition); + vec3 halfDirection = normalize(lightDirection + viewDirection); + float specularAngle = max(dot(halfDirection, normal), 0.0); + specular = pow(specularAngle, uShininess); + } + + vec3 color = uAmbientColor + lambertian * uDiffuseColor + specular * uSpecularColor; + gl_FragColor = vec4(color, 1.0); } diff --git a/shaders/gles2/demo-3d-monument.vs.glsl b/shaders/gles2/demo-3d-monument.vs.glsl index 729a702b..d8e66de8 100644 --- a/shaders/gles2/demo-3d-monument.vs.glsl +++ b/shaders/gles2/demo-3d-monument.vs.glsl @@ -10,10 +10,15 @@ precision mediump float; -uniform mat4 uTransform; +uniform mat4 uProjection; +uniform mat4 uModelview; attribute vec3 aPosition; +varying vec3 vPosition; + void main() { - gl_Position = uTransform * vec4(aPosition, 1.0); + vec4 position = uModelview * vec4(aPosition, 1.0); + vPosition = position.xyz / position.w; + gl_Position = uProjection * position; }