diff --git a/src/index.html b/src/index.html index d02e512a776d06df273422fceecb580868173185..b147d3469d021a7db33dbb16e03e74eca56c1bd1 100644 --- a/src/index.html +++ b/src/index.html @@ -5,6 +5,45 @@ <link rel="stylesheet" href="lib/css/bootstrap.min.css"> <link rel="stylesheet" href="index.css"> <link rel="stylesheet" href="index-mobile.css"> +<script id="vertex-shader-shadow-map" type="x-shader/x-vertex"> + precision mediump float; + + uniform mat4 mProj; + uniform mat4 mView; + uniform mat4 mWorld; + + attribute vec4 vPos; + + varying vec3 fPos; + + void main() + { + fPos = (mWorld * vPos).xyz; + + gl_Position = mProj * mView * vec4(fPos, 1.0); + } +</script> +<script id="fragment-shader-shadow-map" type="x-shader/x-fragment"> + precision mediump float; + + uniform vec3 pointLightPosition; + uniform vec2 shadowClipNearFar; + + varying vec3 fPos; + + void main() + { + vec3 fromLightToFrag = (fPos - pointLightPosition); + + float lightFragDist = + (length(fromLightToFrag) - shadowClipNearFar.x) + / + (shadowClipNearFar.y - shadowClipNearFar.x); + + gl_FragColor = vec4(lightFragDist, lightFragDist, lightFragDist, 1.0); + } +</script> + <script id="vertex-shader" type="x-shader/x-vertex"> attribute vec4 vPosition; attribute vec3 vNormal; @@ -97,9 +136,6 @@ void main() } </script> - - - <script id="vertex-shader-3d" type="x-shader/x-vertex"> attribute vec4 vPosition; attribute vec2 a_texcoord; diff --git a/src/modules/brand-new-class.js b/src/modules/brand-new-class.js index 705d2bbb71db49fcc76cdcba393c87a411e72efc..9f62f0121821e8368ae4607992f5925ee4719dc0 100644 --- a/src/modules/brand-new-class.js +++ b/src/modules/brand-new-class.js @@ -65,7 +65,6 @@ class Geometry { return } - console.log('update buffers') let gl = renderer.gl let attributes = renderer.attribs @@ -94,7 +93,7 @@ class Geometry { gl.vertexAttribPointer(attributes.vNormal, 4, gl.FLOAT, false, 0, 0) gl.enableVertexAttribArray(attributes.vNormal) - Geometry.bufferDataNeedsUpdate = false + Geometry.bufferDataNeedsUpdate = true } /** @@ -950,12 +949,37 @@ class Light extends Object3D { } +var Camera = function (position, lookAt, up) { + this.forward = vec3(); + this.up = vec3(); + this.right = vec3(); + + this.position = position; + + this.forward = subtract(lookAt, this.position); + this.right = cross(this.forward, up); + this.up = cross(this.right, this.forward); + + this.forward = normalize(this.forward); + this.right = normalize(this.right); + this.up = normalize(this.up); +}; + +Camera.prototype.GetViewMatrix = function (out) { + var lookAtVec = vec3(); + lookAtVec = add(this.position, this.forward); + out = lookAt(this.position, lookAtVec, this.up); + return out; +}; + + class Renderer { - constructor(canvas) { + constructor(canvas, textureSize = 128) { this.canvas = canvas + this.textureSize = textureSize this.gl = null this.program = null this.textureProgram = null @@ -1009,8 +1033,10 @@ class Renderer { gl.enable(gl.DEPTH_TEST) + this.textureProgram = initShaders(gl, 'vertex-shader-3d', 'fragment-shader-3d') this.program = initShaders(gl, 'vertex-shader', 'fragment-shader') + this.shadowMapProgram = initShaders(gl, 'vertex-shader-shadow-map', 'fragment-shader-shadow-map') gl.useProgram(this.program) } @@ -1021,6 +1047,37 @@ class Renderer { this.verticesBuffer = gl.createBuffer() this.normalsBuffer = gl.createBuffer() this.texcoordsBuffer = gl.createBuffer() + + + this.shadowMapCube = this.gl.createTexture(); + this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.shadowMapCube); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, this.gl.TEXTURE_WRAP_S, this.gl.MIRRORED_REPEAT); + this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP, this.gl.TEXTURE_WRAP_T, this.gl.MIRRORED_REPEAT); + for (var i = 0; i < 6; i++) { + this.gl.texImage2D( + this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, + 0, this.gl.RGBA, + this.textureSize, this.textureSize, + 0, this.gl.RGBA, + this.gl.UNSIGNED_BYTE, null + ); + } + + this.shadowMapFramebuffer = this.gl.createFramebuffer(); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.shadowMapFramebuffer); + + this.shadowMapRenderbuffer = this.gl.createRenderbuffer(); + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.shadowMapRenderbuffer); + this.gl.renderbufferStorage( + this.gl.RENDERBUFFER, this.gl.DEPTH_COMPONENT16, + this.textureSize, this.textureSize + ); + + this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, null); + this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); + this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); } @@ -1032,6 +1089,16 @@ class Renderer { this.uniformList.forEach(uniformName => { uniforms[uniformName] = gl.getUniformLocation(program, uniformName) }) + this.gl.useProgram(this.shadowMapProgram); + this.shadowMapProgram.uniforms = { + mProj: this.gl.getUniformLocation(this.shadowMapProgram, 'mProj'), + mView: this.gl.getUniformLocation(this.shadowMapProgram, 'mView'), + mWorld: this.gl.getUniformLocation(this.shadowMapProgram, 'mWorld'), + pointLightPosition: this.gl.getUniformLocation(this.shadowMapProgram, 'pointLightPosition'), + shadowClipNearFar: this.gl.getUniformLocation(this.shadowMapProgram, 'shadowClipNearFar'), + }; + console.log('pointLightPosition: ', this.gl.getUniformLocation(this.shadowMapProgram, 'pointLightPosition')); + this.gl.useProgram(this.program); } @@ -1043,6 +1110,10 @@ class Renderer { this.attributeList.forEach(attribName => { attribs[attribName] = gl.getAttribLocation(program, attribName) }) + + this.shadowMapProgram.attribs = { + vPos: this.gl.getAttribLocation(this.shadowMapProgram, 'vPos'), + }; } @@ -1067,8 +1138,167 @@ class Renderer { self.renderObjectTree(object, camera, app) }) + this.renderShadowMap(scene, camera, app); + this.gl.useProgram(this.program); + } + + renderShadowMap(scene, camera, app) { + this.gl.useProgram(this.shadowMapProgram) + + + this.lightPosition = app.objects['cube-lighting'].position.property + this.shadowMapCameras = [ + // Positive X + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(1, 0, 0)), + vec3(0, -1, 0) + ), + // Negative X + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(-1, 0, 0)), + vec3(0, -1, 0) + ), + // Positive Y + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(0, 1, 0)), + vec3(0, 0, 1) + ), + // Negative Y + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(0, -1, 0)), + vec3(0, 0, -1) + ), + // Positive Z + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(0, 0, 1)), + vec3(0, -1, 0) + ), + // Negative Z + new Camera( + this.lightPosition, + add(this.lightPosition, vec3(0, 0, -1)), + vec3(0, -1, 0) + ), + ]; + this.shadowMapViewMatrices = [ + mat4(), + mat4(), + mat4(), + mat4(), + mat4(), + mat4() + ]; + this.shadowClipNearFar = vec2(0.05, 15.0); + this.shadowMapProj = perspective( + degToRad(90), + 1.0, + this.shadowClipNearFar[0], + this.shadowClipNearFar[1] + ); + + + var gl = this.gl; + + // Set GL state status + gl.useProgram(this.shadowMapProgram); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, this.shadowMapCube); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.shadowMapFramebuffer); + gl.bindRenderbuffer(gl.RENDERBUFFER, this.shadowMapRenderbuffer); + + gl.enable(gl.DEPTH_TEST); + gl.enable(gl.CULL_FACE); + + // Set per-frame uniforms + gl.uniform2fv( + this.shadowMapProgram.uniforms.shadowClipNearFar, + flatten(this.shadowClipNearFar) + ); + gl.uniform3fv( + this.shadowMapProgram.uniforms.pointLightPosition, + flatten(this.lightPosition) + ); + gl.uniformMatrix4fv( + this.shadowMapProgram.uniforms.mProj, + gl.FALSE, + flatten(this.shadowMapProj) + ); + + for (var i = 0; i < this.shadowMapCameras.length; i++) { + // Set per light uniforms + gl.useProgram(this.shadowMapProgram) + + gl.uniformMatrix4fv( + this.shadowMapProgram.uniforms.mView, + gl.FALSE, + flatten(this.shadowMapCameras[i].GetViewMatrix(this.shadowMapViewMatrices[i])) + ); + + // Set framebuffer destination + gl.framebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, + this.shadowMapCube, + 0 + ); + gl.framebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, + this.shadowMapRenderbuffer + ); + + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.verticesBuffer) + gl.bufferData(gl.ARRAY_BUFFER, flatten(Geometry.verticesBufferData), gl.STATIC_DRAW) + + gl.vertexAttribPointer(this.shadowMapProgram.attribs.vPos, 4, gl.FLOAT, false, 0, 0) + gl.enableVertexAttribArray(this.shadowMapProgram.attribs.vPos) + + scene.children.forEach(object => { + this.renderShadowTree(object, camera, app) + }) + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); + }; + + /** + * Render this object and all of its children recursively + * while performing operations. + * + * @param {*} object + * @param {*} camera + * @param {*} app + */ + + renderShadowTree(object, camera, app) { + let gl = this.gl + gl.useProgram(this.shadowMapProgram) + const self = this + self.renderShadow(object, camera, app) + object.children.forEach(child => { + self.renderShadowTree(child, camera, app) + }) } + renderShadow(object) { + let gl = this.gl + gl.uniformMatrix4fv(this.shadowMapProgram.uniforms.mWorld, gl.FALSE, flatten(object.worldMatrix)) + let geometry = object.geometry + let start = geometry.bufferStartIndex + let count = geometry.triangleVerticesCount + gl.drawArrays(gl.TRIANGLES, start, count) + } /** * Render this object and all of its children recursively @@ -1079,9 +1309,9 @@ class Renderer { * @param {*} app */ - renderObjectTree(object, camera, app) { + renderObjectTree(object, camera, app, program) { let gl = this.gl - + gl.useProgram(this.program) const self = this self.renderObject(object, camera, app) object.children.forEach(child => { diff --git a/src/resources/objects/custom-objects.js b/src/resources/objects/custom-objects.js index 408a133d85a0eefd702f12014b97aa023c9f7f19..ebcf63654be99d2459ed0bf0a672a1ed712fb593 100644 --- a/src/resources/objects/custom-objects.js +++ b/src/resources/objects/custom-objects.js @@ -7,66 +7,11 @@ var [LENGTH, WIDTH, HEIGHT] = [10.0, 10.0, 10.0]; objects_info = { ...objects_info, - "Cube": { - "position": [ - 0.0, - 0.0, - 0.0 - ], - "material_name": "cubes", - "rotation": [ - 0.0, - 0.0, - 0.0 - ], - "scale": [ - 1.0, - 1.0, - 1.0 - ] - }, - "Cube.001": { - "position": [ - 2.525, - -1.463, - -0.45 - ], - "material_name": "cubes", - "parent": "Cube", - "rotation": [ - 0.0, - 0.0, - 0.0 - ], - "scale": [ - 0.5456840991973877, - 0.5456840991973877, - 0.5456840991973877 - ] - }, - "Suzanne": { - "position": [ - 0.886, - -2.385, - 1.582 - ], - "material_name": "monkey", - "rotation": [ - 0.0, - 0.0, - 0.0 - ], - "scale": [ - 0.8567630648612976, - 0.8567630648612976, - 0.8567630648612976 - ] - }, "floor": { "position": [ 0.0, -3.0, - -1.0 + -10.0 ], "material_name": "floor", "rotation": [ @@ -102,7 +47,7 @@ objects_info = { objects_info.floor = { ...objects_info.floor, - position: [0.0, 0.0, -1.0], + position: [0.0, 0.0, -5.0], scale: [LENGTH, WIDTH, 1], };