Initial commit

This commit is contained in:
ChKendel
2026-01-31 21:50:25 +01:00
commit c65f983229
1088 changed files with 452910 additions and 0 deletions

View File

@@ -0,0 +1,943 @@
import { WebGLCoordinateSystem } from 'three';
import GLSLNodeBuilder from './nodes/GLSLNodeBuilder.js';
import Backend from '../common/Backend.js';
import WebGLAttributeUtils from './utils/WebGLAttributeUtils.js';
import WebGLState from './utils/WebGLState.js';
import WebGLUtils from './utils/WebGLUtils.js';
import WebGLTextureUtils from './utils/WebGLTextureUtils.js';
import WebGLExtensions from './utils/WebGLExtensions.js';
//
class WebGLBackend extends Backend {
constructor( parameters = {} ) {
super( parameters );
this.isWebGLBackend = true;
}
async init( renderer ) {
await super.init( renderer );
//
const parameters = this.parameters;
const glContext = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgl2' );
this.gl = glContext;
this.extensions = new WebGLExtensions( this );
this.attributeUtils = new WebGLAttributeUtils( this );
this.textureUtils = new WebGLTextureUtils( this );
this.state = new WebGLState( this );
this.utils = new WebGLUtils( this );
this.defaultTextures = {};
this.extensions.get( 'EXT_color_buffer_float' );
this._currentContext = null;
}
get coordinateSystem() {
return WebGLCoordinateSystem;
}
beginRender( renderContext ) {
const { gl } = this;
const renderContextData = this.get( renderContext );
//
renderContextData.previousContext = this._currentContext;
this._currentContext = renderContext;
this._setFramebuffer( renderContext );
this.clear( renderContext, renderContext.clearColor, renderContext.clearDepth, renderContext.clearStencil );
//
if ( renderContext.viewport ) {
this.updateViewport( renderContext );
} else {
gl.viewport( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );
}
const occlusionQueryCount = renderContext.occlusionQueryCount;
if ( occlusionQueryCount > 0 ) {
// Get a reference to the array of objects with queries. The renderContextData property
// can be changed by another render pass before the async reading of all previous queries complete
renderContextData.currentOcclusionQueries = renderContextData.occlusionQueries;
renderContextData.currentOcclusionQueryObjects = renderContextData.occlusionQueryObjects;
renderContextData.lastOcclusionObject = null;
renderContextData.occlusionQueries = new Array( occlusionQueryCount );
renderContextData.occlusionQueryObjects = new Array( occlusionQueryCount );
renderContextData.occlusionQueryIndex = 0;
}
}
finishRender( renderContext ) {
const renderContextData = this.get( renderContext );
const previousContext = renderContextData.previousContext;
this._currentContext = previousContext;
if ( previousContext !== null ) {
this._setFramebuffer( previousContext );
if ( previousContext.viewport ) {
this.updateViewport( previousContext );
} else {
const gl = this.gl;
gl.viewport( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );
}
}
const occlusionQueryCount = renderContext.occlusionQueryCount;
if ( occlusionQueryCount > 0 ) {
const renderContextData = this.get( renderContext );
if ( occlusionQueryCount > renderContextData.occlusionQueryIndex ) {
const { gl } = this;
gl.endQuery( gl.ANY_SAMPLES_PASSED );
}
this.resolveOccludedAsync( renderContext );
}
}
resolveOccludedAsync( renderContext ) {
const renderContextData = this.get( renderContext );
// handle occlusion query results
const { currentOcclusionQueries, currentOcclusionQueryObjects } = renderContextData;
if ( currentOcclusionQueries && currentOcclusionQueryObjects ) {
const occluded = new WeakSet();
const { gl } = this;
renderContextData.currentOcclusionQueryObjects = null;
renderContextData.currentOcclusionQueries = null;
const check = () => {
let completed = 0;
// check all queries and requeue as appropriate
for ( let i = 0; i < currentOcclusionQueries.length; i ++ ) {
const query = currentOcclusionQueries[ i ];
if ( query === null ) continue;
if ( gl.getQueryParameter( query, gl.QUERY_RESULT_AVAILABLE ) ) {
if ( gl.getQueryParameter( query, gl.QUERY_RESULT ) > 0 ) occluded.add( currentOcclusionQueryObjects[ i ] );
currentOcclusionQueries[ i ] = null;
gl.deleteQuery( query );
completed ++;
}
}
if ( completed < currentOcclusionQueries.length ) {
requestAnimationFrame( check );
} else {
renderContextData.occluded = occluded;
}
}
check();
}
}
isOccluded( renderContext, object ) {
const renderContextData = this.get( renderContext );
return renderContextData.occluded && renderContextData.occluded.has( object );
}
updateViewport( renderContext ) {
const gl = this.gl;
const { x, y, width, height } = renderContext.viewportValue;
gl.viewport( x, y, width, height );
}
clear( renderContext, color, depth, stencil ) {
const { gl } = this;
//
let clear = 0;
if ( color ) clear |= gl.COLOR_BUFFER_BIT;
if ( depth ) clear |= gl.DEPTH_BUFFER_BIT;
if ( stencil ) clear |= gl.STENCIL_BUFFER_BIT;
if ( clear !== 0 ) {
const clearColor = renderContext.clearColorValue;
if ( depth ) this.state.setDepthMask( true );
if ( renderContext.textures === null ) {
gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearColor.a );
gl.clear( clear );
} else {
if ( color ) {
for ( let i = 0; i < renderContext.textures.length; i ++ ) {
gl.clearBufferfv( gl.COLOR, i, [ clearColor.r, clearColor.g, clearColor.b, clearColor.a ] );
}
}
if ( depth && stencil ) {
gl.clearBufferfi( gl.DEPTH_STENCIL, 0, 1, 0 );
} else if ( depth ) {
gl.clearBufferfv( gl.DEPTH, 0, [ 1.0 ] );
} else if ( stencil ) {
gl.clearBufferiv( gl.STENCIL, 0, [ 0 ] );
}
}
}
}
beginCompute( /*computeGroup*/ ) {
console.warn( 'Abstract class.' );
}
compute( /*computeGroup, computeNode, bindings, pipeline*/ ) {
console.warn( 'Abstract class.' );
}
finishCompute( /*computeGroup*/ ) {
console.warn( 'Abstract class.' );
}
draw( renderObject, info ) {
const { pipeline, material, context } = renderObject;
const { programGPU, vaoGPU } = this.get( pipeline );
const { gl, state } = this;
const contextData = this.get( context );
//
const bindings = renderObject.getBindings();
for ( const binding of bindings ) {
const bindingData = this.get( binding );
const index = bindingData.index;
if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
gl.bindBufferBase( gl.UNIFORM_BUFFER, index, bindingData.bufferGPU );
} else if ( binding.isSampledTexture ) {
gl.activeTexture( gl.TEXTURE0 + index );
gl.bindTexture( bindingData.glTextureType, bindingData.textureGPU );
}
}
state.setMaterial( material );
gl.useProgram( programGPU );
gl.bindVertexArray( vaoGPU );
//
const index = renderObject.getIndex();
const object = renderObject.object;
const geometry = renderObject.geometry;
const drawRange = geometry.drawRange;
const firstVertex = drawRange.start;
//
const lastObject = contextData.lastOcclusionObject;
if ( lastObject !== object && lastObject !== undefined ) {
if ( lastObject !== null && lastObject.occlusionTest === true ) {
gl.endQuery( gl.ANY_SAMPLES_PASSED );
contextData.occlusionQueryIndex ++;
}
if ( object.occlusionTest === true ) {
const query = gl.createQuery();
gl.beginQuery( gl.ANY_SAMPLES_PASSED, query );
contextData.occlusionQueries[ contextData.occlusionQueryIndex ] = query;
contextData.occlusionQueryObjects[ contextData.occlusionQueryIndex ] = object;
}
contextData.lastOcclusionObject = object;
}
//
let mode;
if ( object.isPoints ) mode = gl.POINTS;
else if ( object.isLineSegments ) mode = gl.LINES;
else if ( object.isLine ) mode = gl.LINE_STRIP;
else if ( object.isLineLoop ) mode = gl.LINE_LOOP;
else mode = gl.TRIANGLES;
//
const instanceCount = this.getInstanceCount( renderObject );
if ( index !== null ) {
const indexData = this.get( index );
const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
if ( instanceCount > 1 ) {
gl.drawElementsInstanced( mode, index.count, indexData.type, firstVertex, instanceCount );
} else {
gl.drawElements( mode, index.count, indexData.type, firstVertex );
}
info.update( object, indexCount, 1 );
} else {
const positionAttribute = geometry.attributes.position;
const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
if ( instanceCount > 1 ) {
gl.drawArraysInstanced( mode, 0, vertexCount, instanceCount );
} else {
gl.drawArrays( mode, 0, vertexCount );
}
//gl.drawArrays( mode, vertexCount, gl.UNSIGNED_SHORT, firstVertex );
info.update( object, vertexCount, 1 );
}
//
gl.bindVertexArray( null );
}
needsUpdate( renderObject ) {
return false;
}
getCacheKey( renderObject ) {
return renderObject.geometry.id;
}
// textures
createSampler( /*texture*/ ) {
//console.warn( 'Abstract class.' );
}
destroySampler( /*texture*/ ) {
console.warn( 'Abstract class.' );
}
createDefaultTexture( texture ) {
const { gl, textureUtils, defaultTextures } = this;
const glTextureType = textureUtils.getGLTextureType( texture );
let textureGPU = defaultTextures[ glTextureType ];
if ( textureGPU === undefined ) {
textureGPU = gl.createTexture();
gl.bindTexture( glTextureType, textureGPU );
gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
//gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );
defaultTextures[ glTextureType ] = textureGPU;
}
this.set( texture, {
textureGPU,
glTextureType,
isDefault: true
} );
}
createTexture( texture, options ) {
const { gl, utils, textureUtils } = this;
const { levels, width, height } = options;
const glFormat = utils.convert( texture.format, texture.colorSpace );
const glType = utils.convert( texture.type );
const glInternalFormat = textureUtils.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture );
const textureGPU = gl.createTexture();
const glTextureType = textureUtils.getGLTextureType( texture );
gl.bindTexture( glTextureType, textureGPU );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE );
textureUtils.setTextureParameters( glTextureType, texture );
gl.bindTexture( glTextureType, textureGPU );
if ( ! texture.isVideoTexture ) {
gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height );
}
this.set( texture, {
textureGPU,
glTextureType,
glFormat,
glType,
glInternalFormat
} );
}
updateTexture( texture, options ) {
const { gl } = this;
const { width, height } = options;
const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.get( texture );
const getImage = ( source ) => {
if ( source.isDataTexture ) {
return source.image.data;
} else if ( source instanceof ImageBitmap || source instanceof OffscreenCanvas || source instanceof HTMLImageElement || source instanceof HTMLCanvasElement ) {
return source;
}
return source.data;
};
gl.bindTexture( glTextureType, textureGPU );
if ( texture.isCubeTexture ) {
const images = options.images;
for ( let i = 0; i < 6; i ++ ) {
const image = getImage( images[ i ] );
gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image );
}
} else if ( texture.isVideoTexture ) {
texture.update();
gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image );
} else {
const image = getImage( options.image );
gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image );
}
}
generateMipmaps( texture ) {
const { gl } = this;
const { textureGPU, glTextureType } = this.get( texture );
gl.bindTexture( glTextureType, textureGPU );
gl.generateMipmap( glTextureType );
}
destroyTexture( /*texture*/ ) {
console.warn( 'Abstract class.' );
}
copyTextureToBuffer( /*texture, x, y, width, height*/ ) {
console.warn( 'Abstract class.' );
}
// node builder
createNodeBuilder( object, renderer, scene = null ) {
return new GLSLNodeBuilder( object, renderer, scene );
}
// program
createProgram( program ) {
const gl = this.gl;
const { stage, code } = program;
const shader = stage === 'vertex' ? gl.createShader( gl.VERTEX_SHADER ) : gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource( shader, code );
gl.compileShader( shader );
this.set( program, {
shaderGPU: shader
} );
}
destroyProgram( /*program*/ ) {
console.warn( 'Abstract class.' );
}
createRenderPipeline( renderObject ) {
const gl = this.gl;
const pipeline = renderObject.pipeline;
// Program
const { fragmentProgram, vertexProgram } = pipeline;
const programGPU = gl.createProgram();
const fragmentShader = this.get( fragmentProgram ).shaderGPU;
const vertexShader = this.get( vertexProgram ).shaderGPU;
gl.attachShader( programGPU, fragmentShader );
gl.attachShader( programGPU, vertexShader );
gl.linkProgram( programGPU );
if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) {
console.error( 'THREE.WebGLBackend:', gl.getProgramInfoLog( programGPU ) );
console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( fragmentShader ) );
console.error( 'THREE.WebGLBackend:', gl.getShaderInfoLog( vertexShader ) );
}
gl.useProgram( programGPU );
// Bindings
const bindings = renderObject.getBindings();
for ( const binding of bindings ) {
const bindingData = this.get( binding );
const index = bindingData.index;
if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
const location = gl.getUniformBlockIndex( programGPU, binding.name );
gl.uniformBlockBinding( programGPU, location, index );
} else if ( binding.isSampledTexture ) {
const location = gl.getUniformLocation( programGPU, binding.name );
gl.uniform1i( location, index );
}
}
// VAO
const vaoGPU = gl.createVertexArray();
const index = renderObject.getIndex();
const attributes = renderObject.getAttributes();
gl.bindVertexArray( vaoGPU );
if ( index !== null ) {
const indexData = this.get( index );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, indexData.bufferGPU );
}
for ( let i = 0; i < attributes.length; i ++ ) {
const attribute = attributes[ i ];
const attributeData = this.get( attribute );
gl.bindBuffer( gl.ARRAY_BUFFER, attributeData.bufferGPU );
gl.enableVertexAttribArray( i );
let stride, offset;
if ( attribute.isInterleavedBufferAttribute === true ) {
stride = attribute.data.stride * attributeData.bytesPerElement;
offset = attribute.offset * attributeData.bytesPerElement;
} else {
stride = 0;
offset = 0;
}
if ( attributeData.isFloat ) {
gl.vertexAttribPointer( i, attribute.itemSize, attributeData.type, false, stride, offset );
} else {
gl.vertexAttribIPointer( i, attribute.itemSize, attributeData.type, stride, offset );
}
if ( attribute.isInstancedBufferAttribute && ! attribute.isInterleavedBufferAttribute ) {
gl.vertexAttribDivisor( i, attribute.meshPerAttribute );
} else if ( attribute.isInterleavedBufferAttribute && attribute.data.isInstancedInterleavedBuffer ) {
gl.vertexAttribDivisor( i, attribute.data.meshPerAttribute );
}
}
gl.bindVertexArray( null );
//
this.set( pipeline, {
programGPU,
vaoGPU
} );
}
createComputePipeline( /*computePipeline, bindings*/ ) {
console.warn( 'Abstract class.' );
}
createBindings( bindings ) {
this.updateBindings( bindings );
}
updateBindings( bindings ) {
const { gl } = this;
let groupIndex = 0;
let textureIndex = 0;
for ( const binding of bindings ) {
if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
const bufferGPU = gl.createBuffer();
const data = binding.buffer;
gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
gl.bindBufferBase( gl.UNIFORM_BUFFER, groupIndex, bufferGPU );
this.set( binding, {
index: groupIndex ++,
bufferGPU
} );
} else if ( binding.isSampledTexture ) {
const { textureGPU, glTextureType } = this.get( binding.texture );
this.set( binding, {
index: textureIndex ++,
textureGPU,
glTextureType
} );
}
}
}
updateBinding( binding ) {
const gl = this.gl;
if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
const bindingData = this.get( binding );
const bufferGPU = bindingData.bufferGPU;
const data = binding.buffer;
gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
}
}
// attributes
createIndexAttribute( attribute ) {
const gl = this.gl;
this.attributeUtils.createAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER );
}
createAttribute( attribute ) {
const gl = this.gl;
this.attributeUtils.createAttribute( attribute, gl.ARRAY_BUFFER );
}
createStorageAttribute( /*attribute*/ ) {
console.warn( 'Abstract class.' );
}
updateAttribute( attribute ) {
this.attributeUtils.updateAttribute( attribute );
}
destroyAttribute( /*attribute*/ ) {
console.warn( 'Abstract class.' );
}
updateSize() {
//console.warn( 'Abstract class.' );
}
hasFeature( name ) {
return true;
}
copyFramebufferToTexture( texture /*, renderContext */ ) {
const { gl } = this;
const { textureGPU } = this.get( texture );
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
gl.bindTexture( gl.TEXTURE_2D, textureGPU );
gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, texture.image.width, texture.image.height );
if ( texture.generateMipmaps ) gl.generateMipmap( gl.TEXTURE_2D );
gl.bindTexture( gl.TEXTURE_2D, null );
}
_setFramebuffer( renderContext ) {
const { gl } = this;
if ( renderContext.textures !== null ) {
const renderContextData = this.get( renderContext );
let fb = renderContextData.framebuffer;
if ( fb === undefined ) {
fb = gl.createFramebuffer();
gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
const textures = renderContext.textures;
const drawBuffers = [];
for ( let i = 0; i < textures.length; i++ ) {
const texture = textures[ i ];
const { textureGPU } = this.get( texture );
const attachment = gl.COLOR_ATTACHMENT0 + i;
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, textureGPU, 0 );
drawBuffers.push( attachment );
}
gl.drawBuffers( drawBuffers );
if ( renderContext.depthTexture !== null ) {
const { textureGPU } = this.get( renderContext.depthTexture );
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, textureGPU, 0 );
}
renderContextData.framebuffer = fb;
} else {
gl.bindFramebuffer( gl.FRAMEBUFFER, fb );
}
} else {
gl.bindFramebuffer( gl.FRAMEBUFFER, null );
}
}
}
export default WebGLBackend;

