313 lines
8.3 KiB
JavaScript
313 lines
8.3 KiB
JavaScript
|
/**
|
||
|
* @author mrdoob / http://mrdoob.com/
|
||
|
*/
|
||
|
|
||
|
import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, GammaEncoding, LinearEncoding, ObjectSpaceNormalMap } from '../../constants.js';
|
||
|
import { WebGLProgram } from './WebGLProgram.js';
|
||
|
|
||
|
function WebGLPrograms( renderer, extensions, capabilities ) {
|
||
|
|
||
|
var programs = [];
|
||
|
|
||
|
var shaderIDs = {
|
||
|
MeshDepthMaterial: 'depth',
|
||
|
MeshDistanceMaterial: 'distanceRGBA',
|
||
|
MeshNormalMaterial: 'normal',
|
||
|
MeshBasicMaterial: 'basic',
|
||
|
MeshLambertMaterial: 'lambert',
|
||
|
MeshPhongMaterial: 'phong',
|
||
|
MeshToonMaterial: 'phong',
|
||
|
MeshStandardMaterial: 'physical',
|
||
|
MeshPhysicalMaterial: 'physical',
|
||
|
MeshMatcapMaterial: 'matcap',
|
||
|
LineBasicMaterial: 'basic',
|
||
|
LineDashedMaterial: 'dashed',
|
||
|
PointsMaterial: 'points',
|
||
|
ShadowMaterial: 'shadow',
|
||
|
SpriteMaterial: 'sprite'
|
||
|
};
|
||
|
|
||
|
var parameterNames = [
|
||
|
"precision", "supportsVertexTextures", "map", "mapEncoding", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
|
||
|
"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "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", "dithering"
|
||
|
];
|
||
|
|
||
|
|
||
|
function allocateBones( object ) {
|
||
|
|
||
|
var skeleton = object.skeleton;
|
||
|
var bones = skeleton.bones;
|
||
|
|
||
|
if ( capabilities.floatVertexTextures ) {
|
||
|
|
||
|
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, shadows, 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 = capabilities.precision;
|
||
|
|
||
|
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.getRenderTarget();
|
||
|
|
||
|
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 ),
|
||
|
matcap: !! material.matcap,
|
||
|
matcapEncoding: getTextureEncodingFromMap( material.matcap, 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,
|
||
|
objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
|
||
|
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.flatShading,
|
||
|
|
||
|
sizeAttenuation: material.sizeAttenuation,
|
||
|
logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,
|
||
|
|
||
|
skinning: material.skinning && maxBones > 0,
|
||
|
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,
|
||
|
|
||
|
dithering: material.dithering,
|
||
|
|
||
|
shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && 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 ] ] );
|
||
|
|
||
|
}
|
||
|
|
||
|
array.push( material.onBeforeCompile.toString() );
|
||
|
|
||
|
array.push( renderer.gammaOutput );
|
||
|
|
||
|
array.push( renderer.gammaFactor );
|
||
|
|
||
|
return array.join();
|
||
|
|
||
|
};
|
||
|
|
||
|
this.acquireProgram = function ( material, shader, 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, extensions, code, material, shader, parameters, capabilities );
|
||
|
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 };
|