Explorar o código

WebGPURenderer: Support Compressed Texture Array (#29231)

* WebGPURenderer: Support Compressed Texture Array

* small cleanup

* more cleanup
Renaud Rohlinger hai 1 ano
pai
achega
cde3cd25a1

+ 2 - 1
examples/files.json

@@ -426,7 +426,8 @@
 		"webgpu_volume_cloud",
 		"webgpu_volume_perlin",
 		"webgpu_water",
-		"webgpu_performance"
+		"webgpu_performance",
+		"webgpu_texture2darray_compressed"
 	],
 	"webaudio": [
 		"webaudio_orientation",

BIN=BIN
examples/screenshots/webgpu_texture2darray_compressed.jpg


+ 129 - 0
examples/webgpu_texture2darray_compressed.html

@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - 2D compressed texture array</title>
+		<meta charset="UTF-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - WebGPU - 2D Compressed Texture Array<br />
+			Loop from the movie Spirited away
+			by the <a href="https://www.ghibli.jp/" target="_blank" rel="noopener">Studio Ghibli</a><br />
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.webgpu.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { texture, uniform, uv } from 'three/tsl';
+
+			import Stats from 'three/addons/libs/stats.module.js';
+			import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
+
+			let camera, scene, mesh, renderer, stats, clock;
+
+			const depth = uniform( 0 );
+
+			const planeWidth = 50;
+			const planeHeight = 25;
+
+			let depthStep = 1;
+
+			init();
+
+			async function init() {
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
+				camera.position.z = 70;
+
+				scene = new THREE.Scene();
+
+				//
+				clock = new THREE.Clock();
+
+				renderer = new THREE.WebGPURenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				const ktx2Loader = new KTX2Loader();
+				ktx2Loader.setTranscoderPath( 'jsm/libs/basis/' );
+				await ktx2Loader.detectSupportAsync( renderer );
+
+				ktx2Loader.load( 'textures/spiritedaway.ktx2', function ( texturearray ) {
+
+					const material = new THREE.NodeMaterial();
+
+					material.colorNode = texture( texturearray, uv().flipY() ).depth( depth );
+					const geometry = new THREE.PlaneGeometry( planeWidth, planeHeight );
+
+					mesh = new THREE.Mesh( geometry, material );
+
+					scene.add( mesh );
+
+				} );
+
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				if ( mesh ) {
+
+					const delta = clock.getDelta() * 10;
+
+					depthStep += delta;
+
+					const value = depthStep % 5;
+
+					depth.value = value;
+
+				}
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 1 - 1
src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js

@@ -387,7 +387,7 @@ ${ flowData.code }
 
 					snippet = `sampler2DShadow ${ uniform.name };`;
 
-				} else if ( texture.isDataArrayTexture === true ) {
+				} else if ( texture.isDataArrayTexture === true || texture.isCompressedArrayTexture === true ) {
 
 					snippet = `${typePrefix}sampler2DArray ${ uniform.name };`;
 

+ 5 - 4
src/renderers/webgl-fallback/utils/WebGLTextureUtils.js

@@ -79,11 +79,11 @@ class WebGLTextureUtils {
 
 			glTextureType = gl.TEXTURE_CUBE_MAP;
 
-		} else if ( texture.isDataArrayTexture === true ) {
+		} else if ( texture.isDataArrayTexture === true || texture.isCompressedArrayTexture === true ) {
 
 			glTextureType = gl.TEXTURE_2D_ARRAY;
 
-		} else if ( texture.isData3DTexture === true ) {
+		} else if ( texture.isData3DTexture === true ) { // TODO: isCompressed3DTexture, wait for #26642
 
 			glTextureType = gl.TEXTURE_3D;
 
@@ -340,7 +340,7 @@ class WebGLTextureUtils {
 
 		this.setTextureParameters( glTextureType, texture );
 
-		if ( texture.isDataArrayTexture ) {
+		if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
 
 			gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth );
 
@@ -429,6 +429,7 @@ class WebGLTextureUtils {
 		if ( texture.isCompressedTexture ) {
 
 			const mipmaps = texture.mipmaps;
+			const image = options.image;
 
 			for ( let i = 0; i < mipmaps.length; i ++ ) {
 
@@ -436,7 +437,6 @@ class WebGLTextureUtils {
 
 				if ( texture.isCompressedArrayTexture ) {
 
-					const image = options.image;
 
 					if ( texture.format !== gl.RGBA ) {
 
@@ -473,6 +473,7 @@ class WebGLTextureUtils {
 
 			}
 
+
 		} else if ( texture.isCubeTexture ) {
 
 			const images = options.images;

+ 1 - 1
src/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -961,7 +961,7 @@ ${ flowData.code }
 
 					textureType = 'texture_cube<f32>';
 
-				} else if ( texture.isDataArrayTexture === true ) {
+				} else if ( texture.isDataArrayTexture === true || texture.isCompressedArrayTexture === true ) {
 
 					textureType = 'texture_2d_array<f32>';
 

+ 2 - 2
src/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -114,7 +114,7 @@ class WebGPUBindingUtils {
 
 					texture.viewDimension = GPUTextureViewDimension.Cube;
 
-				} else if ( binding.texture.isDataArrayTexture ) {
+				} else if ( binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
 
 					texture.viewDimension = GPUTextureViewDimension.TwoDArray;
 
@@ -244,7 +244,7 @@ class WebGPUBindingUtils {
 
 					dimensionViewGPU = GPUTextureViewDimension.ThreeD;
 
-				} else if ( binding.texture.isDataArrayTexture ) {
+				} else if ( binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
 
 					dimensionViewGPU = GPUTextureViewDimension.TwoDArray;
 

+ 28 - 19
src/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -136,7 +136,7 @@ class WebGPUTextureUtils {
 
 		}
 
-		if ( texture.isCompressedTexture !== true ) {
+		if ( texture.isCompressedTexture !== true && texture.isCompressedArrayTexture !== true ) {
 
 			usage |= GPUTextureUsage.RENDER_ATTACHMENT;
 
@@ -341,7 +341,7 @@ class WebGPUTextureUtils {
 
 			}
 
-		} else if ( texture.isCompressedTexture ) {
+		} else if ( texture.isCompressedTexture || texture.isCompressedArrayTexture ) {
 
 			this._copyCompressedBufferToTexture( texture.mipmaps, textureData.texture, textureDescriptorGPU );
 
@@ -605,6 +605,7 @@ class WebGPUTextureUtils {
 		const device = this.backend.device;
 
 		const blockData = this._getBlockData( textureDescriptorGPU.format );
+		const isTextureArray = textureDescriptorGPU.size.depthOrArrayLayers > 1;
 
 		for ( let i = 0; i < mipmaps.length; i ++ ) {
 
@@ -612,25 +613,33 @@ class WebGPUTextureUtils {
 
 			const width = mipmap.width;
 			const height = mipmap.height;
+			const depth = isTextureArray ? textureDescriptorGPU.size.depthOrArrayLayers : 1;
 
 			const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
+			const bytesPerImage = bytesPerRow * Math.ceil( height / blockData.height );
+
+			for ( let j = 0; j < depth; j ++ ) {
+
+				device.queue.writeTexture(
+					{
+						texture: textureGPU,
+						mipLevel: i,
+						origin: { x: 0, y: 0, z: j }
+					},
+					mipmap.data,
+					{
+						offset: j * bytesPerImage,
+						bytesPerRow,
+						rowsPerImage: Math.ceil( height / blockData.height )
+					},
+					{
+						width: Math.ceil( width / blockData.width ) * blockData.width,
+						height: Math.ceil( height / blockData.height ) * blockData.height,
+						depthOrArrayLayers: 1
+					}
+				);
 
-			device.queue.writeTexture(
-				{
-					texture: textureGPU,
-					mipLevel: i
-				},
-				mipmap.data,
-				{
-					offset: 0,
-					bytesPerRow
-				},
-				{
-					width: Math.ceil( width / blockData.width ) * blockData.width,
-					height: Math.ceil( height / blockData.width ) * blockData.width,
-					depthOrArrayLayers: 1
-				}
-			);
+			}
 
 		}
 
@@ -843,7 +852,7 @@ export function getFormat( texture, device = null ) {
 
 		formatGPU = GPUTextureFormat.BGRA8Unorm;
 
-	} else if ( texture.isCompressedTexture === true ) {
+	} else if ( texture.isCompressedTexture === true || texture.isCompressedArrayTexture === true ) {
 
 		switch ( format ) {
 

粤ICP备19079148号