first commit

This commit is contained in:
2026-06-24 09:48:54 +02:00
commit 41e62ddcad
33739 changed files with 4266226 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
import { Data3DTexture } from '../textures/Data3DTexture.js';
class WebGL3DRenderTarget extends WebGLRenderTarget {
constructor( width = 1, height = 1, depth = 1, options = {} ) {
super( width, height, options );
this.isWebGL3DRenderTarget = true;
this.depth = depth;
this.texture = new Data3DTexture( null, width, height, depth );
this.texture.isRenderTargetTexture = true;
}
}
export { WebGL3DRenderTarget };
+22
View File
@@ -0,0 +1,22 @@
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
import { DataArrayTexture } from '../textures/DataArrayTexture.js';
class WebGLArrayRenderTarget extends WebGLRenderTarget {
constructor( width = 1, height = 1, depth = 1, options = {} ) {
super( width, height, options );
this.isWebGLArrayRenderTarget = true;
this.depth = depth;
this.texture = new DataArrayTexture( null, width, height, depth );
this.texture.isRenderTargetTexture = true;
}
}
export { WebGLArrayRenderTarget };
+146
View File
@@ -0,0 +1,146 @@
import { BackSide, LinearFilter, LinearMipmapLinearFilter, NoBlending } from '../constants.js';
import { Mesh } from '../objects/Mesh.js';
import { BoxGeometry } from '../geometries/BoxGeometry.js';
import { ShaderMaterial } from '../materials/ShaderMaterial.js';
import { cloneUniforms } from './shaders/UniformsUtils.js';
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
import { CubeCamera } from '../cameras/CubeCamera.js';
import { CubeTexture } from '../textures/CubeTexture.js';
class WebGLCubeRenderTarget extends WebGLRenderTarget {
constructor( size = 1, options = {} ) {
super( size, size, options );
this.isWebGLCubeRenderTarget = true;
const image = { width: size, height: size, depth: 1 };
const images = [ image, image, image, image, image, image ];
this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace );
// By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)
// in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,
// in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.
// three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped
// and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture
// as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures).
this.texture.isRenderTargetTexture = true;
this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;
this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;
}
fromEquirectangularTexture( renderer, texture ) {
this.texture.type = texture.type;
this.texture.colorSpace = texture.colorSpace;
this.texture.generateMipmaps = texture.generateMipmaps;
this.texture.minFilter = texture.minFilter;
this.texture.magFilter = texture.magFilter;
const shader = {
uniforms: {
tEquirect: { value: null },
},
vertexShader: /* glsl */`
varying vec3 vWorldDirection;
vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
}
void main() {
vWorldDirection = transformDirection( position, modelMatrix );
#include <begin_vertex>
#include <project_vertex>
}
`,
fragmentShader: /* glsl */`
uniform sampler2D tEquirect;
varying vec3 vWorldDirection;
#include <common>
void main() {
vec3 direction = normalize( vWorldDirection );
vec2 sampleUV = equirectUv( direction );
gl_FragColor = texture2D( tEquirect, sampleUV );
}
`
};
const geometry = new BoxGeometry( 5, 5, 5 );
const material = new ShaderMaterial( {
name: 'CubemapFromEquirect',
uniforms: cloneUniforms( shader.uniforms ),
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader,
side: BackSide,
blending: NoBlending
} );
material.uniforms.tEquirect.value = texture;
const mesh = new Mesh( geometry, material );
const currentMinFilter = texture.minFilter;
// Avoid blurred poles
if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;
const camera = new CubeCamera( 1, 10, this );
camera.update( renderer, mesh );
texture.minFilter = currentMinFilter;
mesh.geometry.dispose();
mesh.material.dispose();
return this;
}
clear( renderer, color, depth, stencil ) {
const currentRenderTarget = renderer.getRenderTarget();
for ( let i = 0; i < 6; i ++ ) {
renderer.setRenderTarget( this, i );
renderer.clear( color, depth, stencil );
}
renderer.setRenderTarget( currentRenderTarget );
}
}
export { WebGLCubeRenderTarget };
+15
View File
@@ -0,0 +1,15 @@
import { RenderTarget } from '../core/RenderTarget.js';
class WebGLRenderTarget extends RenderTarget {
constructor( width = 1, height = 1, options = {} ) {
super( width, height, options );
this.isWebGLRenderTarget = true;
}
}
export { WebGLRenderTarget };
+2936
View File
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
class Animation {
constructor( nodes, info ) {
this.nodes = nodes;
this.info = info;
this._context = self;
this._animationLoop = null;
this._requestId = null;
}
start() {
const update = ( time, frame ) => {
this._requestId = this._context.requestAnimationFrame( update );
if ( this.info.autoReset === true ) this.info.reset();
this.nodes.nodeFrame.update();
this.info.frame = this.nodes.nodeFrame.frameId;
if ( this._animationLoop !== null ) this._animationLoop( time, frame );
};
update();
}
stop() {
this._context.cancelAnimationFrame( this._requestId );
this._requestId = null;
}
setAnimationLoop( callback ) {
this._animationLoop = callback;
}
setContext( context ) {
this._context = context;
}
dispose() {
this.stop();
}
}
export default Animation;
+82
View File
@@ -0,0 +1,82 @@
import DataMap from './DataMap.js';
import { AttributeType } from './Constants.js';
import { DynamicDrawUsage } from '../../constants.js';
class Attributes extends DataMap {
constructor( backend ) {
super();
this.backend = backend;
}
delete( attribute ) {
const attributeData = super.delete( attribute );
if ( attributeData !== undefined ) {
this.backend.destroyAttribute( attribute );
}
return attributeData;
}
update( attribute, type ) {
const data = this.get( attribute );
if ( data.version === undefined ) {
if ( type === AttributeType.VERTEX ) {
this.backend.createAttribute( attribute );
} else if ( type === AttributeType.INDEX ) {
this.backend.createIndexAttribute( attribute );
} else if ( type === AttributeType.STORAGE ) {
this.backend.createStorageAttribute( attribute );
} else if ( type === AttributeType.INDIRECT ) {
this.backend.createIndirectStorageAttribute( attribute );
}
data.version = this._getBufferAttribute( attribute ).version;
} else {
const bufferAttribute = this._getBufferAttribute( attribute );
if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) {
this.backend.updateAttribute( attribute );
data.version = bufferAttribute.version;
}
}
}
_getBufferAttribute( attribute ) {
if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
return attribute;
}
}
export default Attributes;
+200
View File
@@ -0,0 +1,200 @@
let vector2 = null;
let vector4 = null;
let color4 = null;
import Color4 from './Color4.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector4 } from '../../math/Vector4.js';
import { createCanvasElement } from '../../utils.js';
import { REVISION } from '../../constants.js';
class Backend {
constructor( parameters = {} ) {
this.parameters = Object.assign( {}, parameters );
this.data = new WeakMap();
this.renderer = null;
this.domElement = null;
}
async init( renderer ) {
this.renderer = renderer;
}
// render context
begin( /*renderContext*/ ) { }
finish( /*renderContext*/ ) { }
// render object
draw( /*renderObject, info*/ ) { }
// program
createProgram( /*program*/ ) { }
destroyProgram( /*program*/ ) { }
// bindings
createBindings( /*bingGroup, bindings*/ ) { }
updateBindings( /*bingGroup, bindings*/ ) { }
// pipeline
createRenderPipeline( /*renderObject*/ ) { }
createComputePipeline( /*computeNode, pipeline*/ ) { }
destroyPipeline( /*pipeline*/ ) { }
// cache key
needsRenderUpdate( /*renderObject*/ ) { } // return Boolean ( fast test )
getRenderCacheKey( /*renderObject*/ ) { } // return String
// node builder
createNodeBuilder( /*renderObject*/ ) { } // return NodeBuilder (ADD IT)
// textures
createSampler( /*texture*/ ) { }
createDefaultTexture( /*texture*/ ) { }
createTexture( /*texture*/ ) { }
copyTextureToBuffer( /*texture, x, y, width, height*/ ) {}
// attributes
createAttribute( /*attribute*/ ) { }
createIndexAttribute( /*attribute*/ ) { }
updateAttribute( /*attribute*/ ) { }
destroyAttribute( /*attribute*/ ) { }
// canvas
getContext() { }
updateSize() { }
// utils
resolveTimestampAsync( /*renderContext, type*/ ) { }
hasFeatureAsync( /*name*/ ) { } // return Boolean
hasFeature( /*name*/ ) { } // return Boolean
getInstanceCount( renderObject ) {
const { object, geometry } = renderObject;
return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.count > 1 ? object.count : 1 );
}
getDrawingBufferSize() {
vector2 = vector2 || new Vector2();
return this.renderer.getDrawingBufferSize( vector2 );
}
getScissor() {
vector4 = vector4 || new Vector4();
return this.renderer.getScissor( vector4 );
}
setScissorTest( /*boolean*/ ) { }
getClearColor() {
const renderer = this.renderer;
color4 = color4 || new Color4();
renderer.getClearColor( color4 );
color4.getRGB( color4, this.renderer.currentColorSpace );
return color4;
}
getDomElement() {
let domElement = this.domElement;
if ( domElement === null ) {
domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : createCanvasElement();
// OffscreenCanvas does not have setAttribute, see #22811
if ( 'setAttribute' in domElement ) domElement.setAttribute( 'data-engine', `three.js r${REVISION} webgpu` );
this.domElement = domElement;
}
return domElement;
}
// resource properties
set( object, value ) {
this.data.set( object, value );
}
get( object ) {
let map = this.data.get( object );
if ( map === undefined ) {
map = {};
this.data.set( object, map );
}
return map;
}
has( object ) {
return this.data.has( object );
}
delete( object ) {
this.data.delete( object );
}
dispose() { }
}
export default Backend;
+152
View File
@@ -0,0 +1,152 @@
import DataMap from './DataMap.js';
import Color4 from './Color4.js';
import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, backgroundRotation, modelViewProjection } from '../../nodes/TSL.js';
import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
import { Mesh } from '../../objects/Mesh.js';
import { SphereGeometry } from '../../geometries/SphereGeometry.js';
import { BackSide, LinearSRGBColorSpace } from '../../constants.js';
const _clearColor = /*@__PURE__*/ new Color4();
class Background extends DataMap {
constructor( renderer, nodes ) {
super();
this.renderer = renderer;
this.nodes = nodes;
}
update( scene, renderList, renderContext ) {
const renderer = this.renderer;
const background = this.nodes.getBackgroundNode( scene ) || scene.background;
let forceClear = false;
if ( background === null ) {
// no background settings, use clear color configuration from the renderer
renderer._clearColor.getRGB( _clearColor, LinearSRGBColorSpace );
_clearColor.a = renderer._clearColor.a;
} else if ( background.isColor === true ) {
// background is an opaque color
background.getRGB( _clearColor, LinearSRGBColorSpace );
_clearColor.a = 1;
forceClear = true;
} else if ( background.isNode === true ) {
const sceneData = this.get( scene );
const backgroundNode = background;
_clearColor.copy( renderer._clearColor );
let backgroundMesh = sceneData.backgroundMesh;
if ( backgroundMesh === undefined ) {
const backgroundMeshNode = context( vec4( backgroundNode ).mul( backgroundIntensity ), {
// @TODO: Add Texture2D support using node context
getUV: () => backgroundRotation.mul( normalWorld ),
getTextureLevel: () => backgroundBlurriness
} );
let viewProj = modelViewProjection();
viewProj = viewProj.setZ( viewProj.w );
const nodeMaterial = new NodeMaterial();
nodeMaterial.name = 'Background.material';
nodeMaterial.side = BackSide;
nodeMaterial.depthTest = false;
nodeMaterial.depthWrite = false;
nodeMaterial.fog = false;
nodeMaterial.lights = false;
nodeMaterial.vertexNode = viewProj;
nodeMaterial.colorNode = backgroundMeshNode;
sceneData.backgroundMeshNode = backgroundMeshNode;
sceneData.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
backgroundMesh.frustumCulled = false;
backgroundMesh.name = 'Background.mesh';
backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) {
this.matrixWorld.copyPosition( camera.matrixWorld );
};
}
const backgroundCacheKey = backgroundNode.getCacheKey();
if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
sceneData.backgroundMeshNode.node = vec4( backgroundNode ).mul( backgroundIntensity );
sceneData.backgroundMeshNode.needsUpdate = true;
backgroundMesh.material.needsUpdate = true;
sceneData.backgroundCacheKey = backgroundCacheKey;
}
renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null, null );
} else {
console.error( 'THREE.Renderer: Unsupported background configuration.', background );
}
//
if ( renderer.autoClear === true || forceClear === true ) {
const clearColorValue = renderContext.clearColorValue;
clearColorValue.r = _clearColor.r;
clearColorValue.g = _clearColor.g;
clearColorValue.b = _clearColor.b;
clearColorValue.a = _clearColor.a;
// premultiply alpha
if ( renderer.backend.isWebGLBackend === true || renderer.alpha === true ) {
clearColorValue.r *= clearColorValue.a;
clearColorValue.g *= clearColorValue.a;
clearColorValue.b *= clearColorValue.a;
}
//
renderContext.depthClearValue = renderer._clearDepth;
renderContext.stencilClearValue = renderer._clearStencil;
renderContext.clearColor = renderer.autoClearColor === true;
renderContext.clearDepth = renderer.autoClearDepth === true;
renderContext.clearStencil = renderer.autoClearStencil === true;
} else {
renderContext.clearColor = false;
renderContext.clearDepth = false;
renderContext.clearStencil = false;
}
}
}
export default Background;
+18
View File
@@ -0,0 +1,18 @@
let _id = 0;
class BindGroup {
constructor( name = '', bindings = [], index = 0, bindingsReference = [] ) {
this.name = name;
this.bindings = bindings;
this.index = index;
this.bindingsReference = bindingsReference;
this.id = _id ++;
}
}
export default BindGroup;
+25
View File
@@ -0,0 +1,25 @@
class Binding {
constructor( name = '' ) {
this.name = name;
this.visibility = 0;
}
setVisibility( visibility ) {
this.visibility |= visibility;
}
clone() {
return Object.assign( new this.constructor(), this );
}
}
export default Binding;
+220
View File
@@ -0,0 +1,220 @@
import DataMap from './DataMap.js';
import { AttributeType } from './Constants.js';
class Bindings extends DataMap {
constructor( backend, nodes, textures, attributes, pipelines, info ) {
super();
this.backend = backend;
this.textures = textures;
this.pipelines = pipelines;
this.attributes = attributes;
this.nodes = nodes;
this.info = info;
this.pipelines.bindings = this; // assign bindings to pipelines
}
getForRender( renderObject ) {
const bindings = renderObject.getBindings();
for ( const bindGroup of bindings ) {
const groupData = this.get( bindGroup );
if ( groupData.bindGroup === undefined ) {
// each object defines an array of bindings (ubos, textures, samplers etc.)
this._init( bindGroup );
this.backend.createBindings( bindGroup, bindings, 0 );
groupData.bindGroup = bindGroup;
}
}
return bindings;
}
getForCompute( computeNode ) {
const bindings = this.nodes.getForCompute( computeNode ).bindings;
for ( const bindGroup of bindings ) {
const groupData = this.get( bindGroup );
if ( groupData.bindGroup === undefined ) {
this._init( bindGroup );
this.backend.createBindings( bindGroup, bindings, 0 );
groupData.bindGroup = bindGroup;
}
}
return bindings;
}
updateForCompute( computeNode ) {
this._updateBindings( this.getForCompute( computeNode ) );
}
updateForRender( renderObject ) {
this._updateBindings( this.getForRender( renderObject ) );
}
_updateBindings( bindings ) {
for ( const bindGroup of bindings ) {
this._update( bindGroup, bindings );
}
}
_init( bindGroup ) {
for ( const binding of bindGroup.bindings ) {
if ( binding.isSampledTexture ) {
this.textures.updateTexture( binding.texture );
} else if ( binding.isStorageBuffer ) {
const attribute = binding.attribute;
const attributeType = attribute.isIndirectStorageBufferAttribute ? AttributeType.INDIRECT : AttributeType.STORAGE;
this.attributes.update( attribute, attributeType );
}
}
}
_update( bindGroup, bindings ) {
const { backend } = this;
let needsBindingsUpdate = false;
let cacheBindings = true;
let cacheIndex = 0;
let version = 0;
// iterate over all bindings and check if buffer updates or a new binding group is required
for ( const binding of bindGroup.bindings ) {
if ( binding.isNodeUniformsGroup ) {
const updated = this.nodes.updateGroup( binding );
if ( ! updated ) continue;
}
if ( binding.isUniformBuffer ) {
const updated = binding.update();
if ( updated ) {
backend.updateBinding( binding );
}
} else if ( binding.isSampler ) {
binding.update();
} else if ( binding.isSampledTexture ) {
const texturesTextureData = this.textures.get( binding.texture );
if ( binding.needsBindingsUpdate( texturesTextureData.generation ) ) needsBindingsUpdate = true;
const updated = binding.update();
const texture = binding.texture;
if ( updated ) {
this.textures.updateTexture( texture );
}
const textureData = backend.get( texture );
if ( textureData.externalTexture !== undefined || texturesTextureData.isDefaultTexture ) {
cacheBindings = false;
} else {
cacheIndex = cacheIndex * 10 + texture.id;
version += texture.version;
}
if ( backend.isWebGPUBackend === true && textureData.texture === undefined && textureData.externalTexture === undefined ) {
// TODO: Remove this once we found why updated === false isn't bound to a texture in the WebGPU backend
console.error( 'Bindings._update: binding should be available:', binding, updated, texture, binding.textureNode.value, needsBindingsUpdate );
this.textures.updateTexture( texture );
needsBindingsUpdate = true;
}
if ( texture.isStorageTexture === true ) {
const textureData = this.get( texture );
if ( binding.store === true ) {
textureData.needsMipmap = true;
} else if ( this.textures.needsMipmaps( texture ) && textureData.needsMipmap === true ) {
this.backend.generateMipmaps( texture );
textureData.needsMipmap = false;
}
}
}
}
if ( needsBindingsUpdate === true ) {
this.backend.updateBindings( bindGroup, bindings, cacheBindings ? cacheIndex : 0, version );
}
}
}
export default Bindings;
+38
View File
@@ -0,0 +1,38 @@
import Binding from './Binding.js';
import { getFloatLength } from './BufferUtils.js';
class Buffer extends Binding {
constructor( name, buffer = null ) {
super( name );
this.isBuffer = true;
this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT;
this._buffer = buffer;
}
get byteLength() {
return getFloatLength( this._buffer.byteLength );
}
get buffer() {
return this._buffer;
}
update() {
return true;
}
}
export default Buffer;
+33
View File
@@ -0,0 +1,33 @@
import { GPU_CHUNK_BYTES } from './Constants.js';
function getFloatLength( floatLength ) {
// ensure chunk size alignment (STD140 layout)
return floatLength + ( ( GPU_CHUNK_BYTES - ( floatLength % GPU_CHUNK_BYTES ) ) % GPU_CHUNK_BYTES );
}
function getVectorLength( count, vectorLength = 4 ) {
const strideLength = getStrideLength( vectorLength );
const floatLength = strideLength * count;
return getFloatLength( floatLength );
}
function getStrideLength( vectorLength ) {
const strideLength = 4;
return vectorLength + ( ( strideLength - ( vectorLength % strideLength ) ) % strideLength );
}
export {
getFloatLength,
getVectorLength,
getStrideLength
};
+26
View File
@@ -0,0 +1,26 @@
import { Group } from '../../objects/Group.js';
class BundleGroup extends Group {
constructor() {
super();
this.isBundleGroup = true;
this.type = 'BundleGroup';
this.static = true;
this.version = 0;
}
set needsUpdate( value ) {
if ( value === true ) this.version ++;
}
}
export default BundleGroup;
+59
View File
@@ -0,0 +1,59 @@
export default class ChainMap {
constructor() {
this.weakMap = new WeakMap();
}
get( keys ) {
let map = this.weakMap;
for ( let i = 0; i < keys.length; i ++ ) {
map = map.get( keys[ i ] );
if ( map === undefined ) return undefined;
}
return map.get( keys[ keys.length - 1 ] );
}
set( keys, value ) {
let map = this.weakMap;
for ( let i = 0; i < keys.length; i ++ ) {
const key = keys[ i ];
if ( map.has( key ) === false ) map.set( key, new WeakMap() );
map = map.get( key );
}
return map.set( keys[ keys.length - 1 ], value );
}
delete( keys ) {
let map = this.weakMap;
for ( let i = 0; i < keys.length; i ++ ) {
map = map.get( keys[ i ] );
if ( map === undefined ) return false;
}
return map.delete( keys[ keys.length - 1 ] );
}
}
+169
View File
@@ -0,0 +1,169 @@
import { Matrix3 } from '../../math/Matrix3.js';
import { Plane } from '../../math/Plane.js';
import { Vector4 } from '../../math/Vector4.js';
const _plane = /*@__PURE__*/ new Plane();
class ClippingContext {
constructor( parentContext = null ) {
this.version = 0;
this.clipIntersection = null;
this.cacheKey = '';
if ( parentContext === null ) {
this.intersectionPlanes = [];
this.unionPlanes = [];
this.viewNormalMatrix = new Matrix3();
this.clippingGroupContexts = new WeakMap();
this.shadowPass = false;
} else {
this.viewNormalMatrix = parentContext.viewNormalMatrix;
this.clippingGroupContexts = parentContext.clippingGroupContexts;
this.shadowPass = parentContext.shadowPass;
this.viewMatrix = parentContext.viewMatrix;
}
this.parentVersion = null;
}
projectPlanes( source, destination, offset ) {
const l = source.length;
for ( let i = 0; i < l; i ++ ) {
_plane.copy( source[ i ] ).applyMatrix4( this.viewMatrix, this.viewNormalMatrix );
const v = destination[ offset + i ];
const normal = _plane.normal;
v.x = - normal.x;
v.y = - normal.y;
v.z = - normal.z;
v.w = _plane.constant;
}
}
updateGlobal( scene, camera ) {
this.shadowPass = ( scene.overrideMaterial !== null && scene.overrideMaterial.isShadowNodeMaterial );
this.viewMatrix = camera.matrixWorldInverse;
this.viewNormalMatrix.getNormalMatrix( this.viewMatrix );
}
update( parentContext, clippingGroup ) {
let update = false;
if ( parentContext.version !== this.parentVersion ) {
this.intersectionPlanes = Array.from( parentContext.intersectionPlanes );
this.unionPlanes = Array.from( parentContext.unionPlanes );
this.parentVersion = parentContext.version;
}
if ( this.clipIntersection !== clippingGroup.clipIntersection ) {
this.clipIntersection = clippingGroup.clipIntersection;
if ( this.clipIntersection ) {
this.unionPlanes.length = parentContext.unionPlanes.length;
} else {
this.intersectionPlanes.length = parentContext.intersectionPlanes.length;
}
}
const srcClippingPlanes = clippingGroup.clippingPlanes;
const l = srcClippingPlanes.length;
let dstClippingPlanes;
let offset;
if ( this.clipIntersection ) {
dstClippingPlanes = this.intersectionPlanes;
offset = parentContext.intersectionPlanes.length;
} else {
dstClippingPlanes = this.unionPlanes;
offset = parentContext.unionPlanes.length;
}
if ( dstClippingPlanes.length !== offset + l ) {
dstClippingPlanes.length = offset + l;
for ( let i = 0; i < l; i ++ ) {
dstClippingPlanes[ offset + i ] = new Vector4();
}
update = true;
}
this.projectPlanes( srcClippingPlanes, dstClippingPlanes, offset );
if ( update ) {
this.version ++;
this.cacheKey = `${ this.intersectionPlanes.length }:${ this.unionPlanes.length }`;
}
}
getGroupContext( clippingGroup ) {
if ( this.shadowPass && ! clippingGroup.clipShadows ) return this;
let context = this.clippingGroupContexts.get( clippingGroup );
if ( context === undefined ) {
context = new ClippingContext( this );
this.clippingGroupContexts.set( clippingGroup, context );
}
context.update( this, clippingGroup );
return context;
}
get unionClippingCount() {
return this.unionPlanes.length;
}
}
export default ClippingContext;
+37
View File
@@ -0,0 +1,37 @@
import { Color } from '../../math/Color.js';
class Color4 extends Color {
constructor( r, g, b, a = 1 ) {
super( r, g, b );
this.a = a;
}
set( r, g, b, a = 1 ) {
this.a = a;
return super.set( r, g, b );
}
copy( color ) {
if ( color.a !== undefined ) this.a = color.a;
return super.copy( color );
}
clone() {
return new this.constructor( this.r, this.g, this.b, this.a );
}
}
export default Color4;
+17
View File
@@ -0,0 +1,17 @@
import Pipeline from './Pipeline.js';
class ComputePipeline extends Pipeline {
constructor( cacheKey, computeProgram ) {
super( cacheKey );
this.computeProgram = computeProgram;
this.isComputePipeline = true;
}
}
export default ComputePipeline;
+15
View File
@@ -0,0 +1,15 @@
export const AttributeType = {
VERTEX: 1,
INDEX: 2,
STORAGE: 3,
INDIRECT: 4
};
// size of a chunk in bytes (STD140 layout)
export const GPU_CHUNK_BYTES = 16;
// @TODO: Move to src/constants.js
export const BlendColorFactor = 211;
export const OneMinusBlendColorFactor = 212;
+77
View File
@@ -0,0 +1,77 @@
import { equirectUV } from '../../nodes/utils/EquirectUVNode.js';
import { texture as TSL_Texture } from '../../nodes/accessors/TextureNode.js';
import { positionWorldDirection } from '../../nodes/accessors/Position.js';
import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
import { WebGLCubeRenderTarget } from '../../renderers/WebGLCubeRenderTarget.js';
import { Scene } from '../../scenes/Scene.js';
import { CubeCamera } from '../../cameras/CubeCamera.js';
import { BoxGeometry } from '../../geometries/BoxGeometry.js';
import { Mesh } from '../../objects/Mesh.js';
import { BackSide, NoBlending, LinearFilter, LinearMipmapLinearFilter } from '../../constants.js';
// @TODO: Consider rename WebGLCubeRenderTarget to just CubeRenderTarget
class CubeRenderTarget extends WebGLCubeRenderTarget {
constructor( size = 1, options = {} ) {
super( size, options );
this.isCubeRenderTarget = true;
}
fromEquirectangularTexture( renderer, texture ) {
const currentMinFilter = texture.minFilter;
const currentGenerateMipmaps = texture.generateMipmaps;
texture.generateMipmaps = true;
this.texture.type = texture.type;
this.texture.colorSpace = texture.colorSpace;
this.texture.generateMipmaps = texture.generateMipmaps;
this.texture.minFilter = texture.minFilter;
this.texture.magFilter = texture.magFilter;
const geometry = new BoxGeometry( 5, 5, 5 );
const uvNode = equirectUV( positionWorldDirection );
const material = new NodeMaterial();
material.colorNode = TSL_Texture( texture, uvNode, 0 );
material.side = BackSide;
material.blending = NoBlending;
const mesh = new Mesh( geometry, material );
const scene = new Scene();
scene.add( mesh );
// Avoid blurred poles
if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;
const camera = new CubeCamera( 1, 10, this );
const currentMRT = renderer.getMRT();
renderer.setMRT( null );
camera.update( renderer, scene );
renderer.setMRT( currentMRT );
texture.minFilter = currentMinFilter;
texture.currentGenerateMipmaps = currentGenerateMipmaps;
mesh.geometry.dispose();
mesh.material.dispose();
return this;
}
}
export default CubeRenderTarget;
+54
View File
@@ -0,0 +1,54 @@
class DataMap {
constructor() {
this.data = new WeakMap();
}
get( object ) {
let map = this.data.get( object );
if ( map === undefined ) {
map = {};
this.data.set( object, map );
}
return map;
}
delete( object ) {
let map;
if ( this.data.has( object ) ) {
map = this.data.get( object );
this.data.delete( object );
}
return map;
}
has( object ) {
return this.data.has( object );
}
dispose() {
this.data = new WeakMap();
}
}
export default DataMap;
+267
View File
@@ -0,0 +1,267 @@
import DataMap from './DataMap.js';
import { AttributeType } from './Constants.js';
import { Uint16BufferAttribute, Uint32BufferAttribute } from '../../core/BufferAttribute.js';
function arrayNeedsUint32( array ) {
// assumes larger values usually on last
for ( let i = array.length - 1; i >= 0; -- i ) {
if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
}
return false;
}
function getWireframeVersion( geometry ) {
return ( geometry.index !== null ) ? geometry.index.version : geometry.attributes.position.version;
}
function getWireframeIndex( geometry ) {
const indices = [];
const geometryIndex = geometry.index;
const geometryPosition = geometry.attributes.position;
if ( geometryIndex !== null ) {
const array = geometryIndex.array;
for ( let i = 0, l = array.length; i < l; i += 3 ) {
const a = array[ i + 0 ];
const b = array[ i + 1 ];
const c = array[ i + 2 ];
indices.push( a, b, b, c, c, a );
}
} else {
const array = geometryPosition.array;
for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {
const a = i + 0;
const b = i + 1;
const c = i + 2;
indices.push( a, b, b, c, c, a );
}
}
const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );
attribute.version = getWireframeVersion( geometry );
return attribute;
}
class Geometries extends DataMap {
constructor( attributes, info ) {
super();
this.attributes = attributes;
this.info = info;
this.wireframes = new WeakMap();
this.attributeCall = new WeakMap();
}
has( renderObject ) {
const geometry = renderObject.geometry;
return super.has( geometry ) && this.get( geometry ).initialized === true;
}
updateForRender( renderObject ) {
if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
this.updateAttributes( renderObject );
}
initGeometry( renderObject ) {
const geometry = renderObject.geometry;
const geometryData = this.get( geometry );
geometryData.initialized = true;
this.info.memory.geometries ++;
const onDispose = () => {
this.info.memory.geometries --;
const index = geometry.index;
const geometryAttributes = renderObject.getAttributes();
if ( index !== null ) {
this.attributes.delete( index );
}
for ( const geometryAttribute of geometryAttributes ) {
this.attributes.delete( geometryAttribute );
}
const wireframeAttribute = this.wireframes.get( geometry );
if ( wireframeAttribute !== undefined ) {
this.attributes.delete( wireframeAttribute );
}
geometry.removeEventListener( 'dispose', onDispose );
};
geometry.addEventListener( 'dispose', onDispose );
}
updateAttributes( renderObject ) {
// attributes
const attributes = renderObject.getAttributes();
for ( const attribute of attributes ) {
if ( attribute.isStorageBufferAttribute || attribute.isStorageInstancedBufferAttribute ) {
this.updateAttribute( attribute, AttributeType.STORAGE );
} else {
this.updateAttribute( attribute, AttributeType.VERTEX );
}
}
// indexes
const index = this.getIndex( renderObject );
if ( index !== null ) {
this.updateAttribute( index, AttributeType.INDEX );
}
// indirect
const indirect = renderObject.geometry.indirect;
if ( indirect !== null ) {
this.updateAttribute( indirect, AttributeType.INDIRECT );
}
}
updateAttribute( attribute, type ) {
const callId = this.info.render.calls;
if ( ! attribute.isInterleavedBufferAttribute ) {
if ( this.attributeCall.get( attribute ) !== callId ) {
this.attributes.update( attribute, type );
this.attributeCall.set( attribute, callId );
}
} else {
if ( this.attributeCall.get( attribute ) === undefined ) {
this.attributes.update( attribute, type );
this.attributeCall.set( attribute, callId );
} else if ( this.attributeCall.get( attribute.data ) !== callId ) {
this.attributes.update( attribute, type );
this.attributeCall.set( attribute.data, callId );
this.attributeCall.set( attribute, callId );
}
}
}
getIndirect( renderObject ) {
return renderObject.geometry.indirect;
}
getIndex( renderObject ) {
const { geometry, material } = renderObject;
let index = geometry.index;
if ( material.wireframe === true ) {
const wireframes = this.wireframes;
let wireframeAttribute = wireframes.get( geometry );
if ( wireframeAttribute === undefined ) {
wireframeAttribute = getWireframeIndex( geometry );
wireframes.set( geometry, wireframeAttribute );
} else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) {
this.attributes.delete( wireframeAttribute );
wireframeAttribute = getWireframeIndex( geometry );
wireframes.set( geometry, wireframeAttribute );
}
index = wireframeAttribute;
}
return index;
}
}
export default Geometries;
@@ -0,0 +1,15 @@
import StorageBufferAttribute from './StorageBufferAttribute.js';
class IndirectStorageBufferAttribute extends StorageBufferAttribute {
constructor( array, itemSize ) {
super( array, itemSize, Uint32Array );
this.isIndirectStorageBufferAttribute = true;
}
}
export default IndirectStorageBufferAttribute;
+127
View File
@@ -0,0 +1,127 @@
class Info {
constructor() {
this.autoReset = true;
this.frame = 0;
this.calls = 0;
this.render = {
calls: 0,
frameCalls: 0,
drawCalls: 0,
triangles: 0,
points: 0,
lines: 0,
timestamp: 0,
previousFrameCalls: 0,
timestampCalls: 0
};
this.compute = {
calls: 0,
frameCalls: 0,
timestamp: 0,
previousFrameCalls: 0,
timestampCalls: 0
};
this.memory = {
geometries: 0,
textures: 0
};
}
update( object, count, instanceCount ) {
this.render.drawCalls ++;
if ( object.isMesh || object.isSprite ) {
this.render.triangles += instanceCount * ( count / 3 );
} else if ( object.isPoints ) {
this.render.points += instanceCount * count;
} else if ( object.isLineSegments ) {
this.render.lines += instanceCount * ( count / 2 );
} else if ( object.isLine ) {
this.render.lines += instanceCount * ( count - 1 );
} else {
console.error( 'THREE.WebGPUInfo: Unknown object type.' );
}
}
updateTimestamp( type, time ) {
if ( this[ type ].timestampCalls === 0 ) {
this[ type ].timestamp = 0;
}
this[ type ].timestamp += time;
this[ type ].timestampCalls ++;
if ( this[ type ].timestampCalls >= this[ type ].previousFrameCalls ) {
this[ type ].timestampCalls = 0;
}
}
reset() {
const previousRenderFrameCalls = this.render.frameCalls;
this.render.previousFrameCalls = previousRenderFrameCalls;
const previousComputeFrameCalls = this.compute.frameCalls;
this.compute.previousFrameCalls = previousComputeFrameCalls;
this.render.drawCalls = 0;
this.render.frameCalls = 0;
this.compute.frameCalls = 0;
this.render.triangles = 0;
this.render.points = 0;
this.render.lines = 0;
}
dispose() {
this.reset();
this.calls = 0;
this.render.calls = 0;
this.compute.calls = 0;
this.render.timestamp = 0;
this.compute.timestamp = 0;
this.memory.geometries = 0;
this.memory.textures = 0;
}
}
export default Info;
+45
View File
@@ -0,0 +1,45 @@
import { LightsNode } from '../../nodes/Nodes.js';
import ChainMap from './ChainMap.js';
const _defaultLights = /*@__PURE__*/ new LightsNode();
class Lighting extends ChainMap {
constructor() {
super();
}
createNode( lights = [] ) {
return new LightsNode().setLights( lights );
}
getNode( scene, camera ) {
// ignore post-processing
if ( scene.isQuadMesh ) return _defaultLights;
// tiled lighting
const keys = [ scene, camera ];
let node = this.get( keys );
if ( node === undefined ) {
node = this.createNode();
this.set( keys, node );
}
return node;
}
}
export default Lighting;
+13
View File
@@ -0,0 +1,13 @@
class Pipeline {
constructor( cacheKey ) {
this.cacheKey = cacheKey;
this.usedTimes = 0;
}
}
export default Pipeline;
+322
View File
@@ -0,0 +1,322 @@
import DataMap from './DataMap.js';
import RenderPipeline from './RenderPipeline.js';
import ComputePipeline from './ComputePipeline.js';
import ProgrammableStage from './ProgrammableStage.js';
class Pipelines extends DataMap {
constructor( backend, nodes ) {
super();
this.backend = backend;
this.nodes = nodes;
this.bindings = null; // set by the bindings
this.caches = new Map();
this.programs = {
vertex: new Map(),
fragment: new Map(),
compute: new Map()
};
}
getForCompute( computeNode, bindings ) {
const { backend } = this;
const data = this.get( computeNode );
if ( this._needsComputeUpdate( computeNode ) ) {
const previousPipeline = data.pipeline;
if ( previousPipeline ) {
previousPipeline.usedTimes --;
previousPipeline.computeProgram.usedTimes --;
}
// get shader
const nodeBuilderState = this.nodes.getForCompute( computeNode );
// programmable stage
let stageCompute = this.programs.compute.get( nodeBuilderState.computeShader );
if ( stageCompute === undefined ) {
if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram );
stageCompute = new ProgrammableStage( nodeBuilderState.computeShader, 'compute', nodeBuilderState.transforms, nodeBuilderState.nodeAttributes );
this.programs.compute.set( nodeBuilderState.computeShader, stageCompute );
backend.createProgram( stageCompute );
}
// determine compute pipeline
const cacheKey = this._getComputeCacheKey( computeNode, stageCompute );
let pipeline = this.caches.get( cacheKey );
if ( pipeline === undefined ) {
if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline );
pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings );
}
// keep track of all used times
pipeline.usedTimes ++;
stageCompute.usedTimes ++;
//
data.version = computeNode.version;
data.pipeline = pipeline;
}
return data.pipeline;
}
getForRender( renderObject, promises = null ) {
const { backend } = this;
const data = this.get( renderObject );
if ( this._needsRenderUpdate( renderObject ) ) {
const previousPipeline = data.pipeline;
if ( previousPipeline ) {
previousPipeline.usedTimes --;
previousPipeline.vertexProgram.usedTimes --;
previousPipeline.fragmentProgram.usedTimes --;
}
// get shader
const nodeBuilderState = renderObject.getNodeBuilderState();
// programmable stages
let stageVertex = this.programs.vertex.get( nodeBuilderState.vertexShader );
if ( stageVertex === undefined ) {
if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram );
stageVertex = new ProgrammableStage( nodeBuilderState.vertexShader, 'vertex' );
this.programs.vertex.set( nodeBuilderState.vertexShader, stageVertex );
backend.createProgram( stageVertex );
}
let stageFragment = this.programs.fragment.get( nodeBuilderState.fragmentShader );
if ( stageFragment === undefined ) {
if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram );
stageFragment = new ProgrammableStage( nodeBuilderState.fragmentShader, 'fragment' );
this.programs.fragment.set( nodeBuilderState.fragmentShader, stageFragment );
backend.createProgram( stageFragment );
}
// determine render pipeline
const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
let pipeline = this.caches.get( cacheKey );
if ( pipeline === undefined ) {
if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline );
pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises );
} else {
renderObject.pipeline = pipeline;
}
// keep track of all used times
pipeline.usedTimes ++;
stageVertex.usedTimes ++;
stageFragment.usedTimes ++;
//
data.pipeline = pipeline;
}
return data.pipeline;
}
delete( object ) {
const pipeline = this.get( object ).pipeline;
if ( pipeline ) {
// pipeline
pipeline.usedTimes --;
if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline );
// programs
if ( pipeline.isComputePipeline ) {
pipeline.computeProgram.usedTimes --;
if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram );
} else {
pipeline.fragmentProgram.usedTimes --;
pipeline.vertexProgram.usedTimes --;
if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram );
if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram );
}
}
return super.delete( object );
}
dispose() {
super.dispose();
this.caches = new Map();
this.programs = {
vertex: new Map(),
fragment: new Map(),
compute: new Map()
};
}
updateForRender( renderObject ) {
this.getForRender( renderObject );
}
_getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) {
// check for existing pipeline
cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute );
let pipeline = this.caches.get( cacheKey );
if ( pipeline === undefined ) {
pipeline = new ComputePipeline( cacheKey, stageCompute );
this.caches.set( cacheKey, pipeline );
this.backend.createComputePipeline( pipeline, bindings );
}
return pipeline;
}
_getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ) {
// check for existing pipeline
cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
let pipeline = this.caches.get( cacheKey );
if ( pipeline === undefined ) {
pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment );
this.caches.set( cacheKey, pipeline );
renderObject.pipeline = pipeline;
this.backend.createRenderPipeline( renderObject, promises );
}
return pipeline;
}
_getComputeCacheKey( computeNode, stageCompute ) {
return computeNode.id + ',' + stageCompute.id;
}
_getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey( renderObject );
}
_releasePipeline( pipeline ) {
this.caches.delete( pipeline.cacheKey );
}
_releaseProgram( program ) {
const code = program.code;
const stage = program.stage;
this.programs[ stage ].delete( code );
}
_needsComputeUpdate( computeNode ) {
const data = this.get( computeNode );
return data.pipeline === undefined || data.version !== computeNode.version;
}
_needsRenderUpdate( renderObject ) {
const data = this.get( renderObject );
return data.pipeline === undefined || this.backend.needsRenderUpdate( renderObject );
}
}
export default Pipelines;
+90
View File
@@ -0,0 +1,90 @@
import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
import { vec4, renderOutput } from '../../nodes/TSL.js';
import { LinearSRGBColorSpace, NoToneMapping } from '../../constants.js';
import QuadMesh from '../../renderers/common/QuadMesh.js';
const _material = /*@__PURE__*/ new NodeMaterial();
const _quadMesh = /*@__PURE__*/ new QuadMesh( _material );
class PostProcessing {
constructor( renderer, outputNode = vec4( 0, 0, 1, 1 ) ) {
this.renderer = renderer;
this.outputNode = outputNode;
this.outputColorTransform = true;
this.needsUpdate = true;
_material.name = 'PostProcessing';
}
render() {
this.update();
const renderer = this.renderer;
const toneMapping = renderer.toneMapping;
const outputColorSpace = renderer.outputColorSpace;
renderer.toneMapping = NoToneMapping;
renderer.outputColorSpace = LinearSRGBColorSpace;
//
_quadMesh.render( renderer );
//
renderer.toneMapping = toneMapping;
renderer.outputColorSpace = outputColorSpace;
}
update() {
if ( this.needsUpdate === true ) {
const renderer = this.renderer;
const toneMapping = renderer.toneMapping;
const outputColorSpace = renderer.outputColorSpace;
_quadMesh.material.fragmentNode = this.outputColorTransform === true ? renderOutput( this.outputNode, toneMapping, outputColorSpace ) : this.outputNode.context( { toneMapping, outputColorSpace } );
_quadMesh.material.needsUpdate = true;
this.needsUpdate = false;
}
}
async renderAsync() {
this.update();
const renderer = this.renderer;
const toneMapping = renderer.toneMapping;
const outputColorSpace = renderer.outputColorSpace;
renderer.toneMapping = NoToneMapping;
renderer.outputColorSpace = LinearSRGBColorSpace;
//
await _quadMesh.renderAsync( renderer );
//
renderer.toneMapping = toneMapping;
renderer.outputColorSpace = outputColorSpace;
}
}
export default PostProcessing;
+86
View File
@@ -0,0 +1,86 @@
import { Color } from '../../math/Color.js';
// renderer state
export function saveRendererState( renderer, state = {} ) {
state.toneMapping = renderer.toneMapping;
state.toneMappingExposure = renderer.toneMappingExposure;
state.outputColorSpace = renderer.outputColorSpace;
state.renderTarget = renderer.getRenderTarget();
state.activeCubeFace = renderer.getActiveCubeFace();
state.activeMipmapLevel = renderer.getActiveMipmapLevel();
state.renderObjectFunction = renderer.getRenderObjectFunction();
state.pixelRatio = renderer.getPixelRatio();
state.mrt = renderer.getMRT();
state.clearColor = renderer.getClearColor( state.clearColor || new Color() );
state.clearAlpha = renderer.getClearAlpha();
state.autoClear = renderer.autoClear;
state.scissorTest = renderer.getScissorTest();
return state;
}
export function resetRendererState( renderer, state ) {
state = saveRendererState( renderer, state );
renderer.setMRT( null );
renderer.setRenderObjectFunction( null );
renderer.setClearColor( 0x000000, 1 );
renderer.autoClear = true;
return state;
}
export function restoreRendererState( renderer, state ) {
renderer.toneMapping = state.toneMapping;
renderer.toneMappingExposure = state.toneMappingExposure;
renderer.outputColorSpace = state.outputColorSpace;
renderer.setRenderTarget( state.renderTarget, state.activeCubeFace, state.activeMipmapLevel );
renderer.setRenderObjectFunction( state.renderObjectFunction );
renderer.setPixelRatio( state.pixelRatio );
renderer.setMRT( state.mrt );
renderer.setClearColor( state.clearColor, state.clearAlpha );
renderer.autoClear = state.autoClear;
renderer.setScissorTest( state.scissorTest );
}
// renderer and scene state
export function saveRendererAndSceneState( renderer, scene, state = {} ) {
state = saveRendererState( renderer, state );
state.background = scene.background;
state.backgroundNode = scene.backgroundNode;
state.overrideMaterial = scene.overrideMaterial;
return state;
}
export function resetRendererAndSceneState( renderer, scene, state ) {
state = saveRendererAndSceneState( renderer, scene, state );
scene.background = null;
scene.backgroundNode = null;
scene.overrideMaterial = null;
return state;
}
export function restoreRendererAndSceneState( renderer, scene, state ) {
restoreRendererState( renderer, state );
scene.background = state.background;
scene.backgroundNode = state.backgroundNode;
scene.overrideMaterial = state.overrideMaterial;
}
+20
View File
@@ -0,0 +1,20 @@
let _id = 0;
class ProgrammableStage {
constructor( code, type, transforms = null, attributes = null ) {
this.id = _id ++;
this.code = code;
this.stage = type;
this.transforms = transforms;
this.attributes = attributes;
this.usedTimes = 0;
}
}
export default ProgrammableStage;
+55
View File
@@ -0,0 +1,55 @@
import { BufferGeometry } from '../../core/BufferGeometry.js';
import { Float32BufferAttribute } from '../../core/BufferAttribute.js';
import { Mesh } from '../../objects/Mesh.js';
import { OrthographicCamera } from '../../cameras/OrthographicCamera.js';
// Helper for passes that need to fill the viewport with a single quad.
const _camera = /*@__PURE__*/ new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
// https://github.com/mrdoob/three.js/pull/21358
class QuadGeometry extends BufferGeometry {
constructor( flipY = false ) {
super();
const uv = flipY === false ? [ 0, - 1, 0, 1, 2, 1 ] : [ 0, 2, 0, 0, 2, 0 ];
this.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) );
this.setAttribute( 'uv', new Float32BufferAttribute( uv, 2 ) );
}
}
const _geometry = /*@__PURE__*/ new QuadGeometry();
class QuadMesh extends Mesh {
constructor( material = null ) {
super( _geometry, material );
this.camera = _camera;
this.isQuadMesh = true;
}
renderAsync( renderer ) {
return renderer.renderAsync( this, _camera );
}
render( renderer ) {
renderer.render( this, _camera );
}
}
export default QuadMesh;
+18
View File
@@ -0,0 +1,18 @@
class RenderBundle {
constructor( scene, camera ) {
this.scene = scene;
this.camera = camera;
}
clone() {
return Object.assign( new this.constructor(), this );
}
}
export default RenderBundle;
+38
View File
@@ -0,0 +1,38 @@
import ChainMap from './ChainMap.js';
import RenderBundle from './RenderBundle.js';
class RenderBundles {
constructor() {
this.lists = new ChainMap();
}
get( scene, camera ) {
const lists = this.lists;
const keys = [ scene, camera ];
let list = lists.get( keys );
if ( list === undefined ) {
list = new RenderBundle( scene, camera );
lists.set( keys, list );
}
return list;
}
dispose() {
this.lists = new ChainMap();
}
}
export default RenderBundles;
+66
View File
@@ -0,0 +1,66 @@
import { Vector4 } from '../../math/Vector4.js';
import { hashArray } from '../../nodes/core/NodeUtils.js';
let id = 0;
class RenderContext {
constructor() {
this.id = id ++;
this.color = true;
this.clearColor = true;
this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 };
this.depth = true;
this.clearDepth = true;
this.clearDepthValue = 1;
this.stencil = false;
this.clearStencil = true;
this.clearStencilValue = 1;
this.viewport = false;
this.viewportValue = new Vector4();
this.scissor = false;
this.scissorValue = new Vector4();
this.textures = null;
this.depthTexture = null;
this.activeCubeFace = 0;
this.sampleCount = 1;
this.width = 0;
this.height = 0;
this.isRenderContext = true;
}
getCacheKey() {
return getCacheKey( this );
}
}
export function getCacheKey( renderContext ) {
const { textures, activeCubeFace } = renderContext;
const values = [ activeCubeFace ];
for ( const texture of textures ) {
values.push( texture.id );
}
return hashArray( values );
}
export default RenderContext;
+63
View File
@@ -0,0 +1,63 @@
import ChainMap from './ChainMap.js';
import RenderContext from './RenderContext.js';
class RenderContexts {
constructor() {
this.chainMaps = {};
}
get( scene, camera, renderTarget = null ) {
const chainKey = [ scene, camera ];
let attachmentState;
if ( renderTarget === null ) {
attachmentState = 'default';
} else {
const format = renderTarget.texture.format;
const count = renderTarget.textures.length;
attachmentState = `${ count }:${ format }:${ renderTarget.samples }:${ renderTarget.depthBuffer }:${ renderTarget.stencilBuffer }`;
}
const chainMap = this.getChainMap( attachmentState );
let renderState = chainMap.get( chainKey );
if ( renderState === undefined ) {
renderState = new RenderContext();
chainMap.set( chainKey, renderState );
}
if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
return renderState;
}
getChainMap( attachmentState ) {
return this.chainMaps[ attachmentState ] || ( this.chainMaps[ attachmentState ] = new ChainMap() );
}
dispose() {
this.chainMaps = {};
}
}
export default RenderContexts;
+226
View File
@@ -0,0 +1,226 @@
import { DoubleSide } from '../../constants.js';
function painterSortStable( a, b ) {
if ( a.groupOrder !== b.groupOrder ) {
return a.groupOrder - b.groupOrder;
} else if ( a.renderOrder !== b.renderOrder ) {
return a.renderOrder - b.renderOrder;
} else if ( a.material.id !== b.material.id ) {
return a.material.id - b.material.id;
} else if ( a.z !== b.z ) {
return a.z - b.z;
} else {
return a.id - b.id;
}
}
function reversePainterSortStable( a, b ) {
if ( a.groupOrder !== b.groupOrder ) {
return a.groupOrder - b.groupOrder;
} else if ( a.renderOrder !== b.renderOrder ) {
return a.renderOrder - b.renderOrder;
} else if ( a.z !== b.z ) {
return b.z - a.z;
} else {
return a.id - b.id;
}
}
function needsDoublePass( material ) {
const hasTransmission = material.transmission > 0 || material.transmissionNode;
return hasTransmission && material.side === DoubleSide && material.forceSinglePass === false;
}
class RenderList {
constructor( lighting, scene, camera ) {
this.renderItems = [];
this.renderItemsIndex = 0;
this.opaque = [];
this.transparentDoublePass = [];
this.transparent = [];
this.bundles = [];
this.lightsNode = lighting.getNode( scene, camera );
this.lightsArray = [];
this.scene = scene;
this.camera = camera;
this.occlusionQueryCount = 0;
}
begin() {
this.renderItemsIndex = 0;
this.opaque.length = 0;
this.transparentDoublePass.length = 0;
this.transparent.length = 0;
this.bundles.length = 0;
this.lightsArray.length = 0;
this.occlusionQueryCount = 0;
return this;
}
getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext ) {
let renderItem = this.renderItems[ this.renderItemsIndex ];
if ( renderItem === undefined ) {
renderItem = {
id: object.id,
object: object,
geometry: geometry,
material: material,
groupOrder: groupOrder,
renderOrder: object.renderOrder,
z: z,
group: group,
clippingContext: clippingContext
};
this.renderItems[ this.renderItemsIndex ] = renderItem;
} else {
renderItem.id = object.id;
renderItem.object = object;
renderItem.geometry = geometry;
renderItem.material = material;
renderItem.groupOrder = groupOrder;
renderItem.renderOrder = object.renderOrder;
renderItem.z = z;
renderItem.group = group;
renderItem.clippingContext = clippingContext;
}
this.renderItemsIndex ++;
return renderItem;
}
push( object, geometry, material, groupOrder, z, group, clippingContext ) {
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
if ( material.transparent === true || material.transmission > 0 ) {
if ( needsDoublePass( material ) ) this.transparentDoublePass.push( renderItem );
this.transparent.push( renderItem );
} else {
this.opaque.push( renderItem );
}
}
unshift( object, geometry, material, groupOrder, z, group, clippingContext ) {
const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group, clippingContext );
if ( material.transparent === true || material.transmission > 0 ) {
if ( needsDoublePass( material ) ) this.transparentDoublePass.unshift( renderItem );
this.transparent.unshift( renderItem );
} else {
this.opaque.unshift( renderItem );
}
}
pushBundle( group ) {
this.bundles.push( group );
}
pushLight( light ) {
this.lightsArray.push( light );
}
sort( customOpaqueSort, customTransparentSort ) {
if ( this.opaque.length > 1 ) this.opaque.sort( customOpaqueSort || painterSortStable );
if ( this.transparentDoublePass.length > 1 ) this.transparentDoublePass.sort( customTransparentSort || reversePainterSortStable );
if ( this.transparent.length > 1 ) this.transparent.sort( customTransparentSort || reversePainterSortStable );
}
finish() {
// update lights
this.lightsNode.setLights( this.lightsArray );
// Clear references from inactive renderItems in the list
for ( let i = this.renderItemsIndex, il = this.renderItems.length; i < il; i ++ ) {
const renderItem = this.renderItems[ i ];
if ( renderItem.id === null ) break;
renderItem.id = null;
renderItem.object = null;
renderItem.geometry = null;
renderItem.material = null;
renderItem.groupOrder = null;
renderItem.renderOrder = null;
renderItem.z = null;
renderItem.group = null;
renderItem.clippingContext = null;
}
}
}
export default RenderList;
+40
View File
@@ -0,0 +1,40 @@
import ChainMap from './ChainMap.js';
import RenderList from './RenderList.js';
class RenderLists {
constructor( lighting ) {
this.lighting = lighting;
this.lists = new ChainMap();
}
get( scene, camera ) {
const lists = this.lists;
const keys = [ scene, camera ];
let list = lists.get( keys );
if ( list === undefined ) {
list = new RenderList( this.lighting, scene, camera );
lists.set( keys, list );
}
return list;
}
dispose() {
this.lists = new ChainMap();
}
}
export default RenderLists;
+430
View File
@@ -0,0 +1,430 @@
import { hashString } from '../../nodes/core/NodeUtils.js';
let _id = 0;
function getKeys( obj ) {
const keys = Object.keys( obj );
let proto = Object.getPrototypeOf( obj );
while ( proto ) {
const descriptors = Object.getOwnPropertyDescriptors( proto );
for ( const key in descriptors ) {
if ( descriptors[ key ] !== undefined ) {
const descriptor = descriptors[ key ];
if ( descriptor && typeof descriptor.get === 'function' ) {
keys.push( key );
}
}
}
proto = Object.getPrototypeOf( proto );
}
return keys;
}
export default class RenderObject {
constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext ) {
this._nodes = nodes;
this._geometries = geometries;
this.id = _id ++;
this.renderer = renderer;
this.object = object;
this.material = material;
this.scene = scene;
this.camera = camera;
this.lightsNode = lightsNode;
this.context = renderContext;
this.geometry = object.geometry;
this.version = material.version;
this.drawRange = null;
this.attributes = null;
this.pipeline = null;
this.vertexBuffers = null;
this.drawParams = null;
this.bundle = null;
this.clippingContext = clippingContext;
this.clippingContextCacheKey = clippingContext !== null ? clippingContext.cacheKey : '';
this.initialNodesCacheKey = this.getDynamicCacheKey();
this.initialCacheKey = this.getCacheKey();
this._nodeBuilderState = null;
this._bindings = null;
this._monitor = null;
this.onDispose = null;
this.isRenderObject = true;
this.onMaterialDispose = () => {
this.dispose();
};
this.material.addEventListener( 'dispose', this.onMaterialDispose );
}
updateClipping( parent ) {
this.clippingContext = parent;
}
get clippingNeedsUpdate() {
if ( this.clippingContext === null || this.clippingContext.cacheKey === this.clippingContextCacheKey ) return false;
this.clippingContextCacheKey = this.clippingContext.cacheKey;
return true;
}
get hardwareClippingPlanes() {
return this.material.hardwareClipping === true ? this.clippingContext.unionClippingCount : 0;
}
getNodeBuilderState() {
return this._nodeBuilderState || ( this._nodeBuilderState = this._nodes.getForRender( this ) );
}
getMonitor() {
return this._monitor || ( this._monitor = this.getNodeBuilderState().monitor );
}
getBindings() {
return this._bindings || ( this._bindings = this.getNodeBuilderState().createBindings() );
}
getIndex() {
return this._geometries.getIndex( this );
}
getIndirect() {
return this._geometries.getIndirect( this );
}
getChainArray() {
return [ this.object, this.material, this.context, this.lightsNode ];
}
setGeometry( geometry ) {
this.geometry = geometry;
this.attributes = null;
}
getAttributes() {
if ( this.attributes !== null ) return this.attributes;
const nodeAttributes = this.getNodeBuilderState().nodeAttributes;
const geometry = this.geometry;
const attributes = [];
const vertexBuffers = new Set();
for ( const nodeAttribute of nodeAttributes ) {
const attribute = nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name );
if ( attribute === undefined ) continue;
attributes.push( attribute );
const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
vertexBuffers.add( bufferAttribute );
}
this.attributes = attributes;
this.vertexBuffers = Array.from( vertexBuffers.values() );
return attributes;
}
getVertexBuffers() {
if ( this.vertexBuffers === null ) this.getAttributes();
return this.vertexBuffers;
}
getDrawParameters() {
const { object, material, geometry, group, drawRange } = this;
const drawParams = this.drawParams || ( this.drawParams = {
vertexCount: 0,
firstVertex: 0,
instanceCount: 0,
firstInstance: 0
} );
const index = this.getIndex();
const hasIndex = ( index !== null );
const instanceCount = geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.count > 1 ? object.count : 1 );
if ( instanceCount === 0 ) return null;
drawParams.instanceCount = instanceCount;
if ( object.isBatchedMesh === true ) return drawParams;
let rangeFactor = 1;
if ( material.wireframe === true && ! object.isPoints && ! object.isLineSegments && ! object.isLine && ! object.isLineLoop ) {
rangeFactor = 2;
}
let firstVertex = drawRange.start * rangeFactor;
let lastVertex = ( drawRange.start + drawRange.count ) * rangeFactor;
if ( group !== null ) {
firstVertex = Math.max( firstVertex, group.start * rangeFactor );
lastVertex = Math.min( lastVertex, ( group.start + group.count ) * rangeFactor );
}
const position = geometry.attributes.position;
let itemCount = Infinity;
if ( hasIndex ) {
itemCount = index.count;
} else if ( position !== undefined && position !== null ) {
itemCount = position.count;
}
firstVertex = Math.max( firstVertex, 0 );
lastVertex = Math.min( lastVertex, itemCount );
const count = lastVertex - firstVertex;
if ( count < 0 || count === Infinity ) return null;
drawParams.vertexCount = count;
drawParams.firstVertex = firstVertex;
return drawParams;
}
getGeometryCacheKey() {
const { geometry } = this;
let cacheKey = '';
for ( const name of Object.keys( geometry.attributes ).sort() ) {
const attribute = geometry.attributes[ name ];
cacheKey += name + ',';
if ( attribute.data ) cacheKey += attribute.data.stride + ',';
if ( attribute.offset ) cacheKey += attribute.offset + ',';
if ( attribute.itemSize ) cacheKey += attribute.itemSize + ',';
if ( attribute.normalized ) cacheKey += 'n,';
}
if ( geometry.index ) {
cacheKey += 'index,';
}
return cacheKey;
}
getMaterialCacheKey() {
const { object, material } = this;
let cacheKey = material.customProgramCacheKey();
for ( const property of getKeys( material ) ) {
if ( /^(is[A-Z]|_)|^(visible|version|uuid|name|opacity|userData)$/.test( property ) ) continue;
const value = material[ property ];
let valueKey;
if ( value !== null ) {
// some material values require a formatting
const type = typeof value;
if ( type === 'number' ) {
valueKey = value !== 0 ? '1' : '0'; // Convert to on/off, important for clearcoat, transmission, etc
} else if ( type === 'object' ) {
valueKey = '{';
if ( value.isTexture ) {
valueKey += value.mapping;
}
valueKey += '}';
} else {
valueKey = String( value );
}
} else {
valueKey = String( value );
}
cacheKey += /*property + ':' +*/ valueKey + ',';
}
cacheKey += this.clippingContextCacheKey + ',';
if ( object.geometry ) {
cacheKey += this.getGeometryCacheKey();
}
if ( object.skeleton ) {
cacheKey += object.skeleton.bones.length + ',';
}
if ( object.morphTargetInfluences ) {
cacheKey += object.morphTargetInfluences.length + ',';
}
if ( object.isBatchedMesh ) {
cacheKey += object._matricesTexture.uuid + ',';
if ( object._colorsTexture !== null ) {
cacheKey += object._colorsTexture.uuid + ',';
}
}
if ( object.count > 1 ) {
// TODO: https://github.com/mrdoob/three.js/pull/29066#issuecomment-2269400850
cacheKey += object.uuid + ',';
}
cacheKey += object.receiveShadow + ',';
return hashString( cacheKey );
}
get needsGeometryUpdate() {
return this.geometry.id !== this.object.geometry.id;
}
get needsUpdate() {
return /*this.object.static !== true &&*/ ( this.initialNodesCacheKey !== this.getDynamicCacheKey() || this.clippingNeedsUpdate );
}
getDynamicCacheKey() {
// Environment Nodes Cache Key
let cacheKey = this._nodes.getCacheKey( this.scene, this.lightsNode );
if ( this.object.receiveShadow ) {
cacheKey += 1;
}
return cacheKey;
}
getCacheKey() {
return this.getMaterialCacheKey() + this.getDynamicCacheKey();
}
dispose() {
this.material.removeEventListener( 'dispose', this.onMaterialDispose );
this.onDispose();
}
}
+106
View File
@@ -0,0 +1,106 @@
import ChainMap from './ChainMap.js';
import RenderObject from './RenderObject.js';
const chainArray = [];
class RenderObjects {
constructor( renderer, nodes, geometries, pipelines, bindings, info ) {
this.renderer = renderer;
this.nodes = nodes;
this.geometries = geometries;
this.pipelines = pipelines;
this.bindings = bindings;
this.info = info;
this.chainMaps = {};
}
get( object, material, scene, camera, lightsNode, renderContext, clippingContext, passId ) {
const chainMap = this.getChainMap( passId );
// reuse chainArray
chainArray[ 0 ] = object;
chainArray[ 1 ] = material;
chainArray[ 2 ] = renderContext;
chainArray[ 3 ] = lightsNode;
let renderObject = chainMap.get( chainArray );
if ( renderObject === undefined ) {
renderObject = this.createRenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext, passId );
chainMap.set( chainArray, renderObject );
} else {
renderObject.updateClipping( clippingContext );
if ( renderObject.needsGeometryUpdate ) {
renderObject.setGeometry( object.geometry );
}
if ( renderObject.version !== material.version || renderObject.needsUpdate ) {
if ( renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
renderObject.dispose();
renderObject = this.get( object, material, scene, camera, lightsNode, renderContext, clippingContext, passId );
} else {
renderObject.version = material.version;
}
}
}
return renderObject;
}
getChainMap( passId = 'default' ) {
return this.chainMaps[ passId ] || ( this.chainMaps[ passId ] = new ChainMap() );
}
dispose() {
this.chainMaps = {};
}
createRenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext, passId ) {
const chainMap = this.getChainMap( passId );
const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, clippingContext );
renderObject.onDispose = () => {
this.pipelines.delete( renderObject );
this.bindings.delete( renderObject );
this.nodes.delete( renderObject );
chainMap.delete( renderObject.getChainArray() );
};
return renderObject;
}
}
export default RenderObjects;
+16
View File
@@ -0,0 +1,16 @@
import Pipeline from './Pipeline.js';
class RenderPipeline extends Pipeline {
constructor( cacheKey, vertexProgram, fragmentProgram ) {
super( cacheKey );
this.vertexProgram = vertexProgram;
this.fragmentProgram = fragmentProgram;
}
}
export default RenderPipeline;
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
import Binding from './Binding.js';
let _id = 0;
class SampledTexture extends Binding {
constructor( name, texture ) {
super( name );
this.id = _id ++;
this.texture = texture;
this.version = texture ? texture.version : 0;
this.store = false;
this.generation = null;
this.isSampledTexture = true;
}
needsBindingsUpdate( generation ) {
const { texture } = this;
if ( generation !== this.generation ) {
this.generation = generation;
return true;
}
return texture.isVideoTexture;
}
update() {
const { texture, version } = this;
if ( version !== texture.version ) {
this.version = texture.version;
return true;
}
return false;
}
}
class SampledArrayTexture extends SampledTexture {
constructor( name, texture ) {
super( name, texture );
this.isSampledArrayTexture = true;
}
}
class Sampled3DTexture extends SampledTexture {
constructor( name, texture ) {
super( name, texture );
this.isSampled3DTexture = true;
}
}
class SampledCubeTexture extends SampledTexture {
constructor( name, texture ) {
super( name, texture );
this.isSampledCubeTexture = true;
}
}
export { SampledTexture, SampledArrayTexture, Sampled3DTexture, SampledCubeTexture };
+18
View File
@@ -0,0 +1,18 @@
import Binding from './Binding.js';
class Sampler extends Binding {
constructor( name, texture ) {
super( name );
this.texture = texture;
this.version = texture ? texture.version : 0;
this.isSampler = true;
}
}
export default Sampler;
+17
View File
@@ -0,0 +1,17 @@
import Buffer from './Buffer.js';
class StorageBuffer extends Buffer {
constructor( name, attribute ) {
super( name, attribute ? attribute.array : null );
this.attribute = attribute;
this.isStorageBuffer = true;
}
}
export default StorageBuffer;
+17
View File
@@ -0,0 +1,17 @@
import { BufferAttribute } from '../../core/BufferAttribute.js';
class StorageBufferAttribute extends BufferAttribute {
constructor( array, itemSize, typeClass = Float32Array ) {
if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize );
super( array, itemSize );
this.isStorageBufferAttribute = true;
}
}
export default StorageBufferAttribute;
@@ -0,0 +1,17 @@
import { InstancedBufferAttribute } from '../../core/InstancedBufferAttribute.js';
class StorageInstancedBufferAttribute extends InstancedBufferAttribute {
constructor( array, itemSize, typeClass = Float32Array ) {
if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize );
super( array, itemSize );
this.isStorageInstancedBufferAttribute = true;
}
}
export default StorageInstancedBufferAttribute;
+21
View File
@@ -0,0 +1,21 @@
import { Texture } from '../../textures/Texture.js';
import { LinearFilter } from '../../constants.js';
class StorageTexture extends Texture {
constructor( width = 1, height = 1 ) {
super();
this.image = { width, height };
this.magFilter = LinearFilter;
this.minFilter = LinearFilter;
this.isStorageTexture = true;
}
}
export default StorageTexture;
+368
View File
@@ -0,0 +1,368 @@
import DataMap from './DataMap.js';
import { Vector3 } from '../../math/Vector3.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { DepthStencilFormat, DepthFormat, UnsignedIntType, UnsignedInt248Type, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeReflectionMapping, CubeRefractionMapping, UnsignedByteType } from '../../constants.js';
const _size = /*@__PURE__*/ new Vector3();
class Textures extends DataMap {
constructor( renderer, backend, info ) {
super();
this.renderer = renderer;
this.backend = backend;
this.info = info;
}
updateRenderTarget( renderTarget, activeMipmapLevel = 0 ) {
const renderTargetData = this.get( renderTarget );
const sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
const depthTextureMips = renderTargetData.depthTextureMips || ( renderTargetData.depthTextureMips = {} );
const textures = renderTarget.textures;
const size = this.getSize( textures[ 0 ] );
const mipWidth = size.width >> activeMipmapLevel;
const mipHeight = size.height >> activeMipmapLevel;
let depthTexture = renderTarget.depthTexture || depthTextureMips[ activeMipmapLevel ];
const useDepthTexture = renderTarget.depthBuffer === true || renderTarget.stencilBuffer === true;
let textureNeedsUpdate = false;
if ( depthTexture === undefined && useDepthTexture ) {
depthTexture = new DepthTexture();
depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat;
depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType; // FloatType
depthTexture.image.width = mipWidth;
depthTexture.image.height = mipHeight;
depthTextureMips[ activeMipmapLevel ] = depthTexture;
}
if ( renderTargetData.width !== size.width || size.height !== renderTargetData.height ) {
textureNeedsUpdate = true;
if ( depthTexture ) {
depthTexture.needsUpdate = true;
depthTexture.image.width = mipWidth;
depthTexture.image.height = mipHeight;
}
}
renderTargetData.width = size.width;
renderTargetData.height = size.height;
renderTargetData.textures = textures;
renderTargetData.depthTexture = depthTexture || null;
renderTargetData.depth = renderTarget.depthBuffer;
renderTargetData.stencil = renderTarget.stencilBuffer;
renderTargetData.renderTarget = renderTarget;
if ( renderTargetData.sampleCount !== sampleCount ) {
textureNeedsUpdate = true;
if ( depthTexture ) {
depthTexture.needsUpdate = true;
}
renderTargetData.sampleCount = sampleCount;
}
//
const options = { sampleCount };
for ( let i = 0; i < textures.length; i ++ ) {
const texture = textures[ i ];
if ( textureNeedsUpdate ) texture.needsUpdate = true;
this.updateTexture( texture, options );
}
if ( depthTexture ) {
this.updateTexture( depthTexture, options );
}
// dispose handler
if ( renderTargetData.initialized !== true ) {
renderTargetData.initialized = true;
// dispose
const onDispose = () => {
renderTarget.removeEventListener( 'dispose', onDispose );
for ( let i = 0; i < textures.length; i ++ ) {
this._destroyTexture( textures[ i ] );
}
if ( depthTexture ) {
this._destroyTexture( depthTexture );
}
this.delete( renderTarget );
};
renderTarget.addEventListener( 'dispose', onDispose );
}
}
updateTexture( texture, options = {} ) {
const textureData = this.get( texture );
if ( textureData.initialized === true && textureData.version === texture.version ) return;
const isRenderTarget = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture;
const backend = this.backend;
if ( isRenderTarget && textureData.initialized === true ) {
// it's an update
backend.destroySampler( texture );
backend.destroyTexture( texture );
}
//
if ( texture.isFramebufferTexture ) {
const renderTarget = this.renderer.getRenderTarget();
if ( renderTarget ) {
texture.type = renderTarget.texture.type;
} else {
texture.type = UnsignedByteType;
}
}
//
const { width, height, depth } = this.getSize( texture );
options.width = width;
options.height = height;
options.depth = depth;
options.needsMipmaps = this.needsMipmaps( texture );
options.levels = options.needsMipmaps ? this.getMipLevels( texture, width, height ) : 1;
//
if ( isRenderTarget || texture.isStorageTexture === true ) {
backend.createSampler( texture );
backend.createTexture( texture, options );
textureData.generation = texture.version;
} else {
const needsCreate = textureData.initialized !== true;
if ( needsCreate ) backend.createSampler( texture );
if ( texture.version > 0 ) {
const image = texture.image;
if ( image === undefined ) {
console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' );
} else if ( image.complete === false ) {
console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' );
} else {
if ( texture.images ) {
const images = [];
for ( const image of texture.images ) {
images.push( image );
}
options.images = images;
} else {
options.image = image;
}
if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) {
backend.createTexture( texture, options );
textureData.isDefaultTexture = false;
textureData.generation = texture.version;
}
if ( texture.source.dataReady === true ) backend.updateTexture( texture, options );
if ( options.needsMipmaps && texture.mipmaps.length === 0 ) backend.generateMipmaps( texture );
}
} else {
// async update
backend.createDefaultTexture( texture );
textureData.isDefaultTexture = true;
textureData.generation = texture.version;
}
}
// dispose handler
if ( textureData.initialized !== true ) {
textureData.initialized = true;
textureData.generation = texture.version;
//
this.info.memory.textures ++;
// dispose
const onDispose = () => {
texture.removeEventListener( 'dispose', onDispose );
this._destroyTexture( texture );
this.info.memory.textures --;
};
texture.addEventListener( 'dispose', onDispose );
}
//
textureData.version = texture.version;
}
getSize( texture, target = _size ) {
let image = texture.images ? texture.images[ 0 ] : texture.image;
if ( image ) {
if ( image.image !== undefined ) image = image.image;
target.width = image.width || 1;
target.height = image.height || 1;
target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 );
} else {
target.width = target.height = target.depth = 1;
}
return target;
}
getMipLevels( texture, width, height ) {
let mipLevelCount;
if ( texture.isCompressedTexture ) {
if ( texture.mipmaps ) {
mipLevelCount = texture.mipmaps.length;
} else {
mipLevelCount = 1;
}
} else {
mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
}
return mipLevelCount;
}
needsMipmaps( texture ) {
return this.isEnvironmentTexture( texture ) || texture.isCompressedTexture === true || texture.generateMipmaps;
}
isEnvironmentTexture( texture ) {
const mapping = texture.mapping;
return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );
}
_destroyTexture( texture ) {
this.backend.destroySampler( texture );
this.backend.destroyTexture( texture );
this.delete( texture );
}
}
export default Textures;
+145
View File
@@ -0,0 +1,145 @@
import { Color } from '../../math/Color.js';
import { Matrix3 } from '../../math/Matrix3.js';
import { Matrix4 } from '../../math/Matrix4.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { Vector4 } from '../../math/Vector4.js';
class Uniform {
constructor( name, value ) {
this.name = name;
this.value = value;
this.boundary = 0; // used to build the uniform buffer according to the STD140 layout
this.itemSize = 0;
this.offset = 0; // this property is set by WebGPUUniformsGroup and marks the start position in the uniform buffer
}
setValue( value ) {
this.value = value;
}
getValue() {
return this.value;
}
}
class NumberUniform extends Uniform {
constructor( name, value = 0 ) {
super( name, value );
this.isNumberUniform = true;
this.boundary = 4;
this.itemSize = 1;
}
}
class Vector2Uniform extends Uniform {
constructor( name, value = new Vector2() ) {
super( name, value );
this.isVector2Uniform = true;
this.boundary = 8;
this.itemSize = 2;
}
}
class Vector3Uniform extends Uniform {
constructor( name, value = new Vector3() ) {
super( name, value );
this.isVector3Uniform = true;
this.boundary = 16;
this.itemSize = 3;
}
}
class Vector4Uniform extends Uniform {
constructor( name, value = new Vector4() ) {
super( name, value );
this.isVector4Uniform = true;
this.boundary = 16;
this.itemSize = 4;
}
}
class ColorUniform extends Uniform {
constructor( name, value = new Color() ) {
super( name, value );
this.isColorUniform = true;
this.boundary = 16;
this.itemSize = 3;
}
}
class Matrix3Uniform extends Uniform {
constructor( name, value = new Matrix3() ) {
super( name, value );
this.isMatrix3Uniform = true;
this.boundary = 48;
this.itemSize = 12;
}
}
class Matrix4Uniform extends Uniform {
constructor( name, value = new Matrix4() ) {
super( name, value );
this.isMatrix4Uniform = true;
this.boundary = 64;
this.itemSize = 16;
}
}
export {
NumberUniform,
Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform,
Matrix3Uniform, Matrix4Uniform
};
+15
View File
@@ -0,0 +1,15 @@
import Buffer from './Buffer.js';
class UniformBuffer extends Buffer {
constructor( name, buffer = null ) {
super( name, buffer );
this.isUniformBuffer = true;
}
}
export default UniformBuffer;
+351
View File
@@ -0,0 +1,351 @@
import UniformBuffer from './UniformBuffer.js';
import { GPU_CHUNK_BYTES } from './Constants.js';
class UniformsGroup extends UniformBuffer {
constructor( name ) {
super( name );
this.isUniformsGroup = true;
this._values = null;
// the order of uniforms in this array must match the order of uniforms in the shader
this.uniforms = [];
}
addUniform( uniform ) {
this.uniforms.push( uniform );
return this;
}
removeUniform( uniform ) {
const index = this.uniforms.indexOf( uniform );
if ( index !== - 1 ) {
this.uniforms.splice( index, 1 );
}
return this;
}
get values() {
if ( this._values === null ) {
this._values = Array.from( this.buffer );
}
return this._values;
}
get buffer() {
let buffer = this._buffer;
if ( buffer === null ) {
const byteLength = this.byteLength;
buffer = new Float32Array( new ArrayBuffer( byteLength ) );
this._buffer = buffer;
}
return buffer;
}
get byteLength() {
let offset = 0; // global buffer offset in bytes
for ( let i = 0, l = this.uniforms.length; i < l; i ++ ) {
const uniform = this.uniforms[ i ];
const { boundary, itemSize } = uniform;
// offset within a single chunk in bytes
const chunkOffset = offset % GPU_CHUNK_BYTES;
const remainingSizeInChunk = GPU_CHUNK_BYTES - chunkOffset;
// conformance tests
if ( chunkOffset !== 0 && ( remainingSizeInChunk - boundary ) < 0 ) {
// check for chunk overflow
offset += ( GPU_CHUNK_BYTES - chunkOffset );
} else if ( chunkOffset % boundary !== 0 ) {
// check for correct alignment
offset += ( chunkOffset % boundary );
}
uniform.offset = ( offset / this.bytesPerElement );
offset += ( itemSize * this.bytesPerElement );
}
return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
}
update() {
let updated = false;
for ( const uniform of this.uniforms ) {
if ( this.updateByType( uniform ) === true ) {
updated = true;
}
}
return updated;
}
updateByType( uniform ) {
if ( uniform.isNumberUniform ) return this.updateNumber( uniform );
if ( uniform.isVector2Uniform ) return this.updateVector2( uniform );
if ( uniform.isVector3Uniform ) return this.updateVector3( uniform );
if ( uniform.isVector4Uniform ) return this.updateVector4( uniform );
if ( uniform.isColorUniform ) return this.updateColor( uniform );
if ( uniform.isMatrix3Uniform ) return this.updateMatrix3( uniform );
if ( uniform.isMatrix4Uniform ) return this.updateMatrix4( uniform );
console.error( 'THREE.WebGPUUniformsGroup: Unsupported uniform type.', uniform );
}
updateNumber( uniform ) {
let updated = false;
const a = this.values;
const v = uniform.getValue();
const offset = uniform.offset;
const type = uniform.getType();
if ( a[ offset ] !== v ) {
const b = this._getBufferForType( type );
b[ offset ] = a[ offset ] = v;
updated = true;
}
return updated;
}
updateVector2( uniform ) {
let updated = false;
const a = this.values;
const v = uniform.getValue();
const offset = uniform.offset;
const type = uniform.getType();
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y ) {
const b = this._getBufferForType( type );
b[ offset + 0 ] = a[ offset + 0 ] = v.x;
b[ offset + 1 ] = a[ offset + 1 ] = v.y;
updated = true;
}
return updated;
}
updateVector3( uniform ) {
let updated = false;
const a = this.values;
const v = uniform.getValue();
const offset = uniform.offset;
const type = uniform.getType();
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z ) {
const b = this._getBufferForType( type );
b[ offset + 0 ] = a[ offset + 0 ] = v.x;
b[ offset + 1 ] = a[ offset + 1 ] = v.y;
b[ offset + 2 ] = a[ offset + 2 ] = v.z;
updated = true;
}
return updated;
}
updateVector4( uniform ) {
let updated = false;
const a = this.values;
const v = uniform.getValue();
const offset = uniform.offset;
const type = uniform.getType();
if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z || a[ offset + 4 ] !== v.w ) {
const b = this._getBufferForType( type );
b[ offset + 0 ] = a[ offset + 0 ] = v.x;
b[ offset + 1 ] = a[ offset + 1 ] = v.y;
b[ offset + 2 ] = a[ offset + 2 ] = v.z;
b[ offset + 3 ] = a[ offset + 3 ] = v.w;
updated = true;
}
return updated;
}
updateColor( uniform ) {
let updated = false;
const a = this.values;
const c = uniform.getValue();
const offset = uniform.offset;
if ( a[ offset + 0 ] !== c.r || a[ offset + 1 ] !== c.g || a[ offset + 2 ] !== c.b ) {
const b = this.buffer;
b[ offset + 0 ] = a[ offset + 0 ] = c.r;
b[ offset + 1 ] = a[ offset + 1 ] = c.g;
b[ offset + 2 ] = a[ offset + 2 ] = c.b;
updated = true;
}
return updated;
}
updateMatrix3( uniform ) {
let updated = false;
const a = this.values;
const e = uniform.getValue().elements;
const offset = uniform.offset;
if ( a[ offset + 0 ] !== e[ 0 ] || a[ offset + 1 ] !== e[ 1 ] || a[ offset + 2 ] !== e[ 2 ] ||
a[ offset + 4 ] !== e[ 3 ] || a[ offset + 5 ] !== e[ 4 ] || a[ offset + 6 ] !== e[ 5 ] ||
a[ offset + 8 ] !== e[ 6 ] || a[ offset + 9 ] !== e[ 7 ] || a[ offset + 10 ] !== e[ 8 ] ) {
const b = this.buffer;
b[ offset + 0 ] = a[ offset + 0 ] = e[ 0 ];
b[ offset + 1 ] = a[ offset + 1 ] = e[ 1 ];
b[ offset + 2 ] = a[ offset + 2 ] = e[ 2 ];
b[ offset + 4 ] = a[ offset + 4 ] = e[ 3 ];
b[ offset + 5 ] = a[ offset + 5 ] = e[ 4 ];
b[ offset + 6 ] = a[ offset + 6 ] = e[ 5 ];
b[ offset + 8 ] = a[ offset + 8 ] = e[ 6 ];
b[ offset + 9 ] = a[ offset + 9 ] = e[ 7 ];
b[ offset + 10 ] = a[ offset + 10 ] = e[ 8 ];
updated = true;
}
return updated;
}
updateMatrix4( uniform ) {
let updated = false;
const a = this.values;
const e = uniform.getValue().elements;
const offset = uniform.offset;
if ( arraysEqual( a, e, offset ) === false ) {
const b = this.buffer;
b.set( e, offset );
setArray( a, e, offset );
updated = true;
}
return updated;
}
_getBufferForType( type ) {
if ( type === 'int' || type === 'ivec2' || type === 'ivec3' || type === 'ivec4' ) return new Int32Array( this.buffer.buffer );
if ( type === 'uint' || type === 'uvec2' || type === 'uvec3' || type === 'uvec4' ) return new Uint32Array( this.buffer.buffer );
return this.buffer;
}
}
function setArray( a, b, offset ) {
for ( let i = 0, l = b.length; i < l; i ++ ) {
a[ offset + i ] = b[ i ];
}
}
function arraysEqual( a, b, offset ) {
for ( let i = 0, l = b.length; i < l; i ++ ) {
if ( a[ offset + i ] !== b[ i ] ) return false;
}
return true;
}
export default UniformsGroup;
+847
View File
@@ -0,0 +1,847 @@
import NodeMaterial from '../../../materials/nodes/NodeMaterial.js';
import { getDirection, blur } from '../../../nodes/pmrem/PMREMUtils.js';
import { equirectUV } from '../../../nodes/utils/EquirectUVNode.js';
import { uniform } from '../../../nodes/core/UniformNode.js';
import { uniformArray } from '../../../nodes/accessors/UniformArrayNode.js';
import { texture } from '../../../nodes/accessors/TextureNode.js';
import { cubeTexture } from '../../../nodes/accessors/CubeTextureNode.js';
import { float, vec3 } from '../../../nodes/tsl/TSLBase.js';
import { uv } from '../../../nodes/accessors/UV.js';
import { attribute } from '../../../nodes/core/AttributeNode.js';
import { OrthographicCamera } from '../../../cameras/OrthographicCamera.js';
import { Color } from '../../../math/Color.js';
import { Vector3 } from '../../../math/Vector3.js';
import { BufferGeometry } from '../../../core/BufferGeometry.js';
import { BufferAttribute } from '../../../core/BufferAttribute.js';
import { RenderTarget } from '../../../core/RenderTarget.js';
import { Mesh } from '../../../objects/Mesh.js';
import { PerspectiveCamera } from '../../../cameras/PerspectiveCamera.js';
import { MeshBasicMaterial } from '../../../materials/MeshBasicMaterial.js';
import { BoxGeometry } from '../../../geometries/BoxGeometry.js';
import {
CubeReflectionMapping,
CubeRefractionMapping,
CubeUVReflectionMapping,
LinearFilter,
NoBlending,
RGBAFormat,
HalfFloatType,
BackSide,
LinearSRGBColorSpace
} from '../../../constants.js';
const LOD_MIN = 4;
// The standard deviations (radians) associated with the extra mips. These are
// chosen to approximate a Trowbridge-Reitz distribution function times the
// geometric shadowing function. These sigma values squared must match the
// variance #defines in cube_uv_reflection_fragment.glsl.js.
const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
// The maximum length of the blur for loop. Smaller sigmas will use fewer
// samples and exit early, but not recompile the shader.
const MAX_SAMPLES = 20;
const _flatCamera = /*@__PURE__*/ new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
const _cubeCamera = /*@__PURE__*/ new PerspectiveCamera( 90, 1 );
const _clearColor = /*@__PURE__*/ new Color();
let _oldTarget = null;
let _oldActiveCubeFace = 0;
let _oldActiveMipmapLevel = 0;
// Golden Ratio
const PHI = ( 1 + Math.sqrt( 5 ) ) / 2;
const INV_PHI = 1 / PHI;
// Vertices of a dodecahedron (except the opposites, which represent the
// same axis), used as axis directions evenly spread on a sphere.
const _axisDirections = [
/*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ),
/*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ),
/*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ),
/*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ),
/*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ),
/*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ),
/*@__PURE__*/ new Vector3( - 1, 1, - 1 ),
/*@__PURE__*/ new Vector3( 1, 1, - 1 ),
/*@__PURE__*/ new Vector3( - 1, 1, 1 ),
/*@__PURE__*/ new Vector3( 1, 1, 1 )
];
//
// WebGPU Face indices
const _faceLib = [
3, 1, 5,
0, 4, 2
];
const direction = getDirection( uv(), attribute( 'faceIndex' ) ).normalize();
const outputDirection = vec3( direction.x, direction.y.negate(), direction.z );
/**
* This class generates a Prefiltered, Mipmapped Radiance Environment Map
* (PMREM) from a cubeMap environment texture. This allows different levels of
* blur to be quickly accessed based on material roughness. It is packed into a
* special CubeUV format that allows us to perform custom interpolation so that
* we can support nonlinear formats such as RGBE. Unlike a traditional mipmap
* chain, it only goes down to the LOD_MIN level (above), and then creates extra
* even more filtered 'mips' at the same LOD_MIN resolution, associated with
* higher roughness levels. In this way we maintain resolution to smoothly
* interpolate diffuse lighting while limiting sampling computation.
*
* Paper: Fast, Accurate Image-Based Lighting
* https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view
*/
class PMREMGenerator {
constructor( renderer ) {
this._renderer = renderer;
this._pingPongRenderTarget = null;
this._lodMax = 0;
this._cubeSize = 0;
this._lodPlanes = [];
this._sizeLods = [];
this._sigmas = [];
this._lodMeshes = [];
this._blurMaterial = null;
this._cubemapMaterial = null;
this._equirectMaterial = null;
this._backgroundBox = null;
}
get _hasInitialized() {
return this._renderer.hasInitialized();
}
/**
* Generates a PMREM from a supplied Scene, which can be faster than using an
* image if networking bandwidth is low. Optional sigma specifies a blur radius
* in radians to be applied to the scene before PMREM generation. Optional near
* and far planes ensure the scene is rendered in its entirety (the cubeCamera
* is placed at the origin).
*/
fromScene( scene, sigma = 0, near = 0.1, far = 100, renderTarget = null ) {
this._setSize( 256 );
if ( this._hasInitialized === false ) {
console.warn( 'THREE.PMREMGenerator: .fromScene() called before the backend is initialized. Try using .fromSceneAsync() instead.' );
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
this.fromSceneAsync( scene, sigma, near, far, cubeUVRenderTarget );
return cubeUVRenderTarget;
}
_oldTarget = this._renderer.getRenderTarget();
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
cubeUVRenderTarget.depthBuffer = true;
this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
if ( sigma > 0 ) {
this._blur( cubeUVRenderTarget, 0, 0, sigma );
}
this._applyPMREM( cubeUVRenderTarget );
this._cleanup( cubeUVRenderTarget );
return cubeUVRenderTarget;
}
async fromSceneAsync( scene, sigma = 0, near = 0.1, far = 100, renderTarget = null ) {
if ( this._hasInitialized === false ) await this._renderer.init();
return this.fromScene( scene, sigma, near, far, renderTarget );
}
/**
* Generates a PMREM from an equirectangular texture, which can be either LDR
* or HDR. The ideal input image size is 1k (1024 x 512),
* as this matches best with the 256 x 256 cubemap output.
*/
fromEquirectangular( equirectangular, renderTarget = null ) {
if ( this._hasInitialized === false ) {
console.warn( 'THREE.PMREMGenerator: .fromEquirectangular() called before the backend is initialized. Try using .fromEquirectangularAsync() instead.' );
this._setSizeFromTexture( equirectangular );
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
this.fromEquirectangularAsync( equirectangular, cubeUVRenderTarget );
return cubeUVRenderTarget;
}
return this._fromTexture( equirectangular, renderTarget );
}
async fromEquirectangularAsync( equirectangular, renderTarget = null ) {
if ( this._hasInitialized === false ) await this._renderer.init();
return this._fromTexture( equirectangular, renderTarget );
}
/**
* Generates a PMREM from an cubemap texture, which can be either LDR
* or HDR. The ideal input cube size is 256 x 256,
* as this matches best with the 256 x 256 cubemap output.
*/
fromCubemap( cubemap, renderTarget = null ) {
if ( this._hasInitialized === false ) {
console.warn( 'THREE.PMREMGenerator: .fromCubemap() called before the backend is initialized. Try using .fromCubemapAsync() instead.' );
this._setSizeFromTexture( cubemap );
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
this.fromCubemapAsync( cubemap, renderTarget );
return cubeUVRenderTarget;
}
return this._fromTexture( cubemap, renderTarget );
}
async fromCubemapAsync( cubemap, renderTarget = null ) {
if ( this._hasInitialized === false ) await this._renderer.init();
return this._fromTexture( cubemap, renderTarget );
}
/**
* Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during
* your texture's network fetch for increased concurrency.
*/
async compileCubemapShader() {
if ( this._cubemapMaterial === null ) {
this._cubemapMaterial = _getCubemapMaterial();
await this._compileMaterial( this._cubemapMaterial );
}
}
/**
* Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during
* your texture's network fetch for increased concurrency.
*/
async compileEquirectangularShader() {
if ( this._equirectMaterial === null ) {
this._equirectMaterial = _getEquirectMaterial();
await this._compileMaterial( this._equirectMaterial );
}
}
/**
* Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class,
* so you should not need more than one PMREMGenerator object. If you do, calling dispose() on
* one of them will cause any others to also become unusable.
*/
dispose() {
this._dispose();
if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose();
if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose();
if ( this._backgroundBox !== null ) {
this._backgroundBox.geometry.dispose();
this._backgroundBox.material.dispose();
}
}
// private interface
_setSizeFromTexture( texture ) {
if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {
this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) );
} else { // Equirectangular
this._setSize( texture.image.width / 4 );
}
}
_setSize( cubeSize ) {
this._lodMax = Math.floor( Math.log2( cubeSize ) );
this._cubeSize = Math.pow( 2, this._lodMax );
}
_dispose() {
if ( this._blurMaterial !== null ) this._blurMaterial.dispose();
if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
for ( let i = 0; i < this._lodPlanes.length; i ++ ) {
this._lodPlanes[ i ].dispose();
}
}
_cleanup( outputTarget ) {
this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel );
outputTarget.scissorTest = false;
_setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height );
}
_fromTexture( texture, renderTarget ) {
this._setSizeFromTexture( texture );
_oldTarget = this._renderer.getRenderTarget();
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
const cubeUVRenderTarget = renderTarget || this._allocateTargets();
this._textureToCubeUV( texture, cubeUVRenderTarget );
this._applyPMREM( cubeUVRenderTarget );
this._cleanup( cubeUVRenderTarget );
return cubeUVRenderTarget;
}
_allocateTargets() {
const width = 3 * Math.max( this._cubeSize, 16 * 7 );
const height = 4 * this._cubeSize;
const params = {
magFilter: LinearFilter,
minFilter: LinearFilter,
generateMipmaps: false,
type: HalfFloatType,
format: RGBAFormat,
colorSpace: LinearSRGBColorSpace,
//depthBuffer: false
};
const cubeUVRenderTarget = _createRenderTarget( width, height, params );
if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) {
if ( this._pingPongRenderTarget !== null ) {
this._dispose();
}
this._pingPongRenderTarget = _createRenderTarget( width, height, params );
const { _lodMax } = this;
( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas, lodMeshes: this._lodMeshes } = _createPlanes( _lodMax ) );
this._blurMaterial = _getBlurShader( _lodMax, width, height );
}
return cubeUVRenderTarget;
}
async _compileMaterial( material ) {
const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material );
await this._renderer.compile( tmpMesh, _flatCamera );
}
_sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) {
const cubeCamera = _cubeCamera;
cubeCamera.near = near;
cubeCamera.far = far;
// px, py, pz, nx, ny, nz
const upSign = [ - 1, 1, - 1, - 1, - 1, - 1 ];
const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ];
const renderer = this._renderer;
const originalAutoClear = renderer.autoClear;
renderer.getClearColor( _clearColor );
renderer.autoClear = false;
let backgroundBox = this._backgroundBox;
if ( backgroundBox === null ) {
const backgroundMaterial = new MeshBasicMaterial( {
name: 'PMREM.Background',
side: BackSide,
depthWrite: false,
depthTest: false
} );
backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial );
}
let useSolidColor = false;
const background = scene.background;
if ( background ) {
if ( background.isColor ) {
backgroundBox.material.color.copy( background );
scene.background = null;
useSolidColor = true;
}
} else {
backgroundBox.material.color.copy( _clearColor );
useSolidColor = true;
}
renderer.setRenderTarget( cubeUVRenderTarget );
renderer.clear();
if ( useSolidColor ) {
renderer.render( backgroundBox, cubeCamera );
}
for ( let i = 0; i < 6; i ++ ) {
const col = i % 3;
if ( col === 0 ) {
cubeCamera.up.set( 0, upSign[ i ], 0 );
cubeCamera.lookAt( forwardSign[ i ], 0, 0 );
} else if ( col === 1 ) {
cubeCamera.up.set( 0, 0, upSign[ i ] );
cubeCamera.lookAt( 0, forwardSign[ i ], 0 );
} else {
cubeCamera.up.set( 0, upSign[ i ], 0 );
cubeCamera.lookAt( 0, 0, forwardSign[ i ] );
}
const size = this._cubeSize;
_setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );
renderer.render( scene, cubeCamera );
}
renderer.autoClear = originalAutoClear;
scene.background = background;
}
_textureToCubeUV( texture, cubeUVRenderTarget ) {
const renderer = this._renderer;
const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping );
if ( isCubeTexture ) {
if ( this._cubemapMaterial === null ) {
this._cubemapMaterial = _getCubemapMaterial( texture );
}
} else {
if ( this._equirectMaterial === null ) {
this._equirectMaterial = _getEquirectMaterial( texture );
}
}
const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial;
material.fragmentNode.value = texture;
const mesh = this._lodMeshes[ 0 ];
mesh.material = material;
const size = this._cubeSize;
_setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );
renderer.setRenderTarget( cubeUVRenderTarget );
renderer.render( mesh, _flatCamera );
}
_applyPMREM( cubeUVRenderTarget ) {
const renderer = this._renderer;
const autoClear = renderer.autoClear;
renderer.autoClear = false;
const n = this._lodPlanes.length;
for ( let i = 1; i < n; i ++ ) {
const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );
const poleAxis = _axisDirections[ ( n - i - 1 ) % _axisDirections.length ];
this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
}
renderer.autoClear = autoClear;
}
/**
* This is a two-pass Gaussian blur for a cubemap. Normally this is done
* vertically and horizontally, but this breaks down on a cube. Here we apply
* the blur latitudinally (around the poles), and then longitudinally (towards
* the poles) to approximate the orthogonally-separable blur. It is least
* accurate at the poles, but still does a decent job.
*/
_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
const pingPongRenderTarget = this._pingPongRenderTarget;
this._halfBlur(
cubeUVRenderTarget,
pingPongRenderTarget,
lodIn,
lodOut,
sigma,
'latitudinal',
poleAxis );
this._halfBlur(
pingPongRenderTarget,
cubeUVRenderTarget,
lodOut,
lodOut,
sigma,
'longitudinal',
poleAxis );
}
_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
const renderer = this._renderer;
const blurMaterial = this._blurMaterial;
if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
console.error( 'blur direction must be either latitudinal or longitudinal!' );
}
// Number of standard deviations at which to cut off the discrete approximation.
const STANDARD_DEVIATIONS = 3;
const blurMesh = this._lodMeshes[ lodOut ];
blurMesh.material = blurMaterial;
const blurUniforms = blurMaterial.uniforms;
const pixels = this._sizeLods[ lodIn ] - 1;
const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
const sigmaPixels = sigmaRadians / radiansPerPixel;
const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
if ( samples > MAX_SAMPLES ) {
console.warn( `sigmaRadians, ${
sigmaRadians}, is too large and will clip, as it requested ${
samples} samples when the maximum is set to ${MAX_SAMPLES}` );
}
const weights = [];
let sum = 0;
for ( let i = 0; i < MAX_SAMPLES; ++ i ) {
const x = i / sigmaPixels;
const weight = Math.exp( - x * x / 2 );
weights.push( weight );
if ( i === 0 ) {
sum += weight;
} else if ( i < samples ) {
sum += 2 * weight;
}
}
for ( let i = 0; i < weights.length; i ++ ) {
weights[ i ] = weights[ i ] / sum;
}
targetIn.texture.frame = ( targetIn.texture.frame || 0 ) + 1;
blurUniforms.envMap.value = targetIn.texture;
blurUniforms.samples.value = samples;
blurUniforms.weights.array = weights;
blurUniforms.latitudinal.value = direction === 'latitudinal' ? 1 : 0;
if ( poleAxis ) {
blurUniforms.poleAxis.value = poleAxis;
}
const { _lodMax } = this;
blurUniforms.dTheta.value = radiansPerPixel;
blurUniforms.mipInt.value = _lodMax - lodIn;
const outputSize = this._sizeLods[ lodOut ];
const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );
const y = 4 * ( this._cubeSize - outputSize );
_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
renderer.setRenderTarget( targetOut );
renderer.render( blurMesh, _flatCamera );
}
}
function _createPlanes( lodMax ) {
const lodPlanes = [];
const sizeLods = [];
const sigmas = [];
const lodMeshes = [];
let lod = lodMax;
const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
for ( let i = 0; i < totalLods; i ++ ) {
const sizeLod = Math.pow( 2, lod );
sizeLods.push( sizeLod );
let sigma = 1.0 / sizeLod;
if ( i > lodMax - LOD_MIN ) {
sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];
} else if ( i === 0 ) {
sigma = 0;
}
sigmas.push( sigma );
const texelSize = 1.0 / ( sizeLod - 2 );
const min = - texelSize;
const max = 1 + texelSize;
const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];
const cubeFaces = 6;
const vertices = 6;
const positionSize = 3;
const uvSize = 2;
const faceIndexSize = 1;
const position = new Float32Array( positionSize * vertices * cubeFaces );
const uv = new Float32Array( uvSize * vertices * cubeFaces );
const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces );
for ( let face = 0; face < cubeFaces; face ++ ) {
const x = ( face % 3 ) * 2 / 3 - 1;
const y = face > 2 ? 0 : - 1;
const coordinates = [
x, y, 0,
x + 2 / 3, y, 0,
x + 2 / 3, y + 1, 0,
x, y, 0,
x + 2 / 3, y + 1, 0,
x, y + 1, 0
];
const faceIdx = _faceLib[ face ];
position.set( coordinates, positionSize * vertices * faceIdx );
uv.set( uv1, uvSize * vertices * faceIdx );
const fill = [ faceIdx, faceIdx, faceIdx, faceIdx, faceIdx, faceIdx ];
faceIndex.set( fill, faceIndexSize * vertices * faceIdx );
}
const planes = new BufferGeometry();
planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
lodPlanes.push( planes );
lodMeshes.push( new Mesh( planes, null ) );
if ( lod > LOD_MIN ) {
lod --;
}
}
return { lodPlanes, sizeLods, sigmas, lodMeshes };
}
function _createRenderTarget( width, height, params ) {
const cubeUVRenderTarget = new RenderTarget( width, height, params );
cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
cubeUVRenderTarget.texture.isPMREMTexture = true;
cubeUVRenderTarget.scissorTest = true;
return cubeUVRenderTarget;
}
function _setViewport( target, x, y, width, height ) {
target.viewport.set( x, y, width, height );
target.scissor.set( x, y, width, height );
}
function _getMaterial( type ) {
const material = new NodeMaterial();
material.depthTest = false;
material.depthWrite = false;
material.blending = NoBlending;
material.name = `PMREM_${ type }`;
return material;
}
function _getBlurShader( lodMax, width, height ) {
const weights = uniformArray( new Array( MAX_SAMPLES ).fill( 0 ) );
const poleAxis = uniform( new Vector3( 0, 1, 0 ) );
const dTheta = uniform( 0 );
const n = float( MAX_SAMPLES );
const latitudinal = uniform( 0 ); // false, bool
const samples = uniform( 1 ); // int
const envMap = texture( null );
const mipInt = uniform( 0 ); // int
const CUBEUV_TEXEL_WIDTH = float( 1 / width );
const CUBEUV_TEXEL_HEIGHT = float( 1 / height );
const CUBEUV_MAX_MIP = float( lodMax );
const materialUniforms = {
n,
latitudinal,
weights,
poleAxis,
outputDirection,
dTheta,
samples,
envMap,
mipInt,
CUBEUV_TEXEL_WIDTH,
CUBEUV_TEXEL_HEIGHT,
CUBEUV_MAX_MIP
};
const material = _getMaterial( 'blur' );
material.uniforms = materialUniforms; // TODO: Move to outside of the material
material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( 1 ) } );
return material;
}
function _getCubemapMaterial( envTexture ) {
const material = _getMaterial( 'cubemap' );
material.fragmentNode = cubeTexture( envTexture, outputDirection );
return material;
}
function _getEquirectMaterial( envTexture ) {
const material = _getMaterial( 'equirect' );
material.fragmentNode = texture( envTexture, equirectUV( outputDirection ), 0 );
return material;
}
export default PMREMGenerator;
+58
View File
@@ -0,0 +1,58 @@
import BindGroup from '../BindGroup.js';
class NodeBuilderState {
constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, monitor, transforms = [] ) {
this.vertexShader = vertexShader;
this.fragmentShader = fragmentShader;
this.computeShader = computeShader;
this.transforms = transforms;
this.nodeAttributes = nodeAttributes;
this.bindings = bindings;
this.updateNodes = updateNodes;
this.updateBeforeNodes = updateBeforeNodes;
this.updateAfterNodes = updateAfterNodes;
this.monitor = monitor;
this.usedTimes = 0;
}
createBindings() {
const bindings = [];
for ( const instanceGroup of this.bindings ) {
const shared = instanceGroup.bindings[ 0 ].groupNode.shared;
if ( shared !== true ) {
const bindingsGroup = new BindGroup( instanceGroup.name, [], instanceGroup.index, instanceGroup );
bindings.push( bindingsGroup );
for ( const instanceBinding of instanceGroup.bindings ) {
bindingsGroup.bindings.push( instanceBinding.clone() );
}
} else {
bindings.push( instanceGroup );
}
}
return bindings;
}
}
export default NodeBuilderState;
+105
View File
@@ -0,0 +1,105 @@
class NodeLibrary {
constructor() {
this.lightNodes = new WeakMap();
this.materialNodes = new Map();
this.toneMappingNodes = new Map();
}
fromMaterial( material ) {
if ( material.isNodeMaterial ) return material;
let nodeMaterial = null;
const nodeMaterialClass = this.getMaterialNodeClass( material.type );
if ( nodeMaterialClass !== null ) {
nodeMaterial = new nodeMaterialClass();
for ( const key in material ) {
nodeMaterial[ key ] = material[ key ];
}
}
return nodeMaterial;
}
addToneMapping( toneMappingNode, toneMapping ) {
this.addType( toneMappingNode, toneMapping, this.toneMappingNodes );
}
getToneMappingFunction( toneMapping ) {
return this.toneMappingNodes.get( toneMapping ) || null;
}
getMaterialNodeClass( materialType ) {
return this.materialNodes.get( materialType ) || null;
}
addMaterial( materialNodeClass, materialClassType ) {
this.addType( materialNodeClass, materialClassType, this.materialNodes );
}
getLightNodeClass( light ) {
return this.lightNodes.get( light ) || null;
}
addLight( lightNodeClass, lightClass ) {
this.addClass( lightNodeClass, lightClass, this.lightNodes );
}
addType( nodeClass, type, library ) {
if ( library.has( type ) ) {
console.warn( `Redefinition of node ${ type }` );
return;
}
if ( typeof nodeClass !== 'function' ) throw new Error( `Node class ${ nodeClass.name } is not a class.` );
if ( typeof type === 'function' || typeof type === 'object' ) throw new Error( `Base class ${ type } is not a class.` );
library.set( type, nodeClass );
}
addClass( nodeClass, baseClass, library ) {
if ( library.has( baseClass ) ) {
console.warn( `Redefinition of node ${ baseClass.name }` );
return;
}
if ( typeof nodeClass !== 'function' ) throw new Error( `Node class ${ nodeClass.name } is not a class.` );
if ( typeof baseClass !== 'function' ) throw new Error( `Base class ${ baseClass.name } is not a class.` );
library.set( baseClass, nodeClass );
}
}
export default NodeLibrary;
+64
View File
@@ -0,0 +1,64 @@
import { SampledTexture } from '../SampledTexture.js';
class NodeSampledTexture extends SampledTexture {
constructor( name, textureNode, groupNode, access = null ) {
super( name, textureNode ? textureNode.value : null );
this.textureNode = textureNode;
this.groupNode = groupNode;
this.access = access;
}
needsBindingsUpdate( generation ) {
return this.textureNode.value !== this.texture || super.needsBindingsUpdate( generation );
}
update() {
const { textureNode } = this;
if ( this.texture !== textureNode.value ) {
this.texture = textureNode.value;
return true;
}
return super.update();
}
}
class NodeSampledCubeTexture extends NodeSampledTexture {
constructor( name, textureNode, groupNode, access ) {
super( name, textureNode, groupNode, access );
this.isSampledCubeTexture = true;
}
}
class NodeSampledTexture3D extends NodeSampledTexture {
constructor( name, textureNode, groupNode, access ) {
super( name, textureNode, groupNode, access );
this.isSampledTexture3D = true;
}
}
export { NodeSampledTexture, NodeSampledCubeTexture, NodeSampledTexture3D };
+22
View File
@@ -0,0 +1,22 @@
import Sampler from '../Sampler.js';
class NodeSampler extends Sampler {
constructor( name, textureNode, groupNode ) {
super( name, textureNode ? textureNode.value : null );
this.textureNode = textureNode;
this.groupNode = groupNode;
}
update() {
this.texture = this.textureNode.value;
}
}
export default NodeSampler;
+26
View File
@@ -0,0 +1,26 @@
import StorageBuffer from '../StorageBuffer.js';
import { NodeAccess } from '../../../nodes/core/constants.js';
let _id = 0;
class NodeStorageBuffer extends StorageBuffer {
constructor( nodeUniform, groupNode ) {
super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
this.nodeUniform = nodeUniform;
this.access = nodeUniform ? nodeUniform.access : NodeAccess.READ_WRITE;
this.groupNode = groupNode;
}
get buffer() {
return this.nodeUniform.value;
}
}
export default NodeStorageBuffer;
+177
View File
@@ -0,0 +1,177 @@
import {
NumberUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform,
ColorUniform, Matrix3Uniform, Matrix4Uniform
} from '../Uniform.js';
class NumberNodeUniform extends NumberUniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class Vector2NodeUniform extends Vector2Uniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class Vector3NodeUniform extends Vector3Uniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class Vector4NodeUniform extends Vector4Uniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class ColorNodeUniform extends ColorUniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class Matrix3NodeUniform extends Matrix3Uniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
class Matrix4NodeUniform extends Matrix4Uniform {
constructor( nodeUniform ) {
super( nodeUniform.name, nodeUniform.value );
this.nodeUniform = nodeUniform;
}
getValue() {
return this.nodeUniform.value;
}
getType() {
return this.nodeUniform.type;
}
}
export {
NumberNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
};
+24
View File
@@ -0,0 +1,24 @@
import UniformBuffer from '../UniformBuffer.js';
let _id = 0;
class NodeUniformBuffer extends UniformBuffer {
constructor( nodeUniform, groupNode ) {
super( 'UniformBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
this.nodeUniform = nodeUniform;
this.groupNode = groupNode;
}
get buffer() {
return this.nodeUniform.value;
}
}
export default NodeUniformBuffer;
+38
View File
@@ -0,0 +1,38 @@
import UniformsGroup from '../UniformsGroup.js';
let _id = 0;
class NodeUniformsGroup extends UniformsGroup {
constructor( name, groupNode ) {
super( name );
this.id = _id ++;
this.groupNode = groupNode;
this.isNodeUniformsGroup = true;
}
getNodes() {
const nodes = [];
for ( const uniform of this.uniforms ) {
const node = uniform.nodeUniform.node;
if ( ! node ) throw new Error( 'NodeUniformsGroup: Uniform has no node.' );
nodes.push( node );
}
return nodes;
}
}
export default NodeUniformsGroup;
+534
View File
@@ -0,0 +1,534 @@
import DataMap from '../DataMap.js';
import ChainMap from '../ChainMap.js';
import NodeBuilderState from './NodeBuilderState.js';
import { cubeMapNode } from '../../../nodes/utils/CubeMapNode.js';
import { NodeFrame } from '../../../nodes/Nodes.js';
import { objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, pmremTexture, screenUV } from '../../../nodes/TSL.js';
import { CubeUVReflectionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../../constants.js';
import { hashArray } from '../../../nodes/core/NodeUtils.js';
const outputNodeMap = new WeakMap();
class Nodes extends DataMap {
constructor( renderer, backend ) {
super();
this.renderer = renderer;
this.backend = backend;
this.nodeFrame = new NodeFrame();
this.nodeBuilderCache = new Map();
this.callHashCache = new ChainMap();
this.groupsData = new ChainMap();
}
updateGroup( nodeUniformsGroup ) {
const groupNode = nodeUniformsGroup.groupNode;
const name = groupNode.name;
// objectGroup is every updated
if ( name === objectGroup.name ) return true;
// renderGroup is updated once per render/compute call
if ( name === renderGroup.name ) {
const uniformsGroupData = this.get( nodeUniformsGroup );
const renderId = this.nodeFrame.renderId;
if ( uniformsGroupData.renderId !== renderId ) {
uniformsGroupData.renderId = renderId;
return true;
}
return false;
}
// frameGroup is updated once per frame
if ( name === frameGroup.name ) {
const uniformsGroupData = this.get( nodeUniformsGroup );
const frameId = this.nodeFrame.frameId;
if ( uniformsGroupData.frameId !== frameId ) {
uniformsGroupData.frameId = frameId;
return true;
}
return false;
}
// other groups are updated just when groupNode.needsUpdate is true
const groupChain = [ groupNode, nodeUniformsGroup ];
let groupData = this.groupsData.get( groupChain );
if ( groupData === undefined ) this.groupsData.set( groupChain, groupData = {} );
if ( groupData.version !== groupNode.version ) {
groupData.version = groupNode.version;
return true;
}
return false;
}
getForRenderCacheKey( renderObject ) {
return renderObject.initialCacheKey;
}
getForRender( renderObject ) {
const renderObjectData = this.get( renderObject );
let nodeBuilderState = renderObjectData.nodeBuilderState;
if ( nodeBuilderState === undefined ) {
const { nodeBuilderCache } = this;
const cacheKey = this.getForRenderCacheKey( renderObject );
nodeBuilderState = nodeBuilderCache.get( cacheKey );
if ( nodeBuilderState === undefined ) {
const nodeBuilder = this.backend.createNodeBuilder( renderObject.object, this.renderer );
nodeBuilder.scene = renderObject.scene;
nodeBuilder.material = renderObject.material;
nodeBuilder.camera = renderObject.camera;
nodeBuilder.context.material = renderObject.material;
nodeBuilder.lightsNode = renderObject.lightsNode;
nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene );
nodeBuilder.fogNode = this.getFogNode( renderObject.scene );
nodeBuilder.clippingContext = renderObject.clippingContext;
nodeBuilder.build();
nodeBuilderState = this._createNodeBuilderState( nodeBuilder );
nodeBuilderCache.set( cacheKey, nodeBuilderState );
}
nodeBuilderState.usedTimes ++;
renderObjectData.nodeBuilderState = nodeBuilderState;
}
return nodeBuilderState;
}
delete( object ) {
if ( object.isRenderObject ) {
const nodeBuilderState = this.get( object ).nodeBuilderState;
nodeBuilderState.usedTimes --;
if ( nodeBuilderState.usedTimes === 0 ) {
this.nodeBuilderCache.delete( this.getForRenderCacheKey( object ) );
}
}
return super.delete( object );
}
getForCompute( computeNode ) {
const computeData = this.get( computeNode );
let nodeBuilderState = computeData.nodeBuilderState;
if ( nodeBuilderState === undefined ) {
const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer );
nodeBuilder.build();
nodeBuilderState = this._createNodeBuilderState( nodeBuilder );
computeData.nodeBuilderState = nodeBuilderState;
}
return nodeBuilderState;
}
_createNodeBuilderState( nodeBuilder ) {
return new NodeBuilderState(
nodeBuilder.vertexShader,
nodeBuilder.fragmentShader,
nodeBuilder.computeShader,
nodeBuilder.getAttributesArray(),
nodeBuilder.getBindings(),
nodeBuilder.updateNodes,
nodeBuilder.updateBeforeNodes,
nodeBuilder.updateAfterNodes,
nodeBuilder.monitor,
nodeBuilder.transforms
);
}
getEnvironmentNode( scene ) {
return scene.environmentNode || this.get( scene ).environmentNode || null;
}
getBackgroundNode( scene ) {
return scene.backgroundNode || this.get( scene ).backgroundNode || null;
}
getFogNode( scene ) {
return scene.fogNode || this.get( scene ).fogNode || null;
}
getCacheKey( scene, lightsNode ) {
const chain = [ scene, lightsNode ];
const callId = this.renderer.info.calls;
let cacheKeyData = this.callHashCache.get( chain );
if ( cacheKeyData === undefined || cacheKeyData.callId !== callId ) {
const environmentNode = this.getEnvironmentNode( scene );
const fogNode = this.getFogNode( scene );
const values = [];
if ( lightsNode ) values.push( lightsNode.getCacheKey( true ) );
if ( environmentNode ) values.push( environmentNode.getCacheKey() );
if ( fogNode ) values.push( fogNode.getCacheKey() );
values.push( this.renderer.shadowMap.enabled ? 1 : 0 );
cacheKeyData = {
callId,
cacheKey: hashArray( values )
};
this.callHashCache.set( chain, cacheKeyData );
}
return cacheKeyData.cacheKey;
}
updateScene( scene ) {
this.updateEnvironment( scene );
this.updateFog( scene );
this.updateBackground( scene );
}
get isToneMappingState() {
return this.renderer.getRenderTarget() ? false : true;
}
updateBackground( scene ) {
const sceneData = this.get( scene );
const background = scene.background;
if ( background ) {
const forceUpdate = ( scene.backgroundBlurriness === 0 && sceneData.backgroundBlurriness > 0 ) || ( scene.backgroundBlurriness > 0 && sceneData.backgroundBlurriness === 0 );
if ( sceneData.background !== background || forceUpdate ) {
let backgroundNode = null;
if ( background.isCubeTexture === true || ( background.mapping === EquirectangularReflectionMapping || background.mapping === EquirectangularRefractionMapping || background.mapping === CubeUVReflectionMapping ) ) {
if ( scene.backgroundBlurriness > 0 || background.mapping === CubeUVReflectionMapping ) {
backgroundNode = pmremTexture( background );
} else {
let envMap;
if ( background.isCubeTexture === true ) {
envMap = cubeTexture( background );
} else {
envMap = texture( background );
}
backgroundNode = cubeMapNode( envMap );
}
} else if ( background.isTexture === true ) {
backgroundNode = texture( background, screenUV.flipY() ).setUpdateMatrix( true );
} else if ( background.isColor !== true ) {
console.error( 'WebGPUNodes: Unsupported background configuration.', background );
}
sceneData.backgroundNode = backgroundNode;
sceneData.background = background;
sceneData.backgroundBlurriness = scene.backgroundBlurriness;
}
} else if ( sceneData.backgroundNode ) {
delete sceneData.backgroundNode;
delete sceneData.background;
}
}
updateFog( scene ) {
const sceneData = this.get( scene );
const fog = scene.fog;
if ( fog ) {
if ( sceneData.fog !== fog ) {
let fogNode = null;
if ( fog.isFogExp2 ) {
const color = reference( 'color', 'color', fog ).setGroup( renderGroup );
const density = reference( 'density', 'float', fog ).setGroup( renderGroup );
fogNode = densityFog( color, density );
} else if ( fog.isFog ) {
const color = reference( 'color', 'color', fog ).setGroup( renderGroup );
const near = reference( 'near', 'float', fog ).setGroup( renderGroup );
const far = reference( 'far', 'float', fog ).setGroup( renderGroup );
fogNode = rangeFog( color, near, far );
} else {
console.error( 'WebGPUNodes: Unsupported fog configuration.', fog );
}
sceneData.fogNode = fogNode;
sceneData.fog = fog;
}
} else {
delete sceneData.fogNode;
delete sceneData.fog;
}
}
updateEnvironment( scene ) {
const sceneData = this.get( scene );
const environment = scene.environment;
if ( environment ) {
if ( sceneData.environment !== environment ) {
let environmentNode = null;
if ( environment.isCubeTexture === true ) {
environmentNode = cubeTexture( environment );
} else if ( environment.isTexture === true ) {
environmentNode = texture( environment );
} else {
console.error( 'Nodes: Unsupported environment configuration.', environment );
}
sceneData.environmentNode = environmentNode;
sceneData.environment = environment;
}
} else if ( sceneData.environmentNode ) {
delete sceneData.environmentNode;
delete sceneData.environment;
}
}
getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) {
const nodeFrame = this.nodeFrame;
nodeFrame.renderer = renderer;
nodeFrame.scene = scene;
nodeFrame.object = object;
nodeFrame.camera = camera;
nodeFrame.material = material;
return nodeFrame;
}
getNodeFrameForRender( renderObject ) {
return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material );
}
getOutputCacheKey() {
const renderer = this.renderer;
return renderer.toneMapping + ',' + renderer.currentColorSpace;
}
hasOutputChange( outputTarget ) {
const cacheKey = outputNodeMap.get( outputTarget );
return cacheKey !== this.getOutputCacheKey();
}
getOutputNode( outputTexture ) {
const renderer = this.renderer;
const cacheKey = this.getOutputCacheKey();
const output = texture( outputTexture, screenUV ).renderOutput( renderer.toneMapping, renderer.currentColorSpace );
outputNodeMap.set( outputTexture, cacheKey );
return output;
}
updateBefore( renderObject ) {
const nodeBuilder = renderObject.getNodeBuilderState();
for ( const node of nodeBuilder.updateBeforeNodes ) {
// update frame state for each node
this.getNodeFrameForRender( renderObject ).updateBeforeNode( node );
}
}
updateAfter( renderObject ) {
const nodeBuilder = renderObject.getNodeBuilderState();
for ( const node of nodeBuilder.updateAfterNodes ) {
// update frame state for each node
this.getNodeFrameForRender( renderObject ).updateAfterNode( node );
}
}
updateForCompute( computeNode ) {
const nodeFrame = this.getNodeFrame();
const nodeBuilder = this.getForCompute( computeNode );
for ( const node of nodeBuilder.updateNodes ) {
nodeFrame.updateNode( node );
}
}
updateForRender( renderObject ) {
const nodeFrame = this.getNodeFrameForRender( renderObject );
const nodeBuilder = renderObject.getNodeBuilderState();
for ( const node of nodeBuilder.updateNodes ) {
nodeFrame.updateNode( node );
}
}
needsRefresh( renderObject ) {
const nodeFrame = this.getNodeFrameForRender( renderObject );
const monitor = renderObject.getMonitor();
return monitor.needsRefresh( renderObject, nodeFrame );
}
dispose() {
super.dispose();
this.nodeFrame = new NodeFrame();
this.nodeBuilderCache = new Map();
}
}
export default Nodes;
+270
View File
@@ -0,0 +1,270 @@
import alphahash_fragment from './ShaderChunk/alphahash_fragment.glsl.js';
import alphahash_pars_fragment from './ShaderChunk/alphahash_pars_fragment.glsl.js';
import alphamap_fragment from './ShaderChunk/alphamap_fragment.glsl.js';
import alphamap_pars_fragment from './ShaderChunk/alphamap_pars_fragment.glsl.js';
import alphatest_fragment from './ShaderChunk/alphatest_fragment.glsl.js';
import alphatest_pars_fragment from './ShaderChunk/alphatest_pars_fragment.glsl.js';
import aomap_fragment from './ShaderChunk/aomap_fragment.glsl.js';
import aomap_pars_fragment from './ShaderChunk/aomap_pars_fragment.glsl.js';
import batching_pars_vertex from './ShaderChunk/batching_pars_vertex.glsl.js';
import batching_vertex from './ShaderChunk/batching_vertex.glsl.js';
import begin_vertex from './ShaderChunk/begin_vertex.glsl.js';
import beginnormal_vertex from './ShaderChunk/beginnormal_vertex.glsl.js';
import bsdfs from './ShaderChunk/bsdfs.glsl.js';
import iridescence_fragment from './ShaderChunk/iridescence_fragment.glsl.js';
import bumpmap_pars_fragment from './ShaderChunk/bumpmap_pars_fragment.glsl.js';
import clipping_planes_fragment from './ShaderChunk/clipping_planes_fragment.glsl.js';
import clipping_planes_pars_fragment from './ShaderChunk/clipping_planes_pars_fragment.glsl.js';
import clipping_planes_pars_vertex from './ShaderChunk/clipping_planes_pars_vertex.glsl.js';
import clipping_planes_vertex from './ShaderChunk/clipping_planes_vertex.glsl.js';
import color_fragment from './ShaderChunk/color_fragment.glsl.js';
import color_pars_fragment from './ShaderChunk/color_pars_fragment.glsl.js';
import color_pars_vertex from './ShaderChunk/color_pars_vertex.glsl.js';
import color_vertex from './ShaderChunk/color_vertex.glsl.js';
import common from './ShaderChunk/common.glsl.js';
import cube_uv_reflection_fragment from './ShaderChunk/cube_uv_reflection_fragment.glsl.js';
import defaultnormal_vertex from './ShaderChunk/defaultnormal_vertex.glsl.js';
import displacementmap_pars_vertex from './ShaderChunk/displacementmap_pars_vertex.glsl.js';
import displacementmap_vertex from './ShaderChunk/displacementmap_vertex.glsl.js';
import emissivemap_fragment from './ShaderChunk/emissivemap_fragment.glsl.js';
import emissivemap_pars_fragment from './ShaderChunk/emissivemap_pars_fragment.glsl.js';
import colorspace_fragment from './ShaderChunk/colorspace_fragment.glsl.js';
import colorspace_pars_fragment from './ShaderChunk/colorspace_pars_fragment.glsl.js';
import envmap_fragment from './ShaderChunk/envmap_fragment.glsl.js';
import envmap_common_pars_fragment from './ShaderChunk/envmap_common_pars_fragment.glsl.js';
import envmap_pars_fragment from './ShaderChunk/envmap_pars_fragment.glsl.js';
import envmap_pars_vertex from './ShaderChunk/envmap_pars_vertex.glsl.js';
import envmap_vertex from './ShaderChunk/envmap_vertex.glsl.js';
import fog_vertex from './ShaderChunk/fog_vertex.glsl.js';
import fog_pars_vertex from './ShaderChunk/fog_pars_vertex.glsl.js';
import fog_fragment from './ShaderChunk/fog_fragment.glsl.js';
import fog_pars_fragment from './ShaderChunk/fog_pars_fragment.glsl.js';
import gradientmap_pars_fragment from './ShaderChunk/gradientmap_pars_fragment.glsl.js';
import lightmap_pars_fragment from './ShaderChunk/lightmap_pars_fragment.glsl.js';
import lights_lambert_fragment from './ShaderChunk/lights_lambert_fragment.glsl.js';
import lights_lambert_pars_fragment from './ShaderChunk/lights_lambert_pars_fragment.glsl.js';
import lights_pars_begin from './ShaderChunk/lights_pars_begin.glsl.js';
import envmap_physical_pars_fragment from './ShaderChunk/envmap_physical_pars_fragment.glsl.js';
import lights_toon_fragment from './ShaderChunk/lights_toon_fragment.glsl.js';
import lights_toon_pars_fragment from './ShaderChunk/lights_toon_pars_fragment.glsl.js';
import lights_phong_fragment from './ShaderChunk/lights_phong_fragment.glsl.js';
import lights_phong_pars_fragment from './ShaderChunk/lights_phong_pars_fragment.glsl.js';
import lights_physical_fragment from './ShaderChunk/lights_physical_fragment.glsl.js';
import lights_physical_pars_fragment from './ShaderChunk/lights_physical_pars_fragment.glsl.js';
import lights_fragment_begin from './ShaderChunk/lights_fragment_begin.glsl.js';
import lights_fragment_maps from './ShaderChunk/lights_fragment_maps.glsl.js';
import lights_fragment_end from './ShaderChunk/lights_fragment_end.glsl.js';
import logdepthbuf_fragment from './ShaderChunk/logdepthbuf_fragment.glsl.js';
import logdepthbuf_pars_fragment from './ShaderChunk/logdepthbuf_pars_fragment.glsl.js';
import logdepthbuf_pars_vertex from './ShaderChunk/logdepthbuf_pars_vertex.glsl.js';
import logdepthbuf_vertex from './ShaderChunk/logdepthbuf_vertex.glsl.js';
import map_fragment from './ShaderChunk/map_fragment.glsl.js';
import map_pars_fragment from './ShaderChunk/map_pars_fragment.glsl.js';
import map_particle_fragment from './ShaderChunk/map_particle_fragment.glsl.js';
import map_particle_pars_fragment from './ShaderChunk/map_particle_pars_fragment.glsl.js';
import metalnessmap_fragment from './ShaderChunk/metalnessmap_fragment.glsl.js';
import metalnessmap_pars_fragment from './ShaderChunk/metalnessmap_pars_fragment.glsl.js';
import morphinstance_vertex from './ShaderChunk/morphinstance_vertex.glsl.js';
import morphcolor_vertex from './ShaderChunk/morphcolor_vertex.glsl.js';
import morphnormal_vertex from './ShaderChunk/morphnormal_vertex.glsl.js';
import morphtarget_pars_vertex from './ShaderChunk/morphtarget_pars_vertex.glsl.js';
import morphtarget_vertex from './ShaderChunk/morphtarget_vertex.glsl.js';
import normal_fragment_begin from './ShaderChunk/normal_fragment_begin.glsl.js';
import normal_fragment_maps from './ShaderChunk/normal_fragment_maps.glsl.js';
import normal_pars_fragment from './ShaderChunk/normal_pars_fragment.glsl.js';
import normal_pars_vertex from './ShaderChunk/normal_pars_vertex.glsl.js';
import normal_vertex from './ShaderChunk/normal_vertex.glsl.js';
import normalmap_pars_fragment from './ShaderChunk/normalmap_pars_fragment.glsl.js';
import clearcoat_normal_fragment_begin from './ShaderChunk/clearcoat_normal_fragment_begin.glsl.js';
import clearcoat_normal_fragment_maps from './ShaderChunk/clearcoat_normal_fragment_maps.glsl.js';
import clearcoat_pars_fragment from './ShaderChunk/clearcoat_pars_fragment.glsl.js';
import iridescence_pars_fragment from './ShaderChunk/iridescence_pars_fragment.glsl.js';
import opaque_fragment from './ShaderChunk/opaque_fragment.glsl.js';
import packing from './ShaderChunk/packing.glsl.js';
import premultiplied_alpha_fragment from './ShaderChunk/premultiplied_alpha_fragment.glsl.js';
import project_vertex from './ShaderChunk/project_vertex.glsl.js';
import dithering_fragment from './ShaderChunk/dithering_fragment.glsl.js';
import dithering_pars_fragment from './ShaderChunk/dithering_pars_fragment.glsl.js';
import roughnessmap_fragment from './ShaderChunk/roughnessmap_fragment.glsl.js';
import roughnessmap_pars_fragment from './ShaderChunk/roughnessmap_pars_fragment.glsl.js';
import shadowmap_pars_fragment from './ShaderChunk/shadowmap_pars_fragment.glsl.js';
import shadowmap_pars_vertex from './ShaderChunk/shadowmap_pars_vertex.glsl.js';
import shadowmap_vertex from './ShaderChunk/shadowmap_vertex.glsl.js';
import shadowmask_pars_fragment from './ShaderChunk/shadowmask_pars_fragment.glsl.js';
import skinbase_vertex from './ShaderChunk/skinbase_vertex.glsl.js';
import skinning_pars_vertex from './ShaderChunk/skinning_pars_vertex.glsl.js';
import skinning_vertex from './ShaderChunk/skinning_vertex.glsl.js';
import skinnormal_vertex from './ShaderChunk/skinnormal_vertex.glsl.js';
import specularmap_fragment from './ShaderChunk/specularmap_fragment.glsl.js';
import specularmap_pars_fragment from './ShaderChunk/specularmap_pars_fragment.glsl.js';
import tonemapping_fragment from './ShaderChunk/tonemapping_fragment.glsl.js';
import tonemapping_pars_fragment from './ShaderChunk/tonemapping_pars_fragment.glsl.js';
import transmission_fragment from './ShaderChunk/transmission_fragment.glsl.js';
import transmission_pars_fragment from './ShaderChunk/transmission_pars_fragment.glsl.js';
import uv_pars_fragment from './ShaderChunk/uv_pars_fragment.glsl.js';
import uv_pars_vertex from './ShaderChunk/uv_pars_vertex.glsl.js';
import uv_vertex from './ShaderChunk/uv_vertex.glsl.js';
import worldpos_vertex from './ShaderChunk/worldpos_vertex.glsl.js';
import * as background from './ShaderLib/background.glsl.js';
import * as backgroundCube from './ShaderLib/backgroundCube.glsl.js';
import * as cube from './ShaderLib/cube.glsl.js';
import * as depth from './ShaderLib/depth.glsl.js';
import * as distanceRGBA from './ShaderLib/distanceRGBA.glsl.js';
import * as equirect from './ShaderLib/equirect.glsl.js';
import * as linedashed from './ShaderLib/linedashed.glsl.js';
import * as meshbasic from './ShaderLib/meshbasic.glsl.js';
import * as meshlambert from './ShaderLib/meshlambert.glsl.js';
import * as meshmatcap from './ShaderLib/meshmatcap.glsl.js';
import * as meshnormal from './ShaderLib/meshnormal.glsl.js';
import * as meshphong from './ShaderLib/meshphong.glsl.js';
import * as meshphysical from './ShaderLib/meshphysical.glsl.js';
import * as meshtoon from './ShaderLib/meshtoon.glsl.js';
import * as points from './ShaderLib/points.glsl.js';
import * as shadow from './ShaderLib/shadow.glsl.js';
import * as sprite from './ShaderLib/sprite.glsl.js';
export const ShaderChunk = {
alphahash_fragment: alphahash_fragment,
alphahash_pars_fragment: alphahash_pars_fragment,
alphamap_fragment: alphamap_fragment,
alphamap_pars_fragment: alphamap_pars_fragment,
alphatest_fragment: alphatest_fragment,
alphatest_pars_fragment: alphatest_pars_fragment,
aomap_fragment: aomap_fragment,
aomap_pars_fragment: aomap_pars_fragment,
batching_pars_vertex: batching_pars_vertex,
batching_vertex: batching_vertex,
begin_vertex: begin_vertex,
beginnormal_vertex: beginnormal_vertex,
bsdfs: bsdfs,
iridescence_fragment: iridescence_fragment,
bumpmap_pars_fragment: bumpmap_pars_fragment,
clipping_planes_fragment: clipping_planes_fragment,
clipping_planes_pars_fragment: clipping_planes_pars_fragment,
clipping_planes_pars_vertex: clipping_planes_pars_vertex,
clipping_planes_vertex: clipping_planes_vertex,
color_fragment: color_fragment,
color_pars_fragment: color_pars_fragment,
color_pars_vertex: color_pars_vertex,
color_vertex: color_vertex,
common: common,
cube_uv_reflection_fragment: cube_uv_reflection_fragment,
defaultnormal_vertex: defaultnormal_vertex,
displacementmap_pars_vertex: displacementmap_pars_vertex,
displacementmap_vertex: displacementmap_vertex,
emissivemap_fragment: emissivemap_fragment,
emissivemap_pars_fragment: emissivemap_pars_fragment,
colorspace_fragment: colorspace_fragment,
colorspace_pars_fragment: colorspace_pars_fragment,
envmap_fragment: envmap_fragment,
envmap_common_pars_fragment: envmap_common_pars_fragment,
envmap_pars_fragment: envmap_pars_fragment,
envmap_pars_vertex: envmap_pars_vertex,
envmap_physical_pars_fragment: envmap_physical_pars_fragment,
envmap_vertex: envmap_vertex,
fog_vertex: fog_vertex,
fog_pars_vertex: fog_pars_vertex,
fog_fragment: fog_fragment,
fog_pars_fragment: fog_pars_fragment,
gradientmap_pars_fragment: gradientmap_pars_fragment,
lightmap_pars_fragment: lightmap_pars_fragment,
lights_lambert_fragment: lights_lambert_fragment,
lights_lambert_pars_fragment: lights_lambert_pars_fragment,
lights_pars_begin: lights_pars_begin,
lights_toon_fragment: lights_toon_fragment,
lights_toon_pars_fragment: lights_toon_pars_fragment,
lights_phong_fragment: lights_phong_fragment,
lights_phong_pars_fragment: lights_phong_pars_fragment,
lights_physical_fragment: lights_physical_fragment,
lights_physical_pars_fragment: lights_physical_pars_fragment,
lights_fragment_begin: lights_fragment_begin,
lights_fragment_maps: lights_fragment_maps,
lights_fragment_end: lights_fragment_end,
logdepthbuf_fragment: logdepthbuf_fragment,
logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,
logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,
logdepthbuf_vertex: logdepthbuf_vertex,
map_fragment: map_fragment,
map_pars_fragment: map_pars_fragment,
map_particle_fragment: map_particle_fragment,
map_particle_pars_fragment: map_particle_pars_fragment,
metalnessmap_fragment: metalnessmap_fragment,
metalnessmap_pars_fragment: metalnessmap_pars_fragment,
morphinstance_vertex: morphinstance_vertex,
morphcolor_vertex: morphcolor_vertex,
morphnormal_vertex: morphnormal_vertex,
morphtarget_pars_vertex: morphtarget_pars_vertex,
morphtarget_vertex: morphtarget_vertex,
normal_fragment_begin: normal_fragment_begin,
normal_fragment_maps: normal_fragment_maps,
normal_pars_fragment: normal_pars_fragment,
normal_pars_vertex: normal_pars_vertex,
normal_vertex: normal_vertex,
normalmap_pars_fragment: normalmap_pars_fragment,
clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin,
clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps,
clearcoat_pars_fragment: clearcoat_pars_fragment,
iridescence_pars_fragment: iridescence_pars_fragment,
opaque_fragment: opaque_fragment,
packing: packing,
premultiplied_alpha_fragment: premultiplied_alpha_fragment,
project_vertex: project_vertex,
dithering_fragment: dithering_fragment,
dithering_pars_fragment: dithering_pars_fragment,
roughnessmap_fragment: roughnessmap_fragment,
roughnessmap_pars_fragment: roughnessmap_pars_fragment,
shadowmap_pars_fragment: shadowmap_pars_fragment,
shadowmap_pars_vertex: shadowmap_pars_vertex,
shadowmap_vertex: shadowmap_vertex,
shadowmask_pars_fragment: shadowmask_pars_fragment,
skinbase_vertex: skinbase_vertex,
skinning_pars_vertex: skinning_pars_vertex,
skinning_vertex: skinning_vertex,
skinnormal_vertex: skinnormal_vertex,
specularmap_fragment: specularmap_fragment,
specularmap_pars_fragment: specularmap_pars_fragment,
tonemapping_fragment: tonemapping_fragment,
tonemapping_pars_fragment: tonemapping_pars_fragment,
transmission_fragment: transmission_fragment,
transmission_pars_fragment: transmission_pars_fragment,
uv_pars_fragment: uv_pars_fragment,
uv_pars_vertex: uv_pars_vertex,
uv_vertex: uv_vertex,
worldpos_vertex: worldpos_vertex,
background_vert: background.vertex,
background_frag: background.fragment,
backgroundCube_vert: backgroundCube.vertex,
backgroundCube_frag: backgroundCube.fragment,
cube_vert: cube.vertex,
cube_frag: cube.fragment,
depth_vert: depth.vertex,
depth_frag: depth.fragment,
distanceRGBA_vert: distanceRGBA.vertex,
distanceRGBA_frag: distanceRGBA.fragment,
equirect_vert: equirect.vertex,
equirect_frag: equirect.fragment,
linedashed_vert: linedashed.vertex,
linedashed_frag: linedashed.fragment,
meshbasic_vert: meshbasic.vertex,
meshbasic_frag: meshbasic.fragment,
meshlambert_vert: meshlambert.vertex,
meshlambert_frag: meshlambert.fragment,
meshmatcap_vert: meshmatcap.vertex,
meshmatcap_frag: meshmatcap.fragment,
meshnormal_vert: meshnormal.vertex,
meshnormal_frag: meshnormal.fragment,
meshphong_vert: meshphong.vertex,
meshphong_frag: meshphong.fragment,
meshphysical_vert: meshphysical.vertex,
meshphysical_frag: meshphysical.fragment,
meshtoon_vert: meshtoon.vertex,
meshtoon_frag: meshtoon.fragment,
points_vert: points.vertex,
points_frag: points.fragment,
shadow_vert: shadow.vertex,
shadow_frag: shadow.fragment,
sprite_vert: sprite.vertex,
sprite_frag: sprite.fragment
};
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef USE_ALPHAHASH
if ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard;
#endif
`;
@@ -0,0 +1,68 @@
export default /* glsl */`
#ifdef USE_ALPHAHASH
/**
* See: https://casual-effects.com/research/Wyman2017Hashed/index.html
*/
const float ALPHA_HASH_SCALE = 0.05; // Derived from trials only, and may be changed.
float hash2D( vec2 value ) {
return fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );
}
float hash3D( vec3 value ) {
return hash2D( vec2( hash2D( value.xy ), value.z ) );
}
float getAlphaHashThreshold( vec3 position ) {
// Find the discretized derivatives of our coordinates
float maxDeriv = max(
length( dFdx( position.xyz ) ),
length( dFdy( position.xyz ) )
);
float pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );
// Find two nearest log-discretized noise scales
vec2 pixScales = vec2(
exp2( floor( log2( pixScale ) ) ),
exp2( ceil( log2( pixScale ) ) )
);
// Compute alpha thresholds at our two noise scales
vec2 alpha = vec2(
hash3D( floor( pixScales.x * position.xyz ) ),
hash3D( floor( pixScales.y * position.xyz ) )
);
// Factor to interpolate lerp with
float lerpFactor = fract( log2( pixScale ) );
// Interpolate alpha threshold from noise at two scales
float x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;
// Pass into CDF to compute uniformly distrib threshold
float a = min( lerpFactor, 1.0 - lerpFactor );
vec3 cases = vec3(
x * x / ( 2.0 * a * ( 1.0 - a ) ),
( x - 0.5 * a ) / ( 1.0 - a ),
1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )
);
// Find our final, uniformly distributed alpha threshold (ατ)
float threshold = ( x < ( 1.0 - a ) )
? ( ( x < a ) ? cases.x : cases.y )
: cases.z;
// Avoids ατ == 0. Could also do ατ =1-ατ
return clamp( threshold , 1.0e-6, 1.0 );
}
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef USE_ALPHAMAP
diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g;
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef USE_ALPHAMAP
uniform sampler2D alphaMap;
#endif
`;
@@ -0,0 +1,16 @@
export default /* glsl */`
#ifdef USE_ALPHATEST
#ifdef ALPHA_TO_COVERAGE
diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a );
if ( diffuseColor.a == 0.0 ) discard;
#else
if ( diffuseColor.a < alphaTest ) discard;
#endif
#endif
`;
@@ -0,0 +1,5 @@
export default /* glsl */`
#ifdef USE_ALPHATEST
uniform float alphaTest;
#endif
`;
@@ -0,0 +1,26 @@
export default /* glsl */`
#ifdef USE_AOMAP
// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;
reflectedLight.indirectDiffuse *= ambientOcclusion;
#if defined( USE_CLEARCOAT )
clearcoatSpecularIndirect *= ambientOcclusion;
#endif
#if defined( USE_SHEEN )
sheenSpecularIndirect *= ambientOcclusion;
#endif
#if defined( USE_ENVMAP ) && defined( STANDARD )
float dotNV = saturate( dot( geometryNormal, geometryViewDir ) );
reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );
#endif
#endif
`;
@@ -0,0 +1,8 @@
export default /* glsl */`
#ifdef USE_AOMAP
uniform sampler2D aoMap;
uniform float aoMapIntensity;
#endif
`;
@@ -0,0 +1,49 @@
export default /* glsl */`
#ifdef USE_BATCHING
#if ! defined( GL_ANGLE_multi_draw )
#define gl_DrawID _gl_DrawID
uniform int _gl_DrawID;
#endif
uniform highp sampler2D batchingTexture;
uniform highp usampler2D batchingIdTexture;
mat4 getBatchingMatrix( const in float i ) {
int size = textureSize( batchingTexture, 0 ).x;
int j = int( i ) * 4;
int x = j % size;
int y = j / size;
vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );
vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );
vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );
vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );
return mat4( v1, v2, v3, v4 );
}
float getIndirectIndex( const in int i ) {
int size = textureSize( batchingIdTexture, 0 ).x;
int x = i % size;
int y = i / size;
return float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r );
}
#endif
#ifdef USE_BATCHING_COLOR
uniform sampler2D batchingColorTexture;
vec3 getBatchingColor( const in float i ) {
int size = textureSize( batchingColorTexture, 0 ).x;
int j = int( i );
int x = j % size;
int y = j / size;
return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb;
}
#endif
`;
@@ -0,0 +1,5 @@
export default /* glsl */`
#ifdef USE_BATCHING
mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) );
#endif
`;
@@ -0,0 +1,9 @@
export default /* glsl */`
vec3 transformed = vec3( position );
#ifdef USE_ALPHAHASH
vPosition = vec3( position );
#endif
`;
@@ -0,0 +1,9 @@
export default /* glsl */`
vec3 objectNormal = vec3( normal );
#ifdef USE_TANGENT
vec3 objectTangent = vec3( tangent.xyz );
#endif
`;
+33
View File
@@ -0,0 +1,33 @@
export default /* glsl */`
float G_BlinnPhong_Implicit( /* const in float dotNL, const in float dotNV */ ) {
// geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v)
return 0.25;
}
float D_BlinnPhong( const in float shininess, const in float dotNH ) {
return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );
}
vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {
vec3 halfDir = normalize( lightDir + viewDir );
float dotNH = saturate( dot( normal, halfDir ) );
float dotVH = saturate( dot( viewDir, halfDir ) );
vec3 F = F_Schlick( specularColor, 1.0, dotVH );
float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );
float D = D_BlinnPhong( shininess, dotNH );
return F * ( G * D );
} // validated
`;
@@ -0,0 +1,43 @@
export default /* glsl */`
#ifdef USE_BUMPMAP
uniform sampler2D bumpMap;
uniform float bumpScale;
// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
vec2 dHdxy_fwd() {
vec2 dSTdx = dFdx( vBumpMapUv );
vec2 dSTdy = dFdy( vBumpMapUv );
float Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x;
float dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll;
float dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll;
return vec2( dBx, dBy );
}
vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {
// normalize is done to ensure that the bump map looks the same regardless of the texture's scale
vec3 vSigmaX = normalize( dFdx( surf_pos.xyz ) );
vec3 vSigmaY = normalize( dFdy( surf_pos.xyz ) );
vec3 vN = surf_norm; // normalized
vec3 R1 = cross( vSigmaY, vN );
vec3 R2 = cross( vN, vSigmaX );
float fDet = dot( vSigmaX, R1 ) * faceDirection;
vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
return normalize( abs( fDet ) * surf_norm - vGrad );
}
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef USE_CLEARCOAT
vec3 clearcoatNormal = nonPerturbedNormal;
#endif
`;
@@ -0,0 +1,10 @@
export default /* glsl */`
#ifdef USE_CLEARCOAT_NORMALMAP
vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0;
clearcoatMapN.xy *= clearcoatNormalScale;
clearcoatNormal = normalize( tbn2 * clearcoatMapN );
#endif
`;
@@ -0,0 +1,21 @@
export default /* glsl */`
#ifdef USE_CLEARCOATMAP
uniform sampler2D clearcoatMap;
#endif
#ifdef USE_CLEARCOAT_NORMALMAP
uniform sampler2D clearcoatNormalMap;
uniform vec2 clearcoatNormalScale;
#endif
#ifdef USE_CLEARCOAT_ROUGHNESSMAP
uniform sampler2D clearcoatRoughnessMap;
#endif
`;
@@ -0,0 +1,78 @@
export default /* glsl */`
#if NUM_CLIPPING_PLANES > 0
vec4 plane;
#ifdef ALPHA_TO_COVERAGE
float distanceToPlane, distanceGradient;
float clipOpacity = 1.0;
#pragma unroll_loop_start
for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {
plane = clippingPlanes[ i ];
distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w;
distanceGradient = fwidth( distanceToPlane ) / 2.0;
clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane );
if ( clipOpacity == 0.0 ) discard;
}
#pragma unroll_loop_end
#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES
float unionClipOpacity = 1.0;
#pragma unroll_loop_start
for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {
plane = clippingPlanes[ i ];
distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w;
distanceGradient = fwidth( distanceToPlane ) / 2.0;
unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane );
}
#pragma unroll_loop_end
clipOpacity *= 1.0 - unionClipOpacity;
#endif
diffuseColor.a *= clipOpacity;
if ( diffuseColor.a == 0.0 ) discard;
#else
#pragma unroll_loop_start
for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {
plane = clippingPlanes[ i ];
if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;
}
#pragma unroll_loop_end
#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES
bool clipped = true;
#pragma unroll_loop_start
for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {
plane = clippingPlanes[ i ];
clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;
}
#pragma unroll_loop_end
if ( clipped ) discard;
#endif
#endif
#endif
`;
@@ -0,0 +1,9 @@
export default /* glsl */`
#if NUM_CLIPPING_PLANES > 0
varying vec3 vClipPosition;
uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#if NUM_CLIPPING_PLANES > 0
varying vec3 vClipPosition;
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#if NUM_CLIPPING_PLANES > 0
vClipPosition = - mvPosition.xyz;
#endif
`;
@@ -0,0 +1,11 @@
export default /* glsl */`
#if defined( USE_COLOR_ALPHA )
diffuseColor *= vColor;
#elif defined( USE_COLOR )
diffuseColor.rgb *= vColor;
#endif
`;
@@ -0,0 +1,11 @@
export default /* glsl */`
#if defined( USE_COLOR_ALPHA )
varying vec4 vColor;
#elif defined( USE_COLOR )
varying vec3 vColor;
#endif
`;
@@ -0,0 +1,11 @@
export default /* glsl */`
#if defined( USE_COLOR_ALPHA )
varying vec4 vColor;
#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )
varying vec3 vColor;
#endif
`;
@@ -0,0 +1,31 @@
export default /* glsl */`
#if defined( USE_COLOR_ALPHA )
vColor = vec4( 1.0 );
#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )
vColor = vec3( 1.0 );
#endif
#ifdef USE_COLOR
vColor *= color;
#endif
#ifdef USE_INSTANCING_COLOR
vColor.xyz *= instanceColor.xyz;
#endif
#ifdef USE_BATCHING_COLOR
vec3 batchingColor = getBatchingColor( getIndirectIndex( gl_DrawID ) );
vColor.xyz *= batchingColor.xyz;
#endif
`;
@@ -0,0 +1,3 @@
export default /* glsl */`
gl_FragColor = linearToOutputTexel( gl_FragColor );
`;
@@ -0,0 +1,15 @@
export default /* glsl */`
vec4 LinearTransferOETF( in vec4 value ) {
return value;
}
vec4 sRGBTransferEOTF( in vec4 value ) {
return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );
}
vec4 sRGBTransferOETF( in vec4 value ) {
return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
}
`;
+137
View File
@@ -0,0 +1,137 @@
export default /* glsl */`
#define PI 3.141592653589793
#define PI2 6.283185307179586
#define PI_HALF 1.5707963267948966
#define RECIPROCAL_PI 0.3183098861837907
#define RECIPROCAL_PI2 0.15915494309189535
#define EPSILON 1e-6
#ifndef saturate
// <tonemapping_pars_fragment> may have defined saturate() already
#define saturate( a ) clamp( a, 0.0, 1.0 )
#endif
#define whiteComplement( a ) ( 1.0 - saturate( a ) )
float pow2( const in float x ) { return x*x; }
vec3 pow2( const in vec3 x ) { return x*x; }
float pow3( const in float x ) { return x*x*x; }
float pow4( const in float x ) { float x2 = x*x; return x2*x2; }
float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }
float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }
// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.
// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
highp float rand( const in vec2 uv ) {
const highp float a = 12.9898, b = 78.233, c = 43758.5453;
highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );
return fract( sin( sn ) * c );
}
#ifdef HIGH_PRECISION
float precisionSafeLength( vec3 v ) { return length( v ); }
#else
float precisionSafeLength( vec3 v ) {
float maxComponent = max3( abs( v ) );
return length( v / maxComponent ) * maxComponent;
}
#endif
struct IncidentLight {
vec3 color;
vec3 direction;
bool visible;
};
struct ReflectedLight {
vec3 directDiffuse;
vec3 directSpecular;
vec3 indirectDiffuse;
vec3 indirectSpecular;
};
#ifdef USE_ALPHAHASH
varying vec3 vPosition;
#endif
vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
}
vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {
// dir can be either a direction vector or a normal vector
// upper-left 3x3 of matrix is assumed to be orthogonal
return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );
}
mat3 transposeMat3( const in mat3 m ) {
mat3 tmp;
tmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );
tmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );
tmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );
return tmp;
}
bool isPerspectiveMatrix( mat4 m ) {
return m[ 2 ][ 3 ] == - 1.0;
}
vec2 equirectUv( in vec3 dir ) {
// dir is assumed to be unit length
float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;
float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;
return vec2( u, v );
}
vec3 BRDF_Lambert( const in vec3 diffuseColor ) {
return RECIPROCAL_PI * diffuseColor;
} // validated
vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {
// Original approximation by Christophe Schlick '94
// float fresnel = pow( 1.0 - dotVH, 5.0 );
// Optimized variant (presented by Epic at SIGGRAPH '13)
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
} // validated
float F_Schlick( const in float f0, const in float f90, const in float dotVH ) {
// Original approximation by Christophe Schlick '94
// float fresnel = pow( 1.0 - dotVH, 5.0 );
// Optimized variant (presented by Epic at SIGGRAPH '13)
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );
return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );
} // validated
`;
@@ -0,0 +1,186 @@
export default /* glsl */`
#ifdef ENVMAP_TYPE_CUBE_UV
#define cubeUV_minMipLevel 4.0
#define cubeUV_minTileSize 16.0
// These shader functions convert between the UV coordinates of a single face of
// a cubemap, the 0-5 integer index of a cube face, and the direction vector for
// sampling a textureCube (not generally normalized ).
float getFace( vec3 direction ) {
vec3 absDirection = abs( direction );
float face = - 1.0;
if ( absDirection.x > absDirection.z ) {
if ( absDirection.x > absDirection.y )
face = direction.x > 0.0 ? 0.0 : 3.0;
else
face = direction.y > 0.0 ? 1.0 : 4.0;
} else {
if ( absDirection.z > absDirection.y )
face = direction.z > 0.0 ? 2.0 : 5.0;
else
face = direction.y > 0.0 ? 1.0 : 4.0;
}
return face;
}
// RH coordinate system; PMREM face-indexing convention
vec2 getUV( vec3 direction, float face ) {
vec2 uv;
if ( face == 0.0 ) {
uv = vec2( direction.z, direction.y ) / abs( direction.x ); // pos x
} else if ( face == 1.0 ) {
uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); // pos y
} else if ( face == 2.0 ) {
uv = vec2( - direction.x, direction.y ) / abs( direction.z ); // pos z
} else if ( face == 3.0 ) {
uv = vec2( - direction.z, direction.y ) / abs( direction.x ); // neg x
} else if ( face == 4.0 ) {
uv = vec2( - direction.x, direction.z ) / abs( direction.y ); // neg y
} else {
uv = vec2( direction.x, direction.y ) / abs( direction.z ); // neg z
}
return 0.5 * ( uv + 1.0 );
}
vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {
float face = getFace( direction );
float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );
mipInt = max( mipInt, cubeUV_minMipLevel );
float faceSize = exp2( mipInt );
highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; // #25071
if ( face > 2.0 ) {
uv.y += faceSize;
face -= 3.0;
}
uv.x += face * faceSize;
uv.x += filterInt * 3.0 * cubeUV_minTileSize;
uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );
uv.x *= CUBEUV_TEXEL_WIDTH;
uv.y *= CUBEUV_TEXEL_HEIGHT;
#ifdef texture2DGradEXT
return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; // disable anisotropic filtering
#else
return texture2D( envMap, uv ).rgb;
#endif
}
// These defines must match with PMREMGenerator
#define cubeUV_r0 1.0
#define cubeUV_m0 - 2.0
#define cubeUV_r1 0.8
#define cubeUV_m1 - 1.0
#define cubeUV_r4 0.4
#define cubeUV_m4 2.0
#define cubeUV_r5 0.305
#define cubeUV_m5 3.0
#define cubeUV_r6 0.21
#define cubeUV_m6 4.0
float roughnessToMip( float roughness ) {
float mip = 0.0;
if ( roughness >= cubeUV_r1 ) {
mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;
} else if ( roughness >= cubeUV_r4 ) {
mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;
} else if ( roughness >= cubeUV_r5 ) {
mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;
} else if ( roughness >= cubeUV_r6 ) {
mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;
} else {
mip = - 2.0 * log2( 1.16 * roughness ); // 1.16 = 1.79^0.25
}
return mip;
}
vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {
float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );
float mipF = fract( mip );
float mipInt = floor( mip );
vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );
if ( mipF == 0.0 ) {
return vec4( color0, 1.0 );
} else {
vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );
return vec4( mix( color0, color1, mipF ), 1.0 );
}
}
#endif
`;
@@ -0,0 +1,5 @@
export default /* glsl */`
void main() {
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
`;
@@ -0,0 +1,5 @@
export default /* glsl */`
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
@@ -0,0 +1,63 @@
export default /* glsl */`
vec3 transformedNormal = objectNormal;
#ifdef USE_TANGENT
vec3 transformedTangent = objectTangent;
#endif
#ifdef USE_BATCHING
// this is in lieu of a per-instance normal-matrix
// shear transforms in the instance matrix are not supported
mat3 bm = mat3( batchingMatrix );
transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) );
transformedNormal = bm * transformedNormal;
#ifdef USE_TANGENT
transformedTangent = bm * transformedTangent;
#endif
#endif
#ifdef USE_INSTANCING
// this is in lieu of a per-instance normal-matrix
// shear transforms in the instance matrix are not supported
mat3 im = mat3( instanceMatrix );
transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) );
transformedNormal = im * transformedNormal;
#ifdef USE_TANGENT
transformedTangent = im * transformedTangent;
#endif
#endif
transformedNormal = normalMatrix * transformedNormal;
#ifdef FLIP_SIDED
transformedNormal = - transformedNormal;
#endif
#ifdef USE_TANGENT
transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz;
#ifdef FLIP_SIDED
transformedTangent = - transformedTangent;
#endif
#endif
`;
@@ -0,0 +1,9 @@
export default /* glsl */`
#ifdef USE_DISPLACEMENTMAP
uniform sampler2D displacementMap;
uniform float displacementScale;
uniform float displacementBias;
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef USE_DISPLACEMENTMAP
transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias );
#endif
`;
@@ -0,0 +1,7 @@
export default /* glsl */`
#ifdef DITHERING
gl_FragColor.rgb = dithering( gl_FragColor.rgb );
#endif
`;
@@ -0,0 +1,20 @@
export default /* glsl */`
#ifdef DITHERING
// based on https://www.shadertoy.com/view/MslGR8
vec3 dithering( vec3 color ) {
//Calculate grid position
float grid_position = rand( gl_FragCoord.xy );
//Shift the individual colors differently, thus making it even harder to see the dithering pattern
vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );
//modify shift according to grid position.
dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );
//shift the color by dither_shift
return color + dither_shift_RGB;
}
#endif
`;
@@ -0,0 +1,17 @@
export default /* glsl */`
#ifdef USE_EMISSIVEMAP
vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv );
#ifdef DECODE_VIDEO_TEXTURE_EMISSIVE
// use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)
emissiveColor = sRGBTransferEOTF( emissiveColor );
#endif
totalEmissiveRadiance *= emissiveColor.rgb;
#endif
`;

Some files were not shown because too many files have changed in this diff Show More