three
Version:
JavaScript 3D library
407 lines (267 loc) • 12.5 kB
JavaScript
import { BackSide, DoubleSide, CubeUVRefractionMapping, CubeUVReflectionMapping, LinearEncoding, ObjectSpaceNormalMap, TangentSpaceNormalMap, NoToneMapping } from '../../constants.js';
import { WebGLProgram } from './WebGLProgram.js';
import { ShaderLib } from '../shaders/ShaderLib.js';
import { UniformsUtils } from '../shaders/UniformsUtils.js';
function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingStates, clipping ) {
const programs = [];
const isWebGL2 = capabilities.isWebGL2;
const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer;
const floatVertexTextures = capabilities.floatVertexTextures;
const maxVertexUniforms = capabilities.maxVertexUniforms;
const vertexTextures = capabilities.vertexTextures;
let precision = capabilities.precision;
const shaderIDs = {
MeshDepthMaterial: 'depth',
MeshDistanceMaterial: 'distanceRGBA',
MeshNormalMaterial: 'normal',
MeshBasicMaterial: 'basic',
MeshLambertMaterial: 'lambert',
MeshPhongMaterial: 'phong',
MeshToonMaterial: 'toon',
MeshStandardMaterial: 'physical',
MeshPhysicalMaterial: 'physical',
MeshMatcapMaterial: 'matcap',
LineBasicMaterial: 'basic',
LineDashedMaterial: 'dashed',
PointsMaterial: 'points',
ShadowMaterial: 'shadow',
SpriteMaterial: 'sprite'
};
const parameterNames = [
'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap',
'roughnessMap', 'metalnessMap', 'gradientMap',
'alphaMap', 'combine', 'vertexColors', 'vertexAlphas', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'premultipliedAlpha',
'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights',
'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows',
'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights',
'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering',
'sheen', 'transmission', 'transmissionMap', 'thicknessMap'
];
function getMaxBones( object ) {
const skeleton = object.skeleton;
const bones = skeleton.bones;
if ( 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)
const nVertexUniforms = maxVertexUniforms;
const nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
const 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 ) {
let encoding;
if ( map && map.isTexture ) {
encoding = map.encoding;
} else if ( map && map.isWebGLRenderTarget ) {
console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' );
encoding = map.texture.encoding;
} else {
encoding = LinearEncoding;
}
return encoding;
}
function getParameters( material, lights, shadows, scene, object ) {
const fog = scene.fog;
const environment = material.isMeshStandardMaterial ? scene.environment : null;
const envMap = cubemaps.get( material.envMap || environment );
const shaderID = shaderIDs[ material.type ];
// heuristics to create shader parameters according to lights in the scene
// (not to blow over maxLights budget)
const maxBones = object.isSkinnedMesh ? getMaxBones( object ) : 0;
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.' );
}
}
let vertexShader, fragmentShader;
if ( shaderID ) {
const shader = ShaderLib[ shaderID ];
vertexShader = shader.vertexShader;
fragmentShader = shader.fragmentShader;
} else {
vertexShader = material.vertexShader;
fragmentShader = material.fragmentShader;
}
const currentRenderTarget = renderer.getRenderTarget();
const parameters = {
isWebGL2: isWebGL2,
shaderID: shaderID,
shaderName: material.type,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
defines: material.defines,
isRawShaderMaterial: material.isRawShaderMaterial === true,
glslVersion: material.glslVersion,
precision: precision,
instancing: object.isInstancedMesh === true,
instancingColor: object.isInstancedMesh === true && object.instanceColor !== null,
supportsVertexTextures: vertexTextures,
outputEncoding: ( currentRenderTarget !== null ) ? getTextureEncodingFromMap( currentRenderTarget.texture ) : renderer.outputEncoding,
map: !! material.map,
mapEncoding: getTextureEncodingFromMap( material.map ),
matcap: !! material.matcap,
matcapEncoding: getTextureEncodingFromMap( material.matcap ),
envMap: !! envMap,
envMapMode: envMap && envMap.mapping,
envMapEncoding: getTextureEncodingFromMap( envMap ),
envMapCubeUV: ( !! envMap ) && ( ( envMap.mapping === CubeUVReflectionMapping ) || ( envMap.mapping === CubeUVRefractionMapping ) ),
lightMap: !! material.lightMap,
lightMapEncoding: getTextureEncodingFromMap( material.lightMap ),
aoMap: !! material.aoMap,
emissiveMap: !! material.emissiveMap,
emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap ),
bumpMap: !! material.bumpMap,
normalMap: !! material.normalMap,
objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap,
clearcoatMap: !! material.clearcoatMap,
clearcoatRoughnessMap: !! material.clearcoatRoughnessMap,
clearcoatNormalMap: !! material.clearcoatNormalMap,
displacementMap: !! material.displacementMap,
roughnessMap: !! material.roughnessMap,
metalnessMap: !! material.metalnessMap,
specularMap: !! material.specularMap,
alphaMap: !! material.alphaMap,
gradientMap: !! material.gradientMap,
sheen: !! material.sheen,
transmission: !! material.transmission,
transmissionMap: !! material.transmissionMap,
thicknessMap: !! material.thicknessMap,
combine: material.combine,
vertexTangents: ( material.normalMap && material.vertexTangents ),
vertexColors: material.vertexColors,
vertexAlphas: material.vertexColors === true && object.geometry && object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4,
vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatMap || !! material.clearcoatRoughnessMap || !! material.clearcoatNormalMap || !! material.displacementMap || !! material.transmissionMap || !! material.thicknessMap,
uvsVertexOnly: ! ( !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap || !! material.transmission || !! material.transmissionMap || !! material.thicknessMap ) && !! material.displacementMap,
fog: !! fog,
useFog: material.fog,
fogExp2: ( fog && fog.isFogExp2 ),
flatShading: !! material.flatShading,
sizeAttenuation: material.sizeAttenuation,
logarithmicDepthBuffer: logarithmicDepthBuffer,
skinning: object.isSkinnedMesh === true && maxBones > 0,
maxBones: maxBones,
useVertexTexture: floatVertexTextures,
morphTargets: material.morphTargets,
morphNormals: material.morphNormals,
numDirLights: lights.directional.length,
numPointLights: lights.point.length,
numSpotLights: lights.spot.length,
numRectAreaLights: lights.rectArea.length,
numHemiLights: lights.hemi.length,
numDirLightShadows: lights.directionalShadowMap.length,
numPointLightShadows: lights.pointShadowMap.length,
numSpotLightShadows: lights.spotShadowMap.length,
numClippingPlanes: clipping.numPlanes,
numClipIntersection: clipping.numIntersection,
dithering: material.dithering,
shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0,
shadowMapType: renderer.shadowMap.type,
toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping,
physicallyCorrectLights: renderer.physicallyCorrectLights,
premultipliedAlpha: material.premultipliedAlpha,
alphaTest: material.alphaTest,
doubleSided: material.side === DoubleSide,
flipSided: material.side === BackSide,
depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false,
index0AttributeName: material.index0AttributeName,
extensionDerivatives: material.extensions && material.extensions.derivatives,
extensionFragDepth: material.extensions && material.extensions.fragDepth,
extensionDrawBuffers: material.extensions && material.extensions.drawBuffers,
extensionShaderTextureLOD: material.extensions && material.extensions.shaderTextureLOD,
rendererExtensionFragDepth: isWebGL2 || extensions.has( 'EXT_frag_depth' ),
rendererExtensionDrawBuffers: isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ),
rendererExtensionShaderTextureLod: isWebGL2 || extensions.has( 'EXT_shader_texture_lod' ),
customProgramCacheKey: material.customProgramCacheKey()
};
return parameters;
}
function getProgramCacheKey( parameters ) {
const array = [];
if ( parameters.shaderID ) {
array.push( parameters.shaderID );
} else {
array.push( parameters.fragmentShader );
array.push( parameters.vertexShader );
}
if ( parameters.defines !== undefined ) {
for ( const name in parameters.defines ) {
array.push( name );
array.push( parameters.defines[ name ] );
}
}
if ( parameters.isRawShaderMaterial === false ) {
for ( let i = 0; i < parameterNames.length; i ++ ) {
array.push( parameters[ parameterNames[ i ] ] );
}
array.push( renderer.outputEncoding );
array.push( renderer.gammaFactor );
}
array.push( parameters.customProgramCacheKey );
return array.join();
}
function getUniforms( material ) {
const shaderID = shaderIDs[ material.type ];
let uniforms;
if ( shaderID ) {
const shader = ShaderLib[ shaderID ];
uniforms = UniformsUtils.clone( shader.uniforms );
} else {
uniforms = material.uniforms;
}
return uniforms;
}
function acquireProgram( parameters, cacheKey ) {
let program;
// Check if code has been already compiled
for ( let p = 0, pl = programs.length; p < pl; p ++ ) {
const preexistingProgram = programs[ p ];
if ( preexistingProgram.cacheKey === cacheKey ) {
program = preexistingProgram;
++ program.usedTimes;
break;
}
}
if ( program === undefined ) {
program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates );
programs.push( program );
}
return program;
}
function releaseProgram( program ) {
if ( -- program.usedTimes === 0 ) {
// Remove from unordered set
const i = programs.indexOf( program );
programs[ i ] = programs[ programs.length - 1 ];
programs.pop();
// Free WebGL resources
program.destroy();
}
}
return {
getParameters: getParameters,
getProgramCacheKey: getProgramCacheKey,
getUniforms: getUniforms,
acquireProgram: acquireProgram,
releaseProgram: releaseProgram,
// Exposed for resource monitoring & error feedback via renderer.info:
programs: programs
};
}
export { WebGLPrograms };