THREE.WebGLProgram = ( function () { var programIdCount = 0; var generateDefines = function ( defines ) { var value, chunk, chunks = []; for ( var d in defines ) { value = defines[ d ]; if ( value === false ) continue; chunk = '#define ' + d + ' ' + value; chunks.push( chunk ); } return chunks.join( '\n' ); }; var cacheUniformLocations = function ( gl, program, identifiers ) { var uniforms = {}; for ( var i = 0, l = identifiers.length; i < l; i ++ ) { var id = identifiers[ i ]; uniforms[ id ] = gl.getUniformLocation( program, id ); } return uniforms; }; var cacheAttributeLocations = function ( gl, program, identifiers ) { var attributes = {}; for ( var i = 0, l = identifiers.length; i < l; i ++ ) { var id = identifiers[ i ]; attributes[ id ] = gl.getAttribLocation( program, id ); } return attributes; }; return function ( renderer, code, material, parameters ) { var _this = renderer; var _gl = _this.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 prefix_vertex, prefix_fragment; if ( material instanceof THREE.RawShaderMaterial ) { prefix_vertex = ''; prefix_fragment = ''; } else { prefix_vertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', customDefines, parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', _this.gammaInput ? '#define GAMMA_INPUT' : '', _this.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.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.wrapAround ? '#define WRAP_AROUND' : '', 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' : '', //_this._glExtensionFragDepth ? '#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;', 'attribute vec2 uv2;', '#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', '' ].join( '\n' ); prefix_fragment = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', ( parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', 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 : '', _this.gammaInput ? '#define GAMMA_INPUT' : '', _this.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.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.wrapAround ? '#define WRAP_AROUND' : '', 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' : '', //_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', '' ].join( '\n' ); } var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); _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 ); if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { THREE.error( 'THREE.WebGLProgram: shader error: ' + _gl.getError(), 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ), 'gl.getPRogramInfoLog', programLogInfo ); } if ( programLogInfo !== '' ) { THREE.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()' + programLogInfo ); // THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); // THREE.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); } // clean up _gl.deleteShader( glVertexShader ); _gl.deleteShader( glFragmentShader ); // cache uniform locations var identifiers = [ 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', 'morphTargetInfluences', 'bindMatrix', 'bindMatrixInverse' ]; if ( parameters.useVertexTexture ) { identifiers.push( 'boneTexture' ); identifiers.push( 'boneTextureWidth' ); identifiers.push( 'boneTextureHeight' ); } else { identifiers.push( 'boneGlobalMatrices' ); } if ( parameters.logarithmicDepthBuffer ) { identifiers.push('logDepthBufFC'); } for ( var u in uniforms ) { identifiers.push( u ); } this.uniforms = cacheUniformLocations( _gl, program, identifiers ); // cache attributes locations identifiers = [ 'position', 'normal', 'uv', 'uv2', 'tangent', 'color', 'skinIndex', 'skinWeight', 'lineDistance' ]; for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { identifiers.push( 'morphTarget' + i ); } for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { identifiers.push( 'morphNormal' + i ); } for ( var a in attributes ) { identifiers.push( a ); } this.attributes = cacheAttributeLocations( _gl, program, identifiers ); this.attributesKeys = Object.keys( this.attributes ); // this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; }; } )();