View File

@@ -0,0 +1,603 @@
import { MathNode, GLSLNodeParser, NodeBuilder, NodeMaterial, FunctionNode } from '../../../nodes/Nodes.js';
import UniformBuffer from '../../common/UniformBuffer.js';
import UniformsGroup from '../../common/UniformsGroup.js';
import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js';
const glslMethods = {
[ MathNode.ATAN2 ]: 'atan',
textureDimensions: 'textureSize'
};
const precisionLib = {
low: 'lowp',
medium: 'mediump',
high: 'highp'
};
const supports = {
instance: true
};
class GLSLNodeBuilder extends NodeBuilder {
constructor( object, renderer, scene = null ) {
super( object, renderer, new GLSLNodeParser(), scene );
this.uniformsGroup = {};
}
getMethod( method ) {
return glslMethods[ method ] || method;
}
getPropertyName( node, shaderStage ) {
if ( node.isOutputStructVar ) return '';
return super.getPropertyName( node, shaderStage );
}
buildFunctionNode( shaderNode ) {
const layout = shaderNode.layout;
const flowData = this.flowShaderNode( shaderNode );
const parameters = [];
for ( const input of layout.inputs ) {
parameters.push( this.getType( input.type ) + ' ' + input.name );
}
//
const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) {
${ flowData.vars }
${ flowData.code }
return ${ flowData.result };
}`;
//
return new FunctionNode( code );
}
getTexture( texture, textureProperty, uvSnippet ) {
if ( texture.isTextureCube ) {
return `textureCube( ${textureProperty}, ${uvSnippet} )`;
} else if ( texture.isDepthTexture ) {
return `texture( ${textureProperty}, ${uvSnippet} ).x`;
} else {
return `texture( ${textureProperty}, ${uvSnippet} )`;
}
}
getTextureLevel( texture, textureProperty, uvSnippet, biasSnippet ) {
return `textureLod( ${textureProperty}, ${uvSnippet}, ${biasSnippet} )`;
}
getTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, shaderStage = this.shaderStage ) {
if ( shaderStage === 'fragment' ) {
return `texture( ${textureProperty}, vec3( ${uvSnippet}, ${compareSnippet} ) )`;
} else {
console.error( `WebGPURenderer: THREE.DepthTexture.compareFunction() does not support ${ shaderStage } shader.` );
}
}
getVars( shaderStage ) {
const snippets = [];
const vars = this.vars[ shaderStage ];
if ( vars !== undefined ) {
for ( const variable of vars ) {
if ( variable.isOutputStructVar ) continue;
snippets.push( `${ this.getVar( variable.type, variable.name ) };` );
}
}
return snippets.join( '\n\t' );
}
getUniforms( shaderStage ) {
const uniforms = this.uniforms[ shaderStage ];
const bindingSnippets = [];
const groupSnippets = [];
for ( const uniform of uniforms ) {
let snippet = null;
let group = false;
if ( uniform.type === 'texture' ) {
if ( uniform.node.value.compareFunction ) {
snippet = `sampler2DShadow ${uniform.name};`;
} else {
snippet = `sampler2D ${uniform.name};`;
}
} else if ( uniform.type === 'cubeTexture' ) {
snippet = `samplerCube ${uniform.name};`;
} else if ( uniform.type === 'buffer' ) {
const bufferNode = uniform.node;
const bufferType = this.getType( bufferNode.bufferType );
const bufferCount = bufferNode.bufferCount;
const bufferCountSnippet = bufferCount > 0 ? bufferCount : '';
snippet = `${bufferNode.name} {\n\t${bufferType} ${uniform.name}[${bufferCountSnippet}];\n};\n`;
} else {
const vectorType = this.getVectorType( uniform.type );
snippet = `${vectorType} ${uniform.name};`;
group = true;
}
const precision = uniform.node.precision;
if ( precision !== null ) {
snippet = precisionLib[ precision ] + ' ' + snippet;
}
if ( group ) {
snippet = '\t' + snippet;
groupSnippets.push( snippet );
} else {
snippet = 'uniform ' + snippet;
bindingSnippets.push( snippet );
}
}
let output = '';
if ( groupSnippets.length > 0 ) {
output += this._getGLSLUniformStruct( shaderStage + 'NodeUniforms', groupSnippets.join( '\n' ) ) + '\n';
}
output += bindingSnippets.join( '\n' );
return output;
}
getAttributes( shaderStage ) {
let snippet = '';
if ( shaderStage === 'vertex' ) {
const attributes = this.getAttributesArray();
let location = 0;
for ( const attribute of attributes ) {
snippet += `layout( location = ${ location ++ } ) in ${ attribute.type } ${ attribute.name };\n`;
}
}
return snippet;
}
getStructMembers( struct ) {
const snippets = [];
const members = struct.getMemberTypes();
for ( let i = 0; i < members.length; i ++ ) {
const member = members[ i ];
snippets.push( `layout( location = ${i} ) out ${ member} m${i};` );
}
return snippets.join( '\n' );
}
getStructs( shaderStage ) {
const snippets = [];
const structs = this.structs[ shaderStage ];
if ( structs.length === 0 ) {
return 'layout( location = 0 ) out vec4 fragColor;\n';
}
for ( let index = 0, length = structs.length; index < length; index ++ ) {
const struct = structs[ index ];
let snippet = '\n';
snippet += this.getStructMembers( struct );
snippet += '\n';
snippets.push( snippet );
}
return snippets.join( '\n\n' );
}
getVaryings( shaderStage ) {
let snippet = '';
const varyings = this.varyings;
if ( shaderStage === 'vertex' ) {
for ( const varying of varyings ) {
const type = varying.type;
const flat = type === 'int' || type === 'uint' ? 'flat ' : '';
snippet += `${flat}${varying.needsInterpolation ? 'out' : '/*out*/'} ${type} ${varying.name};\n`;
}
} else if ( shaderStage === 'fragment' ) {
for ( const varying of varyings ) {
if ( varying.needsInterpolation ) {
const type = varying.type;
const flat = type === 'int' || type === 'uint' ? 'flat ' : '';
snippet += `${flat}in ${type} ${varying.name};\n`;
}
}
}
return snippet;
}
getVertexIndex() {
return 'gl_VertexID';
}
getInstanceIndex() {
return 'uint( gl_InstanceID )';
}
getFrontFacing() {
return 'gl_FrontFacing';
}
getFragCoord() {
return 'gl_FragCoord';
}
isAvailable( name ) {
return supports[ name ] === true;
}
isFlipY() {
return true;
}
_getGLSLUniformStruct( name, vars ) {
return `
layout( std140 ) uniform ${name} {
${vars}
};`;
}
_getGLSLVertexCode( shaderData ) {
return `#version 300 es
${ this.getSignature() }
// precision
precision highp float;
precision highp int;
// uniforms
${shaderData.uniforms}
// varyings
${shaderData.varyings}
// attributes
${shaderData.attributes}
// codes
${shaderData.codes}
void main() {
// vars
${shaderData.vars}
// flow
${shaderData.flow}
gl_PointSize = 1.0;
}
`;
}
_getGLSLFragmentCode( shaderData ) {
return `#version 300 es
${ this.getSignature() }
// precision
precision highp float;
precision highp int;
precision lowp sampler2DShadow;
// uniforms
${shaderData.uniforms}
// varyings
${shaderData.varyings}
// codes
${shaderData.codes}
${shaderData.structs}
void main() {
// vars
${shaderData.vars}
// flow
${shaderData.flow}
}
`;
}
buildCode() {
const shadersData = this.material !== null ? { fragment: {}, vertex: {} } : { compute: {} };
for ( const shaderStage in shadersData ) {
let flow = '// code\n\n';
flow += this.flowCode[ shaderStage ];
const flowNodes = this.flowNodes[ shaderStage ];
const mainNode = flowNodes[ flowNodes.length - 1 ];
for ( const node of flowNodes ) {
const flowSlotData = this.getFlowData( node/*, shaderStage*/ );
const slotName = node.name;
if ( slotName ) {
if ( flow.length > 0 ) flow += '\n';
flow += `\t// flow -> ${ slotName }\n\t`;
}
flow += `${ flowSlotData.code }\n\t`;
if ( node === mainNode && shaderStage !== 'compute' ) {
flow += '// result\n\t';
if ( shaderStage === 'vertex' ) {
flow += 'gl_Position = ';
flow += `${ flowSlotData.result };`;
} else if ( shaderStage === 'fragment' ) {
if ( ! node.outputNode.isOutputStructNode ) {
flow += 'fragColor = ';
flow += `${ flowSlotData.result };`;
}
}
}
}
const stageData = shadersData[ shaderStage ];
stageData.uniforms = this.getUniforms( shaderStage );
stageData.attributes = this.getAttributes( shaderStage );
stageData.varyings = this.getVaryings( shaderStage );
stageData.vars = this.getVars( shaderStage );
stageData.structs = this.getStructs( shaderStage );
stageData.codes = this.getCodes( shaderStage );
stageData.flow = flow;
}
if ( this.material !== null ) {
this.vertexShader = this._getGLSLVertexCode( shadersData.vertex );
this.fragmentShader = this._getGLSLFragmentCode( shadersData.fragment );
} else {
console.warn( 'GLSLNodeBuilder: compute shaders are not supported.' );
//this.computeShader = this._getGLSLComputeCode( shadersData.compute );
}
}
getUniformFromNode( node, type, shaderStage, name = null ) {
const uniformNode = super.getUniformFromNode( node, type, shaderStage, name );
const nodeData = this.getDataFromNode( node, shaderStage );
let uniformGPU = nodeData.uniformGPU;
if ( uniformGPU === undefined ) {
if ( type === 'texture' ) {
uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node );
this.bindings[ shaderStage ].push( uniformGPU );
} else if ( type === 'cubeTexture' ) {
uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node );
this.bindings[ shaderStage ].push( uniformGPU );
} else if ( type === 'buffer' ) {
node.name = `NodeBuffer_${node.id}`;
const buffer = new UniformBuffer( node.name, node.value );
uniformNode.name = `buffer${node.id}`;
this.bindings[ shaderStage ].push( buffer );
uniformGPU = buffer;
} else {
let uniformsGroup = this.uniformsGroup[ shaderStage ];
if ( uniformsGroup === undefined ) {
uniformsGroup = new UniformsGroup( shaderStage + 'NodeUniforms' );
//uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );
this.uniformsGroup[ shaderStage ] = uniformsGroup;
this.bindings[ shaderStage ].push( uniformsGroup );
}
uniformGPU = this.getNodeUniform( uniformNode, type );
uniformsGroup.addUniform( uniformGPU );
}
nodeData.uniformGPU = uniformGPU;
}
return uniformNode;
}
build() {
// @TODO: Move this code to super.build()
const { object, material } = this;
if ( material !== null ) {
NodeMaterial.fromMaterial( material ).build( this );
} else {
this.addFlow( 'compute', object );
}
return super.build();
}
}
export default GLSLNodeBuilder;

