UNPKG

voxel-shader

Version:

shader for voxel-mesher with ambient occulusion (voxel.js plugin)

183 lines (153 loc) 6.49 kB
var glslify = require("glslify") var fs = require("fs") var glShader = require('gl-shader') var mat4 = require('gl-mat4') var inherits = require('inherits'); var EventEmitter = require('events').EventEmitter; module.exports = function(game, opts) { return new ShaderPlugin(game, opts); }; module.exports.pluginInfo = { clientOnly: true, loadAfter: ['voxel-stitch', 'game-shell-fps-camera'], }; function ShaderPlugin(game, opts) { this.shell = game.shell; this.stitcher = game.plugins.get('voxel-stitch'); if (!this.stitcher) throw new Error('voxel-shader requires voxel-stitch plugin'); // for tileCount uniform and updateTexture event this.meshes = opts.meshes || game.voxels.meshes if (!this.meshes) throw new Error('voxel-shader requires "meshes" option or game.voxels.meshes set to array of voxel-mesher meshes') if (game.getCamera) { this.camera = game.getCamera() if (!(this.camera && this.camera.view)) throw new Error('voxel-shader requires a camera with a view matrix'); // for camera view matrix } else { this.camera = game.plugins.get('game-shell-fps-camera'); if (!this.camera) throw new Error('voxel-shader requires game-shell-fps-camera plugin'); // for camera view matrix } this.perspectiveResize = opts.perspectiveResize !== undefined ? opts.perspectiveResize : true; this.cameraNear = opts.cameraNear !== undefined ? opts.cameraNear : 0.1; this.cameraFar = opts.cameraFar !== undefined ? opts.cameraFar : 200.0; this.cameraFOV = opts.cameraFOV !== undefined ? opts.cameraFOV : 45.0; this.projectionMatrix = mat4.create(); this.enable(); } inherits(ShaderPlugin, EventEmitter); ShaderPlugin.prototype.enable = function() { this.on('updateProjectionMatrix', this.perspective.bind(this)); this.shell.on('gl-init', this.onInit = this.ginit.bind(this)); this.shell.on('gl-render', this.onRender = this.render.bind(this)); if (this.perspectiveResize) this.shell.on('gl-resize', this.onResize = this.updateProjectionMatrix.bind(this)); this.stitcher.on('updateTexture', this.onUpdateTexture = this.texturesReady.bind(this)); }; ShaderPlugin.prototype.disable = function() { this.shell.removeListener('gl-init', this.onInit); this.shell.removeListener('gl-render', this.onRender); if (this.onResize) this.shell.removeListener('gl-resize', this.onResize); this.stitcher.removeListener('updateTexture', this.onUpdateTexture); }; ShaderPlugin.prototype.texturesReady = function(texture) { this.texture = texture; // used in tileMap uniform } ShaderPlugin.prototype.ginit = function() { this.shader = this.createAOShader(); this.shader2 = this.createCustomModelShader(); this.updateProjectionMatrix(); this.viewMatrix = mat4.create(); }; ShaderPlugin.prototype.perspective = function(out) { mat4.perspective(out, this.cameraFOV*Math.PI/180, this.shell.width/this.shell.height, this.cameraNear, this.cameraFar) }; ShaderPlugin.prototype.updateProjectionMatrix = function() { this.emit('updateProjectionMatrix', this.projectionMatrix); }; ShaderPlugin.prototype.render = function() { var gl = this.shell.gl this.camera.view(this.viewMatrix) gl.enable(gl.CULL_FACE) gl.enable(gl.DEPTH_TEST) //Bind the shader // phase 1 - solid blocks gl.disable(gl.BLEND) var shader = this.shader if (!shader) throw new Error('voxel-shader render() called before gl-init, shader=', this.shader) shader.bind() shader.attributes.attrib0.location = 0 shader.attributes.attrib1.location = 1 shader.uniforms.projection = this.projectionMatrix shader.uniforms.view = this.viewMatrix shader.uniforms.tileCount = this.stitcher.tileCount if (this.texture) shader.uniforms.tileMap = this.texture.bind() // if a texture is loaded var keys = Object.keys(this.meshes) var length = keys.length for (var k = 0; k < length; ++k) { var chunkIndex = keys[k] var mesh = this.meshes[chunkIndex] var triangleVAO = mesh.vertexArrayObjects.surface if (triangleVAO && triangleVAO.length !== 0) { // if there are triangles to render shader.uniforms.model = mesh.modelMatrix triangleVAO.bind() gl.drawArrays(gl.TRIANGLES, 0, triangleVAO.length) triangleVAO.unbind() } } // phase 2 - "porous" blocks gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) // TODO: premult alpha? https://github.com/deathcap/voxel-stitch/issues/6 gl.enable(gl.BLEND) var shader2 = this.shader2 shader2.bind() shader2.attributes.position.location = 0 shader2.uniforms.view = this.viewMatrix shader2.uniforms.projection = this.projectionMatrix if (this.texture) shader2.uniforms.texture = this.texture.bind() for (k = 0; k < length; ++k) { var chunkIndex = keys[k] var mesh = this.meshes[chunkIndex] var blockMesh = mesh.vertexArrayObjects.porous if (blockMesh && blockMesh.length !== 0) { shader2.uniforms.model = this.meshes[chunkIndex].modelMatrix blockMesh.bind() blockMesh.draw(gl.TRIANGLES, blockMesh.length) blockMesh.unbind() } } }; ShaderPlugin.prototype.createAOShader = function() { return glShader(this.shell.gl , glslify('./lib/ao.vsh'), glslify('./lib/ao.fsh') ) // return glslify({ // vertex: './lib/ao.vsh', // fragment: './lib/ao.fsh' // })(this.shell.gl) }; ShaderPlugin.prototype.createCustomModelShader = function() { // TODO: refactor with voxel-decals, voxel-chunkborder? return glShader(this.shell.gl, glslify( "\ attribute vec3 position;\ attribute vec2 uv;\ \ uniform mat4 projection;\ uniform mat4 view;\ uniform mat4 model;\ varying vec2 vUv;\ \ void main() {\ gl_Position = projection * view * model * vec4(position, 1.0);\ vUv = uv;\ }", {inline: true}), glslify( "\ precision highp float;\ \ uniform sampler2D texture;\ varying vec2 vUv;\ \ void main() {\ gl_FragColor = texture2D(texture, vUv);\ }", {inline: true})); };