Bläddra i källkod

DDSLoader: Add support for BC6H textures. (#26608)

* Add support for DX10 BC6H textures to DDSLoader

* Add DX10 BC6H related unit tests for constants

* Update DDS example to demonstrate BC6H variants

* Update screenshot and commit build so e2e test passes

* Update DDSLoader.js

Code style clean up.

* Revert adding build files

---------

Co-authored-by: Michael Herzog <[email protected]>
Cory Gross 1 år sedan
förälder
incheckning
f53bb7dd0e

+ 46 - 3
examples/jsm/loaders/DDSLoader.js

@@ -4,7 +4,9 @@ import {
 	RGBA_S3TC_DXT3_Format,
 	RGBA_S3TC_DXT5_Format,
 	RGB_ETC1_Format,
-	RGB_S3TC_DXT1_Format
+	RGB_S3TC_DXT1_Format,
+	RGB_BPTC_SIGNED_Format,
+	RGB_BPTC_UNSIGNED_Format
 } from 'three';
 
 class DDSLoader extends CompressedTextureLoader {
@@ -56,6 +58,9 @@ class DDSLoader extends CompressedTextureLoader {
 		// const DDPF_YUV = 0x200;
 		// const DDPF_LUMINANCE = 0x20000;
 
+		const DXGI_FORMAT_BC6H_UF16 = 95;
+		const DXGI_FORMAT_BC6H_SF16 = 96;
+
 		function fourCCToInt32( value ) {
 
 			return value.charCodeAt( 0 ) +
@@ -108,8 +113,10 @@ class DDSLoader extends CompressedTextureLoader {
 		const FOURCC_DXT3 = fourCCToInt32( 'DXT3' );
 		const FOURCC_DXT5 = fourCCToInt32( 'DXT5' );
 		const FOURCC_ETC1 = fourCCToInt32( 'ETC1' );
+		const FOURCC_DX10 = fourCCToInt32( 'DX10' );
 
 		const headerLengthInt = 31; // The header length in 32 bit ints
+		const extendedHeaderLengthInt = 5; // The extended header length in 32 bit ints
 
 		// Offsets into the header array
 
@@ -135,6 +142,9 @@ class DDSLoader extends CompressedTextureLoader {
 		// const off_caps3 = 29;
 		// const off_caps4 = 30;
 
+		// If fourCC = DX10, the extended header starts after 32
+		const off_dxgiFormat = 0;
+
 		// Parse header
 
 		const header = new Int32Array( buffer, 0, headerLengthInt );
@@ -152,6 +162,8 @@ class DDSLoader extends CompressedTextureLoader {
 
 		let isRGBAUncompressed = false;
 
+		let dataOffset = header[ off_size ] + 4;
+
 		switch ( fourCC ) {
 
 			case FOURCC_DXT1:
@@ -178,6 +190,39 @@ class DDSLoader extends CompressedTextureLoader {
 				dds.format = RGB_ETC1_Format;
 				break;
 
+			case FOURCC_DX10:
+
+				dataOffset += extendedHeaderLengthInt * 4;
+				const extendedHeader = new Int32Array( buffer, ( headerLengthInt + 1 ) * 4, extendedHeaderLengthInt );
+				const dxgiFormat = extendedHeader[ off_dxgiFormat ];
+				switch ( dxgiFormat ) {
+
+					case DXGI_FORMAT_BC6H_SF16: {
+
+						blockBytes = 16;
+						dds.format = RGB_BPTC_SIGNED_Format;
+						break;
+
+					}
+
+					case DXGI_FORMAT_BC6H_UF16: {
+
+						blockBytes = 16;
+						dds.format = RGB_BPTC_UNSIGNED_Format;
+						break;
+
+					}
+
+					default: {
+
+						console.error( 'THREE.DDSLoader.parse: Unsupported DXGI_FORMAT code ', dxgiFormat );
+						return dds;
+
+					}
+
+				}
+				break;
+
 			default:
 
 				if ( header[ off_RGBBitCount ] === 32
@@ -226,8 +271,6 @@ class DDSLoader extends CompressedTextureLoader {
 		dds.width = header[ off_width ];
 		dds.height = header[ off_height ];
 
-		let dataOffset = header[ off_size ] + 4;
-
 		// Extract mipmaps buffers
 
 		const faces = dds.isCubemap ? 6 : 1;

BIN
examples/screenshots/webgl_loader_texture_dds.jpg


BIN
examples/textures/compressed/disturb_dx10_bc6h_signed_mip.dds


BIN
examples/textures/compressed/disturb_dx10_bc6h_signed_nomip.dds


BIN
examples/textures/compressed/disturb_dx10_bc6h_unsigned_mip.dds


BIN
examples/textures/compressed/disturb_dx10_bc6h_unsigned_nomip.dds


+ 53 - 8
examples/webgl_loader_texture_dds.html

@@ -41,7 +41,7 @@
 			function init() {
 
 				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 );
-				camera.position.z = 10;
+				camera.position.z = 15;
 
 				scene = new THREE.Scene();
 
@@ -83,6 +83,23 @@
 				map6.anisotropy = 4;
 				map6.colorSpace = THREE.SRGBColorSpace;
 
+				const map7 = loader.load( 'textures/compressed/disturb_dx10_bc6h_signed_nomip.dds' );
+				map6.anisotropy = 4;
+				map6.colorSpace = THREE.SRGBColorSpace;
+
+				const map8 = loader.load( 'textures/compressed/disturb_dx10_bc6h_signed_mip.dds' );
+				map6.anisotropy = 4;
+				map6.colorSpace = THREE.SRGBColorSpace;
+
+				const map9 = loader.load( 'textures/compressed/disturb_dx10_bc6h_unsigned_nomip.dds' );
+				map6.anisotropy = 4;
+				map6.colorSpace = THREE.SRGBColorSpace;
+
+				const map10 = loader.load( 'textures/compressed/disturb_dx10_bc6h_unsigned_mip.dds' );
+				map6.anisotropy = 4;
+				map6.colorSpace = THREE.SRGBColorSpace;
+
+
 				const cubemap1 = loader.load( 'textures/compressed/Mountains.dds', function ( texture ) {
 
 					texture.magFilter = THREE.LinearFilter;
@@ -121,51 +138,79 @@
 				const material6 = new THREE.MeshBasicMaterial( { envMap: cubemap3 } );
 				const material7 = new THREE.MeshBasicMaterial( { map: map5 } );
 				const material8 = new THREE.MeshBasicMaterial( { map: map6 } );
+				const material9 = new THREE.MeshBasicMaterial( { map: map7 } );
+				const material10 = new THREE.MeshBasicMaterial( { map: map8 } );
+				const material11 = new THREE.MeshBasicMaterial( { map: map9 } );
+				const material12 = new THREE.MeshBasicMaterial( { map: map10 } );
 
 				let mesh = new THREE.Mesh( new THREE.TorusGeometry(), material1 );
-				mesh.position.x = - 6;
+				mesh.position.x = - 10;
 				mesh.position.y = - 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material2 );
-				mesh.position.x = - 2;
+				mesh.position.x = - 6;
 				mesh.position.y = - 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material3 );
-				mesh.position.x = - 2;
+				mesh.position.x = - 6;
 				mesh.position.y = 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material4 );
-				mesh.position.x = - 6;
+				mesh.position.x = - 10;
 				mesh.position.y = 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material5 );
-				mesh.position.x = 2;
+				mesh.position.x = -2;
 				mesh.position.y = 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material6 );
-				mesh.position.x = 2;
+				mesh.position.x = -2;
 				mesh.position.y = - 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material7 );
-				mesh.position.x = 6;
+				mesh.position.x = 2;
 				mesh.position.y = - 2;
 				scene.add( mesh );
 				meshes.push( mesh );
 
 				mesh = new THREE.Mesh( geometry, material8 );
+				mesh.position.x = 2;
+				mesh.position.y = 2;
+				scene.add( mesh );
+				meshes.push( mesh );
+
+				mesh = new THREE.Mesh( geometry, material9 );
 				mesh.position.x = 6;
+				mesh.position.y = - 2;
+				scene.add( mesh );
+				meshes.push( mesh );
+
+				mesh = new THREE.Mesh( geometry, material10 );
+				mesh.position.x = 6;
+				mesh.position.y = 2;
+				scene.add( mesh );
+				meshes.push( mesh );
+
+				mesh = new THREE.Mesh( geometry, material11 );
+				mesh.position.x = 10;
+				mesh.position.y = - 2;
+				scene.add( mesh );
+				meshes.push( mesh );
+
+				mesh = new THREE.Mesh( geometry, material12 );
+				mesh.position.x = 10;
 				mesh.position.y = 2;
 				scene.add( mesh );
 				meshes.push( mesh );

+ 3 - 0
src/constants.js

@@ -122,6 +122,9 @@ export const RGBA_ASTC_10x10_Format = 37819;
 export const RGBA_ASTC_12x10_Format = 37820;
 export const RGBA_ASTC_12x12_Format = 37821;
 export const RGBA_BPTC_Format = 36492;
+export const SRGB_ALPHA_BPTC_Format = 36493;
+export const RGB_BPTC_SIGNED_Format = 36494;
+export const RGB_BPTC_UNSIGNED_Format = 36495;
 export const RED_RGTC1_Format = 36283;
 export const SIGNED_RED_RGTC1_Format = 36284;
 export const RED_GREEN_RGTC2_Format = 36285;

+ 5 - 3
src/renderers/webgl/WebGLUtils.js

@@ -1,4 +1,4 @@
-import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, _SRGBAFormat, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, SRGBColorSpace, NoColorSpace } from '../../constants.js';
+import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, SRGB_ALPHA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, _SRGBAFormat, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, SRGBColorSpace, NoColorSpace } from '../../constants.js';
 
 function WebGLUtils( gl, extensions, capabilities ) {
 
@@ -207,13 +207,15 @@ function WebGLUtils( gl, extensions, capabilities ) {
 
 		// BPTC
 
-		if ( p === RGBA_BPTC_Format ) {
+		if ( p === RGBA_BPTC_Format || p === SRGB_ALPHA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) {
 
 			extension = extensions.get( 'EXT_texture_compression_bptc' );
 
 			if ( extension !== null ) {
 
-				if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
+				if ( p === RGBA_BPTC_Format || p === SRGB_ALPHA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT;
+				if ( p === RGB_BPTC_SIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;
+				if ( p === RGB_BPTC_UNSIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT;
 
 			} else {
 

+ 3 - 0
test/unit/src/constants.tests.js

@@ -135,6 +135,9 @@ export default QUnit.module( 'Constants', () => {
 		assert.equal( Constants.RGBA_ASTC_12x10_Format, 37820, 'Constants.RGBA_ASTC_12x10_Format is equal to 37820' );
 		assert.equal( Constants.RGBA_ASTC_12x12_Format, 37821, 'Constants.RGBA_ASTC_12x12_Format is equal to 37821' );
 		assert.equal( Constants.RGBA_BPTC_Format, 36492, 'Constants.RGBA_BPTC_Format is equal to 36492' );
+		assert.equal( Constants.SRGB_ALPHA_BPTC_Format, 36493, 'Constants.SRGB_ALPHA_BPTC_Format is equal to 36493' );
+		assert.equal( Constants.RGB_BPTC_SIGNED_Format, 36494, 'Constants.RGB_BPTC_SIGNED_Format is equal to 36494' );
+		assert.equal( Constants.RGB_BPTC_UNSIGNED_Format, 36495, 'Constants.RGB_BPTC_UNSIGNED_Format is equal to 36495' );
 		assert.equal( Constants.RED_RGTC1_Format, 36283, 'Constants.RED_RGTC1_Format is equal to 36283' );
 		assert.equal( Constants.SIGNED_RED_RGTC1_Format, 36284, 'Constants.SIGNED_RED_RGTC1_Format is equal to 36284' );
 		assert.equal( Constants.RED_GREEN_RGTC2_Format, 36285, 'Constants.RED_GREEN_RGTC2_Format is equal to 36285' );