/** * @author mrdoob / http://mrdoob.com/ */ import { BackSide, DoubleSide, FlatShading, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding, FloatType, RGBAFormat } from '../../constants'; import { _Math } from '../../math/Math'; import { DataTexture } from '../../textures/DataTexture'; import { WebGLProgram } from './WebGLProgram'; function WebGLPrograms( renderer, capabilities ) { var programs = []; var shaderIDs = { MeshDepthMaterial: 'depth', MeshNormalMaterial: 'normal', MeshBasicMaterial: 'basic', MeshLambertMaterial: 'lambert', MeshPhongMaterial: 'phong', MeshToonMaterial: 'phong', MeshStandardMaterial: 'physical', MeshPhysicalMaterial: 'physical', LineBasicMaterial: 'basic', LineDashedMaterial: 'dashed', PointsMaterial: 'points' }; var parameterNames = [ "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking" ]; function allocateBones( object ) { var skeleton = object.skeleton; var bones = skeleton.bones; if ( capabilities.floatVertexTextures ) { if ( skeleton.boneTexture === undefined ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix size = _Math.nextPowerOfTwo( Math.ceil( size ) ); size = Math.max( size, 4 ); var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel boneMatrices.set( skeleton.boneMatrices ); // copy current values var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); skeleton.boneMatrices = boneMatrices; skeleton.boneTexture = boneTexture; skeleton.boneTextureSize = size; } return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE's 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = capabilities.maxVertexUniforms; var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = Math.min( nVertexMatrices, bones.length ); if ( maxBones < bones.length ) { console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); return 0; } return maxBones; } } function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; if ( ! map ) { encoding = LinearEncoding; } else if ( map.isTexture ) { encoding = map.encoding; } else if ( map.isWebGLRenderTarget ) { console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); encoding = map.texture.encoding; } // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. if ( encoding === LinearEncoding && gammaOverrideLinear ) { encoding = GammaEncoding; } return encoding; } this.getParameters = function ( material, lights, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; var precision = renderer.getPrecision(); if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } } var currentRenderTarget = renderer.getCurrentRenderTarget(); var parameters = { shaderID: shaderID, precision: precision, supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, gradientMap: !! material.gradientMap, combine: material.combine, vertexColors: material.vertexColors, fog: !! fog, useFog: material.fog, fogExp: ( fog && fog.isFogExp2 ), flatShading: material.shading === FlatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, skinning: material.skinning, maxBones: maxBones, useVertexTexture: capabilities.floatVertexTextures, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: renderer.maxMorphTargets, maxMorphNormals: renderer.maxMorphNormals, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numClippingPlanes: nClipPlanes, numClipIntersection: nClipIntersection, shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, alphaTest: material.alphaTest, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false }; return parameters; }; this.getProgramCode = function ( material, parameters ) { var array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( material.fragmentShader ); array.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { array.push( name ); array.push( material.defines[ name ] ); } } for ( var i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[ parameterNames[ i ] ] ); } return array.join(); }; this.acquireProgram = function ( material, parameters, code ) { var program; // Check if code has been already compiled for ( var p = 0, pl = programs.length; p < pl; p ++ ) { var programInfo = programs[ p ]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, code, material, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set var i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } }; // Exposed for resource monitoring & error feedback via renderer.info: this.programs = programs; } export { WebGLPrograms };