瀏覽代碼

Backend.generateMipmaps() (#26609)

* Backend.generateMipmaps()

* cleanup

* cleanup
sunag 1 年之前
父節點
當前提交
288d41156c

+ 113 - 8
examples/jsm/renderers/common/Textures.js

@@ -1,7 +1,8 @@
 import DataMap from './DataMap.js';
-import { Vector2, DepthTexture, DepthStencilFormat, UnsignedInt248Type } from 'three';
 
-const _size = new Vector2();
+import { Vector3, DepthTexture, DepthStencilFormat, UnsignedInt248Type, LinearFilter, NearestFilter, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeReflectionMapping, CubeRefractionMapping } from 'three';
+
+const _size = new Vector3();
 
 class Textures extends DataMap {
 
@@ -105,6 +106,16 @@ class Textures extends DataMap {
 
 		//
 
+		const { width, height, depth } = this.getSize( texture );
+
+		options.width = width;
+		options.height = height;
+		options.depth = depth;
+		options.needsMipmaps = this.needsMipmaps( texture );
+		options.levels = this.getMipLevels( texture, width, height, options.needsMipmaps );
+
+		//
+
 		if ( isRenderTarget ) {
 
 			backend.createSampler( texture );
@@ -130,6 +141,24 @@ class Textures extends DataMap {
 
 				} else {
 
+					if ( texture.images ) {
+
+						const images = [];
+
+						for ( const image of texture.images ) {
+
+							images.push( this._getUploadImage( image ) );
+
+						}
+
+						options.images = images;
+
+					} else {
+
+						options.image = this._getUploadImage( image );
+
+					}
+
 					if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) {
 
 						backend.createTexture( texture, options );
@@ -138,7 +167,9 @@ class Textures extends DataMap {
 
 					}
 
-					backend.updateTexture( texture );
+					backend.updateTexture( texture, options );
+
+					if ( options.needsMipmaps ) backend.generateMipmaps( texture );
 
 				}
 
@@ -188,15 +219,19 @@ class Textures extends DataMap {
 
 	getSize( texture, target = _size ) {
 
-		if ( texture.isCubeTexture ) {
+		let image = texture.images ? texture.images[ 0 ] : texture.image;
 
-			target.width = texture.image[ 0 ].width;
-			target.height = texture.image[ 0 ].height;
+		if ( image ) {
+
+			if ( image.image !== undefined ) image = image.image;
+
+			target.width = image.width;
+			target.height = image.height;
+			target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 );
 
 		} else {
 
-			target.width = texture.image.width;
-			target.height = texture.image.height;
+			target.width = target.height = target.depth = 1;
 
 		}
 
@@ -204,6 +239,76 @@ class Textures extends DataMap {
 
 	}
 
+	getMipLevels( texture, width, height, needsMipmaps ) {
+
+		let mipLevelCount;
+
+		if ( texture.isCompressedTexture ) {
+
+			mipLevelCount = texture.mipmaps.length;
+
+		} else if ( needsMipmaps ) {
+
+			mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
+
+		} else {
+
+			mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
+
+		}
+
+		return mipLevelCount;
+
+	}
+
+	needsMipmaps( texture ) {
+
+		if ( this.isEnvironmentTexture( texture ) ) return true;
+
+		return ( texture.isCompressedTexture !== true ) /*&& ( texture.generateMipmaps === true )*/ && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
+
+	}
+
+	isEnvironmentTexture( texture ) {
+
+		const mapping = texture.mapping;
+
+		return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping );
+
+	}
+
+	_getUploadImage( image ) {
+
+		if ( this._isHTMLImage( image ) ) {
+
+			return this._imageToCanvas( image );
+
+		}
+
+		return image;
+
+	}
+
+	_imageToCanvas( image ) {
+
+		const { width, height } = image;
+
+		// eslint-disable-next-line compat/compat
+		const canvas = new OffscreenCanvas( width, height );
+
+		const context = canvas.getContext( '2d' );
+		context.drawImage( image, 0, 0, width, height );
+
+		return canvas;
+
+	}
+
+	_isHTMLImage( image ) {
+
+		return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
+
+	}
+
 	_destroyTexture( texture ) {
 
 		this.backend.destroySampler( texture );

+ 9 - 3
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -742,9 +742,15 @@ class WebGPUBackend extends Backend {
 
 	}
 
-	updateTexture( texture ) {
+	updateTexture( texture, options ) {
 
-		this.textureUtils.updateTexture( texture );
+		this.textureUtils.updateTexture( texture, options );
+
+	}
+
+	generateMipmaps( texture ) {
+
+		this.textureUtils.generateMipmaps( texture );
 
 	}
 
@@ -972,7 +978,7 @@ class WebGPUBackend extends Backend {
 		depthTexture.image.width = width;
 		depthTexture.image.height = height;
 
-		this.textureUtils.createTexture( depthTexture, { sampleCount: this.parameters.sampleCount } );
+		this.textureUtils.createTexture( depthTexture, { sampleCount: this.parameters.sampleCount, width, height } );
 
 		return this.get( depthTexture ).texture;
 

+ 20 - 135
examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -4,7 +4,7 @@ import {
 
 import {
 	CubeTexture, Texture,
-	NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter,
+	NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter,
 	RepeatWrapping, MirroredRepeatWrapping,
 	RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
 	RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat,
@@ -97,11 +97,13 @@ class WebGPUTextureUtils {
 
 		}
 
-		const { width, height, depth } = this._getSize( texture );
+		if ( options.needsMipmaps === undefined ) options.needsMipmaps = false;
+		if ( options.levels === undefined ) options.levels = 1;
+		if ( options.depth === undefined ) options.depth = 1;
+
+		const { width, height, depth, levels } = options;
 
-		const needsMipmaps = this._needsMipmaps( texture );
 		const dimension = this._getDimension( texture );
-		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
 		const format = texture.internalFormat || this._getFormat( texture );
 
 		const sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
@@ -122,7 +124,7 @@ class WebGPUTextureUtils {
 				height: height,
 				depthOrArrayLayers: depth,
 			},
-			mipLevelCount: mipLevelCount,
+			mipLevelCount: levels,
 			sampleCount: primarySampleCount,
 			dimension: dimension,
 			format: format,
@@ -170,7 +172,6 @@ class WebGPUTextureUtils {
 
 		textureData.initialized = true;
 
-		textureData.needsMipmaps = needsMipmaps;
 		textureData.textureDescriptorGPU = textureDescriptorGPU;
 
 	}
@@ -217,20 +218,20 @@ class WebGPUTextureUtils {
 
 	}
 
-	updateTexture( texture ) {
+	updateTexture( texture, options ) {
 
 		const textureData = this.backend.get( texture );
 
-		const { needsMipmaps, textureDescriptorGPU } = textureData;
+		const { textureDescriptorGPU } = textureData;
 
-		if ( textureDescriptorGPU === undefined ) // unsupported texture format
+		if ( texture.isRenderTargetTexture || ( textureDescriptorGPU === undefined /* unsupported texture format */ ) )
 			return;
 
 		// transfer texture data
 
 		if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
 
-			this._copyBufferToTexture( texture.image, textureData.texture, textureDescriptorGPU, needsMipmaps );
+			this._copyBufferToTexture( options.image, textureData.texture, textureDescriptorGPU );
 
 		} else if ( texture.isCompressedTexture ) {
 
@@ -238,15 +239,7 @@ class WebGPUTextureUtils {
 
 		} else if ( texture.isCubeTexture ) {
 
-			if ( texture.image.length === 6 ) {
-
-				this._copyCubeMapToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
-
-			}
-
-		} else if ( texture.isRenderTargetTexture ) {
-
-			if ( needsMipmaps === true ) this._generateMipmaps( textureData.texture, textureDescriptorGPU );
+			this._copyCubeMapToTexture( options.images, texture, textureData.texture, textureDescriptorGPU );
 
 		} else if ( texture.isVideoTexture ) {
 
@@ -254,13 +247,9 @@ class WebGPUTextureUtils {
 
 			textureData.externalTexture = video;
 
-		} else if ( texture.image !== null ) {
-
-			this._copyImageToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
-
 		} else {
 
-			console.warn( 'WebGPUTextureUtils: Unable to update texture.' );
+			this._copyImageToTexture( options.image, textureData.texture );
 
 		}
 
@@ -336,7 +325,7 @@ class WebGPUTextureUtils {
 			texture.minFilter = NearestFilter;
 			texture.magFilter = NearestFilter;
 
-			this.createTexture( texture );
+			this.createTexture( texture, { width: 1, height: 1 } );
 
 			this.defaultTexture = defaultTexture = texture;
 
@@ -356,7 +345,7 @@ class WebGPUTextureUtils {
 			texture.minFilter = NearestFilter;
 			texture.magFilter = NearestFilter;
 
-			this.createTexture( texture );
+			this.createTexture( texture, { width: 1, height: 1, depth: 6 } );
 
 			this.defaultCubeTexture = defaultCubeTexture = texture;
 
@@ -366,33 +355,7 @@ class WebGPUTextureUtils {
 
 	}
 
-	_copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) {
-
-		if ( this._isHTMLImage( image ) ) {
-
-			this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => {
-
-				this._copyExternalImageToTexture( imageBitmap, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
-
-			} );
-
-		} else {
-
-			// assume ImageBitmap
-
-			this._copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
-
-		}
-
-	}
-
-	_isHTMLImage( image ) {
-
-		return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
-
-	}
-
-	_copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) {
+	_copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU ) {
 
 		for ( let i = 0; i < 6; i ++ ) {
 
@@ -400,11 +363,11 @@ class WebGPUTextureUtils {
 
 			if ( image.isDataTexture ) {
 
-				this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, needsMipmaps, i );
+				this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, i );
 
 			} else {
 
-				this._copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, i );
+				this._copyImageToTexture( image, textureGPU, i );
 
 			}
 
@@ -412,7 +375,7 @@ class WebGPUTextureUtils {
 
 	}
 
-	_copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
+	_copyImageToTexture( image, textureGPU, originDepth = 0 ) {
 
 		const device = this.backend.device;
 
@@ -430,8 +393,6 @@ class WebGPUTextureUtils {
 			}
 		);
 
-		if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
-
 	}
 
 	_generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer = 0 ) {
@@ -446,21 +407,7 @@ class WebGPUTextureUtils {
 
 	}
 
-	_getImageBitmapFromHTML( image, texture ) {
-
-		const width = image.width;
-		const height = image.height;
-
-		const options = {};
-
-		options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
-		options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
-
-		return createImageBitmap( image, 0, 0, width, height, options );
-
-	}
-
-	_copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
+	_copyBufferToTexture( image, textureGPU, textureDescriptorGPU, originDepth = 0 ) {
 
 		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 		// @TODO: Consider to support valid buffer layouts with other formats like RGB
@@ -489,8 +436,6 @@ class WebGPUTextureUtils {
 				depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
 			} );
 
-		if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
-
 	}
 
 	_copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) {
@@ -600,44 +545,6 @@ class WebGPUTextureUtils {
 
 	}
 
-	_getSize( texture ) {
-
-		const image = texture.image;
-
-		let width, height, depth;
-
-		if ( texture.isCubeTexture ) {
-
-			const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null;
-
-			width = faceImage ? faceImage.width : 1;
-			height = faceImage ? faceImage.height : 1;
-			depth = 6; // one image for each side of the cube map
-
-		} else if ( image !== null ) {
-
-			width = image.width;
-			height = image.height;
-			depth = ( image.depth !== undefined ) ? image.depth : 1;
-
-		} else {
-
-			width = height = depth = 1;
-
-		}
-
-		return { width, height, depth };
-
-	}
-
-	_needsMipmaps( texture ) {
-
-		if ( this._isEnvironmentTexture( texture ) ) return true;
-
-		return ( texture.isCompressedTexture !== true ) /*&& ( texture.generateMipmaps === true )*/ && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
-
-	}
-
 	_getBytesPerTexel( format ) {
 
 		if ( format === GPUTextureFormat.R8Unorm ) return 1;
@@ -706,28 +613,6 @@ class WebGPUTextureUtils {
 
 	}
 
-	_getMipLevelCount( texture, width, height, needsMipmaps ) {
-
-		let mipLevelCount;
-
-		if ( texture.isCompressedTexture ) {
-
-			mipLevelCount = texture.mipmaps.length;
-
-		} else if ( needsMipmaps ) {
-
-			mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
-
-		} else {
-
-			mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
-
-		}
-
-		return mipLevelCount;
-
-	}
-
 	_getFormat( texture ) {
 
 		const format = texture.format;