THREE.WebGLProgram = ( function () { var programIdCount = 0; function generateDefines( defines ) { var chunks = []; for ( var name in defines ) { var value = defines[ name ]; if ( value === false ) continue; chunks.push( '#define ' + name + ' ' + value ); } return chunks.join( '\n' ); } function fetchUniformLocations( gl, program, identifiers ) { var uniforms = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveUniform( program , i ); var name = info.name; var location = gl.getUniformLocation( program, name ); //console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); var suffixPos = name.lastIndexOf( '[0]' ); if ( suffixPos !== -1 && suffixPos === name.length - 3 ) { uniforms[ name.substr( 0, suffixPos ) ] = location; } uniforms[ name ] = location; } return uniforms; } function fetchAttributeLocations( gl, program, identifiers ) { var attributes = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveAttrib( program , i ); var name = info.name; //console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name); attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; } function filterEmptyLine( string ) { return string !== ''; } return function ( renderer, code, material, parameters ) { var gl = renderer.context; var defines = material.defines; var uniforms = material.__webglShader.uniforms; var attributes = material.attributes; var vertexShader = material.__webglShader.vertexShader; var fragmentShader = material.__webglShader.fragmentShader; var index0AttributeName = material.index0AttributeName; /* if ( index0AttributeName === undefined && parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 index0AttributeName = 'position'; } */ var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; if ( parameters.shadowMapType === THREE.PCFShadowMap ) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; } var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; if ( parameters.envMap ) { switch ( material.envMap.mapping ) { case THREE.CubeReflectionMapping: case THREE.CubeRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; break; case THREE.EquirectangularReflectionMapping: case THREE.EquirectangularRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; break; case THREE.SphericalReflectionMapping: envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; break; } switch ( material.envMap.mapping ) { case THREE.CubeRefractionMapping: case THREE.EquirectangularRefractionMapping: envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; break; } switch ( material.combine ) { case THREE.MultiplyOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; break; case THREE.MixOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; break; case THREE.AddOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; break; } } var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; // console.log( 'building new program ' ); // var customDefines = generateDefines( defines ); // var program = gl.createProgram(); var prefixVertex, prefixFragment; if ( material instanceof THREE.RawShaderMaterial ) { prefixVertex = ''; prefixFragment = ''; } else { prefixVertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, customDefines, parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', renderer.gammaInput ? '#define GAMMA_INPUT' : '', renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, '#define MAX_SHADOWS ' + parameters.maxShadows, '#define MAX_BONES ' + parameters.maxBones, parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.flatShading ? '#define FLAT_SHADED': '', parameters.skinning ? '#define USE_SKINNING' : '', parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', parameters.morphNormals ? '#define USE_MORPHNORMALS' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'uniform mat3 normalMatrix;', 'uniform vec3 cameraPosition;', 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', '#ifdef USE_COLOR', ' attribute vec3 color;', '#endif', '#ifdef USE_MORPHTARGETS', ' attribute vec3 morphTarget0;', ' attribute vec3 morphTarget1;', ' attribute vec3 morphTarget2;', ' attribute vec3 morphTarget3;', ' #ifdef USE_MORPHNORMALS', ' attribute vec3 morphNormal0;', ' attribute vec3 morphNormal1;', ' attribute vec3 morphNormal2;', ' attribute vec3 morphNormal3;', ' #else', ' attribute vec3 morphTarget4;', ' attribute vec3 morphTarget5;', ' attribute vec3 morphTarget6;', ' attribute vec3 morphTarget7;', ' #endif', '#endif', '#ifdef USE_SKINNING', ' attribute vec4 skinIndex;', ' attribute vec4 skinWeight;', '#endif', '\n' ].filter( filterEmptyLine ).join( '\n' ); prefixFragment = [ ( parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ) ? '#extension GL_OES_standard_derivatives : enable' : '', 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, customDefines, '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, '#define MAX_SHADOWS ' + parameters.maxShadows, parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', renderer.gammaInput ? '#define GAMMA_INPUT' : '', renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapTypeDefine : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.flatShading ? '#define FLAT_SHADED': '', parameters.metal ? '#define METAL' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', '\n' ].filter( filterEmptyLine ).join( '\n' ); } var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; var glVertexShader = new THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); var glFragmentShader = new THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); if ( index0AttributeName !== undefined ) { // Force a particular attribute to index 0. // because potentially expensive emulation is done by browser if attribute 0 is disabled. // And, color, for example is often automatically bound to index 0 so disabling it gl.bindAttribLocation( program, 0, index0AttributeName ); } gl.linkProgram( program ); var programLogInfo = gl.getProgramInfoLog( program ); var vertexErrorLogInfo = gl.getShaderInfoLog( glVertexShader ); var fragmentErrorLogInfo = gl.getShaderInfoLog( glFragmentShader ); if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLogInfo, vertexErrorLogInfo, fragmentErrorLogInfo ); } else if ( programLogInfo !== '' ) { console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLogInfo ); } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations var getUniforms = function() { return this._cachedUniforms; }; this.getUniforms = function() { // fetch, cache, and next time just use a dumb accessor var uniforms = fetchUniformLocations( gl, program ); this._cachedUniforms = uniforms; this.getUniforms = getUniforms; return uniforms; }; // set up caching for attribute locations var getAttributes = function() { return this._cachedAttributes; }; this.getAttributes = function() { var attributes = fetchAttributeLocations( gl, program ); this._cachedAttributes = attributes; this.getAttributes = getAttributes; return attributes; }; // DEPRECATED Object.defineProperties( this, { uniforms: { get: function() { console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); return this.getUniforms(); } }, attributes: { get: function() { console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); return this.getAttributes(); } } }); // this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; }; } )();