diff --git a/examples/files.json b/examples/files.json index efd9ffe0b5d952..d03a2f0225f8f4 100644 --- a/examples/files.json +++ b/examples/files.json @@ -340,6 +340,7 @@ "webgpu_lights_phong", "webgpu_lights_selective", "webgpu_loader_gltf", + "webgpu_loader_gltf_compressed", "webgpu_materials", "webgpu_materials_video", "webgpu_particles", diff --git a/examples/jsm/nodes/core/AttributeNode.js b/examples/jsm/nodes/core/AttributeNode.js index 23b5b29f7ad861..b45513a22001f2 100644 --- a/examples/jsm/nodes/core/AttributeNode.js +++ b/examples/jsm/nodes/core/AttributeNode.js @@ -30,7 +30,7 @@ class AttributeNode extends Node { const attribute = builder.geometry.getAttribute( attributeName ); - nodeType = builder.getTypeFromLength( attribute.itemSize ); + nodeType = builder.getTypeFromAttribute( attribute ); } else { @@ -66,23 +66,26 @@ class AttributeNode extends Node { if ( geometryAttribute === true ) { - const nodeAttribute = builder.getAttribute( attributeName, nodeType ); + const attribute = builder.geometry.getAttribute( attributeName ); + const attributeType = builder.getTypeFromAttribute( attribute ); + + const nodeAttribute = builder.getAttribute( attributeName, attributeType ); if ( builder.isShaderStage( 'vertex' ) ) { - return nodeAttribute.name; + return builder.format( nodeAttribute.name, attributeType, nodeType ); } else { const nodeVarying = varying( this ); - return nodeVarying.build( builder, nodeAttribute.type ); + return nodeVarying.build( builder, nodeType ); } } else { - console.warn( `Attribute "${ attributeName }" not found.` ); + console.warn( `AttributeNode: Attribute "${ attributeName }" not found.` ); return builder.getConst( nodeType ); diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 25395b1023917e..08c25aac8ee750 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -7,17 +7,28 @@ import NodeKeywords from './NodeKeywords.js'; import NodeCache from './NodeCache.js'; import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js'; -import { REVISION, NoColorSpace, LinearEncoding, sRGBEncoding, SRGBColorSpace, Color, Vector2, Vector3, Vector4 } from 'three'; +import { REVISION, NoColorSpace, LinearEncoding, sRGBEncoding, SRGBColorSpace, Color, Vector2, Vector3, Vector4, Float16BufferAttribute } from 'three'; import { stack } from './StackNode.js'; import { maxMipLevel } from '../utils/MaxMipLevelNode.js'; -const typeFromLength = new Map(); -typeFromLength.set( 2, 'vec2' ); -typeFromLength.set( 3, 'vec3' ); -typeFromLength.set( 4, 'vec4' ); -typeFromLength.set( 9, 'mat3' ); -typeFromLength.set( 16, 'mat4' ); +const typeFromLength = new Map( [ + [ 2, 'vec2' ], + [ 3, 'vec3' ], + [ 4, 'vec4' ], + [ 9, 'mat3' ], + [ 16, 'mat4' ] +] ); + +const typeFromArray = new Map( [ + [ Int8Array, 'int' ], + [ Int16Array, 'int' ], + [ Int32Array, 'int' ], + [ Uint8Array, 'uint' ], + [ Uint16Array, 'uint' ], + [ Uint32Array, 'uint' ], + [ Float32Array, 'float' ] +] ); const toFloat = ( value ) => { @@ -412,12 +423,42 @@ class NodeBuilder { getTypeFromLength( length, componentType = 'float' ) { if ( length === 1 ) return componentType; + const baseType = typeFromLength.get( length ); const prefix = componentType === 'float' ? '' : componentType[ 0 ]; + return prefix + baseType; } + getTypeFromArray( array ) { + + return typeFromArray.get( array.constructor ); + + } + + getTypeFromAttribute( attribute ) { + + let dataAttribute = attribute; + + if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data; + + const array = dataAttribute.array; + const itemSize = dataAttribute.stride || attribute.itemSize; + const normalized = attribute.normalized; + + let arrayType; + + if ( ! ( attribute instanceof Float16BufferAttribute ) && normalized !== true ) { + + arrayType = this.getTypeFromArray( array ); + + } + + return this.getTypeFromLength( itemSize, arrayType ); + + } + getTypeLength( type ) { const vecType = this.getVectorType( type ); @@ -677,7 +718,7 @@ class NodeBuilder { if ( propertyName !== null ) { - flowData.code += `${propertyName} = ${flowData.result};\n` + this.tab; + flowData.code += `${ this.tab + propertyName } = ${ flowData.result };\n`; } @@ -864,7 +905,7 @@ class NodeBuilder { if ( fromTypeLength > toTypeLength ) { - return this.format( `${ snippet }.${ 'xyz'.slice( 0, toTypeLength ) }`, this.getTypeFromLength( toTypeLength ), toType ); + return this.format( `${ snippet }.${ 'xyz'.slice( 0, toTypeLength ) }`, this.getTypeFromLength( toTypeLength, this.getComponentType( fromType ) ), toType ); } diff --git a/examples/jsm/nodes/lighting/EnvironmentNode.js b/examples/jsm/nodes/lighting/EnvironmentNode.js index 995e7a7d1ad9c8..ebc9d65e8f3056 100644 --- a/examples/jsm/nodes/lighting/EnvironmentNode.js +++ b/examples/jsm/nodes/lighting/EnvironmentNode.js @@ -53,7 +53,6 @@ class EnvironmentNode extends LightingNode { // @TODO: Needed PMREM radianceTextureUVNode = equirectUV( reflectVec ); - radianceTextureUVNode = vec2( radianceTextureUVNode.x, radianceTextureUVNode.y.oneMinus() ); } diff --git a/examples/jsm/renderers/webgpu/WebGPUAttributes.js b/examples/jsm/renderers/webgpu/WebGPUAttributes.js index 582ef12a5c4d6b..d047cdc9eb8899 100644 --- a/examples/jsm/renderers/webgpu/WebGPUAttributes.js +++ b/examples/jsm/renderers/webgpu/WebGPUAttributes.js @@ -9,7 +9,7 @@ class WebGPUAttributes { get( attribute ) { - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + attribute = this._getAttribute( attribute ); return this.buffers.get( attribute ); @@ -17,7 +17,7 @@ class WebGPUAttributes { remove( attribute ) { - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + attribute = this._getAttribute( attribute ); const data = this.buffers.get( attribute ); @@ -33,7 +33,7 @@ class WebGPUAttributes { update( attribute, isIndex = false, usage = null ) { - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + attribute = this._getAttribute( attribute ); let data = this.buffers.get( attribute ); @@ -115,6 +115,14 @@ class WebGPUAttributes { } + _getAttribute( attribute ) { + + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + + return attribute; + + } + _createBuffer( attribute, usage ) { const array = attribute.array; diff --git a/examples/jsm/renderers/webgpu/WebGPUBackground.js b/examples/jsm/renderers/webgpu/WebGPUBackground.js index cd86150933f982..226133717ea8a6 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackground.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackground.js @@ -69,13 +69,16 @@ class WebGPUBackground { nodeMaterial.side = BackSide; nodeMaterial.depthTest = false; nodeMaterial.depthWrite = false; + nodeMaterial.frustumCulled = false; nodeMaterial.fog = false; this.boxMesh = boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), nodeMaterial ); boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - this.matrixWorld.copyPosition( camera.matrixWorld ); + const scale = camera.far; + + this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld ); }; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js b/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js index 56ec0bf2863e25..1ffe32a25a2ea4 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderPipeline.js @@ -1,5 +1,6 @@ -import { GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUVertexFormat, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js'; +import { GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js'; import { + Float16BufferAttribute, FrontSide, BackSide, DoubleSide, NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc, @@ -9,6 +10,26 @@ import { ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor } from 'three'; +const typedArraysToVertexFormatPrefix = new Map( [ + [ Int8Array, [ 'sint8', 'snorm8' ]], + [ Uint8Array, [ 'uint8', 'unorm8' ]], + [ Int16Array, [ 'sint16', 'snorm16' ]], + [ Uint16Array, [ 'uint16', 'unorm16' ]], + [ Int32Array, [ 'sint32', 'snorm32' ]], + [ Uint32Array, [ 'uint32', 'unorm32' ]], + [ Float32Array, [ 'float32', ]], +] ); + +const typedAttributeToVertexFormatPrefix = new Map( [ + [ Float16BufferAttribute, [ 'float16', ]], +] ); + +const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ + [ Int32Array, 'sint32' ], + [ Uint32Array, 'uint32' ], + [ Float32Array, 'float32' ] +] ); + class WebGPURenderPipeline { constructor( device, utils ) { @@ -561,127 +582,48 @@ class WebGPURenderPipeline { } - _getVertexFormat( type, bytesPerElement ) { - - // float - - if ( type === 'float' ) return GPUVertexFormat.Float32; - - if ( type === 'vec2' ) { - - if ( bytesPerElement === 2 ) { - - return GPUVertexFormat.Float16x2; - - } else { - - return GPUVertexFormat.Float32x2; - - } - - } - - if ( type === 'vec3' ) return GPUVertexFormat.Float32x3; - - if ( type === 'vec4' ) { + _getVertexFormat( geometryAttribute ) { - if ( bytesPerElement === 2 ) { + const { itemSize, normalized } = geometryAttribute; + const ArrayType = geometryAttribute.array.constructor; + const AttributeType = geometryAttribute.constructor; - return GPUVertexFormat.Float16x4; + let format; - } else { + if ( itemSize == 1 ) { - return GPUVertexFormat.Float32x4; + format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType ); - } - - } - - // int - - if ( type === 'int' ) return GPUVertexFormat.Sint32; - - if ( type === 'ivec2' ) { - - if ( bytesPerElement === 1 ) { - - return GPUVertexFormat.Sint8x2; - - } else if ( bytesPerElement === 2 ) { - - return GPUVertexFormat.Sint16x2; - - } else { - - return GPUVertexFormat.Sint32x2; - - } - - } - - if ( type === 'ivec3' ) return GPUVertexFormat.Sint32x3; - - if ( type === 'ivec4' ) { - - if ( bytesPerElement === 1 ) { - - return GPUVertexFormat.Sint8x4; - - } else if ( bytesPerElement === 2 ) { - - return GPUVertexFormat.Sint16x4; - - } else { - - return GPUVertexFormat.Sint32x4; - - } - - } - - // uint - - if ( type === 'uint' ) return GPUVertexFormat.Uint32; + } else { - if ( type === 'uvec2' ) { + const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType ); + const prefix = prefixOptions[ normalized ? 1 : 0 ]; - if ( bytesPerElement === 1 ) { + if ( prefix ) { - return GPUVertexFormat.Uint8x2; + const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize; + const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4; + const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT; - } else if ( bytesPerElement === 2 ) { + if ( paddedItemSize % 1 ) { - return GPUVertexFormat.Uint16x2; + throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' ); - } else { + } - return GPUVertexFormat.Uint32x2; + format = `${prefix}x${paddedItemSize}`; } } - if ( type === 'uvec3' ) return GPUVertexFormat.Uint32x3; - - if ( type === 'uvec4' ) { - - if ( bytesPerElement === 1 ) { - - return GPUVertexFormat.Uint8x4; + if ( ! format ) { - } else if ( bytesPerElement === 2 ) { - - return GPUVertexFormat.Uint16x4; - - } else { - - return GPUVertexFormat.Uint32x4; - - } + console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' ); } - console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type ); + return format; } @@ -693,14 +635,12 @@ class WebGPURenderPipeline { for ( let slot = 0; slot < nodeAttributes.length; slot ++ ) { const nodeAttribute = nodeAttributes[ slot ]; - const name = nodeAttribute.name; - const type = nodeAttribute.type; const geometryAttribute = geometry.getAttribute( name ); const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; - const format = this._getVertexFormat( type, bytesPerElement ); + const format = this._getVertexFormat( geometryAttribute ); let arrayStride = geometryAttribute.itemSize * bytesPerElement; let offset = 0; diff --git a/examples/jsm/renderers/webgpu/WebGPURenderer.js b/examples/jsm/renderers/webgpu/WebGPURenderer.js index 031b3109b1817d..c2eff3e9272250 100644 --- a/examples/jsm/renderers/webgpu/WebGPURenderer.js +++ b/examples/jsm/renderers/webgpu/WebGPURenderer.js @@ -815,14 +815,19 @@ class WebGPURenderer { if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + const geometry = object.geometry; + const material = object.material; + if ( this.sortObjects === true ) { - _vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix ); + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - } + _vector3 + .copy( geometry.boundingSphere.center ) + .applyMatrix4( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - const geometry = object.geometry; - const material = object.material; + } if ( Array.isArray( material ) ) { diff --git a/examples/screenshots/webgpu_loader_gltf_compressed.jpg b/examples/screenshots/webgpu_loader_gltf_compressed.jpg new file mode 100644 index 00000000000000..001fe203e0b94e Binary files /dev/null and b/examples/screenshots/webgpu_loader_gltf_compressed.jpg differ diff --git a/examples/webgpu_backdrop.html b/examples/webgpu_backdrop.html index a9e146f6d1e174..54d35bb1eb842b 100644 --- a/examples/webgpu_backdrop.html +++ b/examples/webgpu_backdrop.html @@ -112,7 +112,7 @@ } - addBackdropSphere( viewportSharedTexture().bgr.hue( oscSine().mul( Math.PI ) ) ); + addBackdropSphere( viewportSharedTexture().bgr.hue( oscSine().mul( Math.PI ) ) ); addBackdropSphere( viewportSharedTexture().rgb.oneMinus() ); addBackdropSphere( viewportSharedTexture().rgb.saturation( 0 ) ); addBackdropSphere( viewportSharedTexture().rgb.saturation( 10 ), oscSine() ); diff --git a/examples/webgpu_loader_gltf_compressed.html b/examples/webgpu_loader_gltf_compressed.html new file mode 100644 index 00000000000000..698a8a25844e7d --- /dev/null +++ b/examples/webgpu_loader_gltf_compressed.html @@ -0,0 +1,121 @@ + + +
+