View File

@@ -0,0 +1,119 @@
class WebGLAttributeUtils {
constructor( backend ) {
this.backend = backend;
}
createAttribute( attribute, bufferType ) {
const backend = this.backend;
const { gl } = backend;
const array = attribute.array;
const usage = attribute.usage || gl.STATIC_DRAW;
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
const bufferData = backend.get( bufferAttribute );
let bufferGPU = bufferData.bufferGPU;
if ( bufferGPU === undefined ) {
bufferGPU = gl.createBuffer();
gl.bindBuffer( bufferType, bufferGPU );
gl.bufferData( bufferType, array, usage );
gl.bindBuffer( bufferType, null );
bufferData.bufferGPU = bufferGPU;
bufferData.bufferType = bufferType;
bufferData.version = bufferAttribute.version;
}
//attribute.onUploadCallback();
let type;
let isFloat = false;
if ( array instanceof Float32Array ) {
type = gl.FLOAT;
isFloat = true;
} else if ( array instanceof Uint16Array ) {
if ( attribute.isFloat16BufferAttribute ) {
type = gl.HALF_FLOAT;
isFloat = true;
} else {
type = gl.UNSIGNED_SHORT;
}
} else if ( array instanceof Int16Array ) {
type = gl.SHORT;
} else if ( array instanceof Uint32Array ) {
type = gl.UNSIGNED_INT;
} else if ( array instanceof Int32Array ) {
type = gl.INT;
} else if ( array instanceof Int8Array ) {
type = gl.BYTE;
} else if ( array instanceof Uint8Array ) {
type = gl.UNSIGNED_BYTE;
} else if ( array instanceof Uint8ClampedArray ) {
type = gl.UNSIGNED_BYTE;
} else {
throw new Error( 'THREE.WebGLBackend: Unsupported buffer data format: ' + array );
}
backend.set( attribute, {
bufferGPU,
type,
bytesPerElement: array.BYTES_PER_ELEMENT,
version: attribute.version,
isFloat
} );
}
updateAttribute( attribute ) {
const backend = this.backend;
const { gl } = backend;
const array = attribute.array;
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
const bufferData = backend.get( bufferAttribute );
const bufferType = bufferData.bufferType;
gl.bindBuffer( bufferType, bufferData.bufferGPU );
gl.bufferSubData( bufferType, 0, array );
gl.bindBuffer( bufferType, null );
bufferData.version = bufferAttribute.version;
}
}
export default WebGLAttributeUtils;

View File

@@ -0,0 +1,26 @@
class WebGLExtensions {
constructor( backend ) {
this.backend = backend;
this.gl = this.backend.gl;
this.availableExtensions = this.gl.getSupportedExtensions();
}
get( name ) {
return this.gl.getExtension( name );
}
has( name ) {
return this.availableExtensions.includes( name );
}
}
export default WebGLExtensions;

View File

@@ -0,0 +1,541 @@
import {
CullFaceNone, CullFaceBack, CullFaceFront, DoubleSide, BackSide,
NormalBlending, NoBlending, CustomBlending, AddEquation,
AdditiveBlending, SubtractiveBlending, MultiplyBlending, SubtractEquation, ReverseSubtractEquation,
ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor,
OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor,
NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth
} from 'three';
let initialized = false, equationToGL, factorToGL;
class WebGLState {
constructor( backend ) {
this.backend = backend;
this.gl = this.backend.gl;
this.enabled = {};
this.currentFlipSided = null;
this.currentCullFace = null;
this.currentProgram = null;
this.currentBlendingEnabled = false;
this.currentBlending = null;
this.currentBlendSrc = null;
this.currentBlendDst = null;
this.currentBlendSrcAlpha = null;
this.currentBlendDstAlpha = null;
this.currentPremultipledAlpha = null;
this.currentPolygonOffsetFactor = null;
this.currentPolygonOffsetUnits = null;
this.currentColorMask = null;
this.currentDepthFunc = null;
this.currentDepthMask = null;
this.currentStencilFunc = null;
this.currentStencilRef = null;
this.currentStencilFuncMask = null;
this.currentStencilFail = null;
this.currentStencilZFail = null;
this.currentStencilZPass = null;
this.currentStencilMask = null;
if ( initialized === false ) {
this._init( this.gl );
initialized = true;
}
}
_init( gl ) {
// Store only WebGL constants here.
equationToGL = {
[ AddEquation ]: gl.FUNC_ADD,
[ SubtractEquation ]: gl.FUNC_SUBTRACT,
[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT
};
factorToGL = {
[ ZeroFactor ]: gl.ZERO,
[ OneFactor ]: gl.ONE,
[ SrcColorFactor ]: gl.SRC_COLOR,
[ SrcAlphaFactor ]: gl.SRC_ALPHA,
[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,
[ DstColorFactor ]: gl.DST_COLOR,
[ DstAlphaFactor ]: gl.DST_ALPHA,
[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,
[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,
[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,
[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA
};
}
enable( id ) {
const { enabled } = this;
if ( enabled[ id ] !== true ) {
this.gl.enable( id );
enabled[ id ] = true;
}
}
disable( id ) {
const { enabled } = this;
if ( enabled[ id ] !== false ) {
this.gl.disable( id );
enabled[ id ] = false;
}
}
setFlipSided( flipSided ) {
if ( this.currentFlipSided !== flipSided ) {
const { gl } = this;
if ( flipSided ) {
gl.frontFace( gl.CW );
} else {
gl.frontFace( gl.CCW );
}
this.currentFlipSided = flipSided;
}
}
setCullFace( cullFace ) {
const { gl } = this;
if ( cullFace !== CullFaceNone ) {
this.enable( gl.CULL_FACE );
if ( cullFace !== this.currentCullFace ) {
if ( cullFace === CullFaceBack ) {
gl.cullFace( gl.BACK );
} else if ( cullFace === CullFaceFront ) {
gl.cullFace( gl.FRONT );
} else {
gl.cullFace( gl.FRONT_AND_BACK );
}
}
} else {
this.disable( gl.CULL_FACE );
}
this.currentCullFace = cullFace;
}
setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
const { gl } = this;
if ( blending === NoBlending ) {
if ( this.currentBlendingEnabled === true ) {
this.disable( gl.BLEND );
this.currentBlendingEnabled = false;
}
return;
}
if ( this.currentBlendingEnabled === false ) {
this.enable( gl.BLEND );
this.currentBlendingEnabled = true;
}
if ( blending !== CustomBlending ) {
if ( blending !== this.currentBlending || premultipliedAlpha !== this.currentPremultipledAlpha ) {
if ( this.currentBlendEquation !== AddEquation || this.currentBlendEquationAlpha !== AddEquation ) {
gl.blendEquation( gl.FUNC_ADD );
this.currentBlendEquation = AddEquation;
this.currentBlendEquationAlpha = AddEquation;
}
if ( premultipliedAlpha ) {
switch ( blending ) {
case NormalBlending:
gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
break;
case AdditiveBlending:
gl.blendFunc( gl.ONE, gl.ONE );
break;
case SubtractiveBlending:
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
break;
case MultiplyBlending:
gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
break;
default:
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
break;
}
} else {
switch ( blending ) {
case NormalBlending:
gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
break;
case AdditiveBlending:
gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
break;
case SubtractiveBlending:
gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
break;
case MultiplyBlending:
gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
break;
default:
console.error( 'THREE.WebGLState: Invalid blending: ', blending );
break;
}
}
this.currentBlendSrc = null;
this.currentBlendDst = null;
this.currentBlendSrcAlpha = null;
this.currentBlendDstAlpha = null;
this.currentBlending = blending;
this.currentPremultipledAlpha = premultipliedAlpha;
}
return;
}
// custom blending
blendEquationAlpha = blendEquationAlpha || blendEquation;
blendSrcAlpha = blendSrcAlpha || blendSrc;
blendDstAlpha = blendDstAlpha || blendDst;
if ( blendEquation !== this.currentBlendEquation || blendEquationAlpha !== this.currentBlendEquationAlpha ) {
gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
this.currentBlendEquation = blendEquation;
this.currentBlendEquationAlpha = blendEquationAlpha;
}
if ( blendSrc !== this.currentBlendSrc || blendDst !== this.currentBlendDst || blendSrcAlpha !== this.currentBlendSrcAlpha || blendDstAlpha !== this.currentBlendDstAlpha ) {
gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
this.currentBlendSrc = blendSrc;
this.currentBlendDst = blendDst;
this.currentBlendSrcAlpha = blendSrcAlpha;
this.currentBlendDstAlpha = blendDstAlpha;
}
this.currentBlending = blending;
this.currentPremultipledAlpha = false;
}
setColorMask( colorMask ) {
if ( this.currentColorMask !== colorMask ) {
this.gl.colorMask( colorMask, colorMask, colorMask, colorMask );
this.currentColorMask = colorMask;
}
}
setDepthTest( depthTest ) {
const { gl } = this;
if ( depthTest ) {
this.enable( gl.DEPTH_TEST );
} else {
this.disable( gl.DEPTH_TEST );
}
}
setDepthMask( depthMask ) {
if ( this.currentDepthMask !== depthMask ) {
this.gl.depthMask( depthMask );
this.currentDepthMask = depthMask;
}
}
setDepthFunc( depthFunc ) {
if ( this.currentDepthFunc !== depthFunc ) {
const { gl } = this;
switch ( depthFunc ) {
case NeverDepth:
gl.depthFunc( gl.NEVER );
break;
case AlwaysDepth:
gl.depthFunc( gl.ALWAYS );
break;
case LessDepth:
gl.depthFunc( gl.LESS );
break;
case LessEqualDepth:
gl.depthFunc( gl.LEQUAL );
break;
case EqualDepth:
gl.depthFunc( gl.EQUAL );
break;
case GreaterEqualDepth:
gl.depthFunc( gl.GEQUAL );
break;
case GreaterDepth:
gl.depthFunc( gl.GREATER );
break;
case NotEqualDepth:
gl.depthFunc( gl.NOTEQUAL );
break;
default:
gl.depthFunc( gl.LEQUAL );
}
this.currentDepthFunc = depthFunc;
}
}
setStencilTest( stencilTest ) {
const { gl } = this;
if ( stencilTest ) {
this.enable( gl.STENCIL_TEST );
} else {
this.disable( gl.STENCIL_TEST );
}
}
setStencilMask( stencilMask ) {
if ( this.currentStencilMask !== stencilMask ) {
this.gl.stencilMask( stencilMask );
this.currentStencilMask = stencilMask;
}
}
setStencilFunc( stencilFunc, stencilRef, stencilMask ) {
if ( this.currentStencilFunc !== stencilFunc ||
this.currentStencilRef !== stencilRef ||
this.currentStencilFuncMask !== stencilMask ) {
this.gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
this.currentStencilFunc = stencilFunc;
this.currentStencilRef = stencilRef;
this.currentStencilFuncMask = stencilMask;
}
}
setStencilOp( stencilFail, stencilZFail, stencilZPass ) {
if ( this.currentStencilFail !== stencilFail ||
this.currentStencilZFail !== stencilZFail ||
this.currentStencilZPass !== stencilZPass ) {
this.gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
this.currentStencilFail = stencilFail;
this.currentStencilZFail = stencilZFail;
this.currentStencilZPass = stencilZPass;
}
}
setMaterial( material, frontFaceCW ) {
const { gl } = this;
material.side === DoubleSide
? this.disable( gl.CULL_FACE )
: this.enable( gl.CULL_FACE );
let flipSided = ( material.side === BackSide );
if ( frontFaceCW ) flipSided = ! flipSided;
this.setFlipSided( flipSided );
( material.blending === NormalBlending && material.transparent === false )
? this.setBlending( NoBlending )
: this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
this.setDepthFunc( material.depthFunc );
this.setDepthTest( material.depthTest );
this.setDepthMask( material.depthWrite );
this.setColorMask( material.colorWrite );
const stencilWrite = material.stencilWrite;
this.setStencilTest( stencilWrite );
if ( stencilWrite ) {
this.setStencilMask( material.stencilWriteMask );
this.setStencilFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
this.setStencilOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
}
this.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
material.alphaToCoverage === true
? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
: this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
}
setPolygonOffset( polygonOffset, factor, units ) {
const { gl } = this;
if ( polygonOffset ) {
this.enable( gl.POLYGON_OFFSET_FILL );
if ( this.currentPolygonOffsetFactor !== factor || this.currentPolygonOffsetUnits !== units ) {
gl.polygonOffset( factor, units );
this.currentPolygonOffsetFactor = factor;
this.currentPolygonOffsetUnits = units;
}
} else {
this.disable( gl.POLYGON_OFFSET_FILL );
}
}
useProgram( program ) {
if ( this.currentProgram !== program ) {
this.gl.useProgram( program );
this.currentProgram = program;
return true;
}
return false;
}
}
export default WebGLState;

View File

@@ -0,0 +1,212 @@
import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RGBAFormat, DepthFormat, DepthStencilFormat, UnsignedShortType, UnsignedIntType, UnsignedInt248Type, FloatType, HalfFloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, UnsignedByteType, _SRGBAFormat, NoColorSpace, LinearSRGBColorSpace, SRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare } from 'three';
let initialized = false, wrappingToGL, filterToGL, compareToGL;
class WebGLTextureUtils {
constructor( backend ) {
this.backend = backend;
this.gl = backend.gl;
this.extensions = backend.extensions;
if ( initialized === false ) {
this._init( this.gl );
initialized = true;
}
}
_init( gl ) {
// Store only WebGL constants here.
wrappingToGL = {
[ RepeatWrapping ]: gl.REPEAT,
[ ClampToEdgeWrapping ]: gl.CLAMP_TO_EDGE,
[ MirroredRepeatWrapping ]: gl.MIRRORED_REPEAT
};
filterToGL = {
[ NearestFilter ]: gl.NEAREST,
[ NearestMipmapNearestFilter ]: gl.NEAREST_MIPMAP_NEAREST,
[ NearestMipmapLinearFilter ]: gl.NEAREST_MIPMAP_LINEAR,
[ LinearFilter ]: gl.LINEAR,
[ LinearMipmapNearestFilter ]: gl.LINEAR_MIPMAP_NEAREST,
[ LinearMipmapLinearFilter ]: gl.LINEAR_MIPMAP_LINEAR
};
compareToGL = {
[ NeverCompare ]: gl.NEVER,
[ AlwaysCompare ]: gl.ALWAYS,
[ LessCompare ]: gl.LESS,
[ LessEqualCompare ]: gl.LEQUAL,
[ EqualCompare ]: gl.EQUAL,
[ GreaterEqualCompare ]: gl.GEQUAL,
[ GreaterCompare ]: gl.GREATER,
[ NotEqualCompare ]: gl.NOTEQUAL
};
}
filterFallback( f ) {
const { gl } = this;
if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) {
return gl.NEAREST;
}
return gl.LINEAR;
}
getGLTextureType( texture ) {
const { gl } = this;
let glTextureType;
if ( texture.isCubeTexture === true ) {
glTextureType = gl.TEXTURE_CUBE_MAP;
} else {
glTextureType = gl.TEXTURE_2D;
}
return glTextureType;
}
getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) {
const { gl, extensions } = this;
if ( internalFormatName !== null ) {
if ( gl[ internalFormatName ] !== undefined ) return gl[ internalFormatName ];
console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' );
}
let internalFormat = glFormat;
if ( glFormat === gl.RED ) {
if ( glType === gl.FLOAT ) internalFormat = gl.R32F;
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.R16F;
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8;
}
if ( glFormat === gl.RED_INTEGER ) {
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8UI;
if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.R16UI;
if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.R32UI;
if ( glType === gl.BYTE ) internalFormat = gl.R8I;
if ( glType === gl.SHORT ) internalFormat = gl.R16I;
if ( glType === gl.INT ) internalFormat = gl.R32I;
}
if ( glFormat === gl.RG ) {
if ( glType === gl.FLOAT ) internalFormat = gl.RG32F;
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RG16F;
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RG8;
}
if ( glFormat === gl.RGBA ) {
if ( glType === gl.FLOAT ) internalFormat = gl.RGBA32F;
if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGBA16F;
if ( glType === gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? gl.SRGB8_ALPHA8 : gl.RGBA8;
if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGBA4;
if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1;
}
if ( glFormat === gl.DEPTH_COMPONENT ) {
if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.DEPTH_COMPONENT24;
if ( glType === gl.FLOAT ) internalFormat = gl.DEPTH_COMPONENT32F;
}
if ( glFormat === gl.DEPTH_STENCIL ) {
if ( glType === gl.UNSIGNED_INT_24_8 ) internalFormat = gl.DEPTH24_STENCIL8;
}
if ( internalFormat === gl.R16F || internalFormat === gl.R32F ||
internalFormat === gl.RG16F || internalFormat === gl.RG32F ||
internalFormat === gl.RGBA16F || internalFormat === gl.RGBA32F ) {
extensions.get( 'EXT_color_buffer_float' );
}
return internalFormat;
}
setTextureParameters( textureType, texture ) {
const { gl, extensions } = this;
gl.texParameteri( textureType, gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] );
gl.texParameteri( textureType, gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] );
if ( textureType === gl.TEXTURE_3D || textureType === gl.TEXTURE_2D_ARRAY ) {
gl.texParameteri( textureType, gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] );
}
gl.texParameteri( textureType, gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] );
gl.texParameteri( textureType, gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] );
if ( texture.compareFunction ) {
gl.texParameteri( textureType, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE );
gl.texParameteri( textureType, gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] );
}
if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) {
//extension = extensions.get( 'EXT_texture_filter_anisotropic' );
if ( texture.magFilter === NearestFilter ) return;
if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return;
if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2
if ( texture.anisotropy > 1 /*|| properties.get( texture ).__currentAnisotropy*/ ) {
//gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );
//properties.get( texture ).__currentAnisotropy = texture.anisotropy;
}
}
}
}
export default WebGLTextureUtils;

View File

@@ -0,0 +1,242 @@
import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, _SRGBAFormat, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, SRGBColorSpace, NoColorSpace } from 'three';
class WebGLUtils {
constructor( backend ) {
this.backend = backend;
this.gl = this.backend.gl;
this.extensions = backend.extensions;
}
convert( p, colorSpace = NoColorSpace ) {
const { gl, extensions } = this;
let extension;
if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;
if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;
if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;
if ( p === ByteType ) return gl.BYTE;
if ( p === ShortType ) return gl.SHORT;
if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;
if ( p === IntType ) return gl.INT;
if ( p === UnsignedIntType ) return gl.UNSIGNED_INT;
if ( p === FloatType ) return gl.FLOAT;
if ( p === HalfFloatType ) {
return gl.HALF_FLOAT;
}
if ( p === AlphaFormat ) return gl.ALPHA;
if ( p === RGBAFormat ) return gl.RGBA;
if ( p === LuminanceFormat ) return gl.LUMINANCE;
if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;
if ( p === DepthFormat ) return gl.DEPTH_COMPONENT;
if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;
// WebGL2 formats.
if ( p === RedFormat ) return gl.RED;
if ( p === RedIntegerFormat ) return gl.RED_INTEGER;
if ( p === RGFormat ) return gl.RG;
if ( p === RGIntegerFormat ) return gl.RG_INTEGER;
if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER;
// S3TC
if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {
if ( colorSpace === SRGBColorSpace ) {
extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' );
if ( extension !== null ) {
if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT;
if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
} else {
return null;
}
} else {
extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );
if ( extension !== null ) {
if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;
if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;
if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;
if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;
} else {
return null;
}
}
}
// PVRTC
if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {
extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );
if ( extension !== null ) {
if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
} else {
return null;
}
}
// ETC1
if ( p === RGB_ETC1_Format ) {
extension = extensions.get( 'WEBGL_compressed_texture_etc1' );
if ( extension !== null ) {
return extension.COMPRESSED_RGB_ETC1_WEBGL;
} else {
return null;
}
}
// ETC2
if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) {
extension = extensions.get( 'WEBGL_compressed_texture_etc' );
if ( extension !== null ) {
if ( p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2;
if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC;
} else {
return null;
}
}
// ASTC
if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||
p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||
p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||
p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||
p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {
extension = extensions.get( 'WEBGL_compressed_texture_astc' );
if ( extension !== null ) {
if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR;
if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR;
if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR;
if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR;
if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR;
if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR;
if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR;
if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR;
if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR;
if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR;
if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR;
if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR;
if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR;
if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR;
} else {
return null;
}
}
// BPTC
if ( p === RGBA_BPTC_Format ) {
extension = extensions.get( 'EXT_texture_compression_bptc' );
if ( extension !== null ) {
if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
} else {
return null;
}
}
// RGTC
if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) {
extension = extensions.get( 'EXT_texture_compression_rgtc' );
if ( extension !== null ) {
if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT;
if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT;
if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT;
if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
} else {
return null;
}
}
//
if ( p === UnsignedInt248Type ) {
return gl.UNSIGNED_INT_24_8;
}
// if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats)
return ( gl[ p ] !== undefined ) ? gl[ p ] : null;
}
}
export default WebGLUtils;