From 9a5bc75e4e5c0a050d31b6a3b29aef4dca3a0590 Mon Sep 17 00:00:00 2001 From: Don McCurdy Date: Wed, 6 Sep 2023 12:14:48 -0400 Subject: [PATCH] Improve TextureHelper, show slices. --- examples/jsm/helpers/TextureHelper.js | 135 ++++++++++++++++++++++-- examples/jsm/loaders/KTX2Loader.js | 12 ++- examples/webgl_loader_texture_ktx2.html | 4 + src/textures/DataCubeTexture.js | 4 +- 4 files changed, 141 insertions(+), 14 deletions(-) diff --git a/examples/jsm/helpers/TextureHelper.js b/examples/jsm/helpers/TextureHelper.js index 0e9e3e3c591908..1e7e2859454ab6 100644 --- a/examples/jsm/helpers/TextureHelper.js +++ b/examples/jsm/helpers/TextureHelper.js @@ -1,8 +1,13 @@ import { + BoxGeometry, + BufferAttribute, + DoubleSide, Mesh, + PlaneGeometry, ShaderMaterial, - BoxGeometry + Vector3, } from 'three'; +import { mergeGeometries } from '../utils/BufferGeometryUtils.js'; class TextureHelper extends Mesh { @@ -12,19 +17,25 @@ class TextureHelper extends Mesh { type: 'TextureHelperMaterial', + side: DoubleSide, + transparent: true, + uniforms: { map: { value: texture }, + alpha: { value: getAlpha( texture ) }, }, vertexShader: [ + 'attribute vec3 uvw;', + 'varying vec3 vUvw;', 'void main() {', - ' vUvw = vec3( uv, 0.0 );', // TODO: Populate 'w' for cube, 3D, and array textures. + ' vUvw = uvw;', ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', @@ -42,6 +53,8 @@ class TextureHelper extends Mesh { 'uniform {samplerType} map;', + 'uniform float alpha;', + 'varying vec3 vUvw;', 'vec4 textureHelper( in sampler2D map ) { return texture( map, vUvw.xy ); }', @@ -54,7 +67,7 @@ class TextureHelper extends Mesh { 'void main() {', - ' gl_FragColor = linearToOutputTexel( textureHelper( map ) );', + ' gl_FragColor = linearToOutputTexel( vec4( textureHelper( map ).xyz, alpha ) );', '}' @@ -62,9 +75,9 @@ class TextureHelper extends Mesh { } ); - // TODO: Display as stack of images. - const geometry = new BoxGeometry( width, height, depth ); - if ( texture.flipY === false ) flipY( geometry ); + const geometry = texture.isCubeTexture + ? createCubeGeometry( width, height, depth ) + : createSliceGeometry( texture, width, height, depth ); super( geometry, material ); @@ -104,19 +117,121 @@ function getSamplerType( texture ) { } -/** Correct UVs to be compatible with `flipY=false` textures. */ -function flipY( geometry ) { +function getImageCount( texture ) { + + if ( texture.isCubeTexture ) { + + return 6; + + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + + return texture.image.depth; + + } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) { + + return texture.image.depth; + + } else { + + return 1; + + } + +} + +function getAlpha( texture ) { + + if ( texture.isCubeTexture ) { + + return 1; + + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { + + return Math.max( 1 / texture.image.depth, 0.25 ); + + } else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) { + + return Math.max( 1 / texture.image.depth, 0.25 ); + + } else { + + return 1; + + } + +} + +function createCubeGeometry( width, height, depth ) { + const geometry = new BoxGeometry( width, height, depth ); + + const position = geometry.attributes.position; const uv = geometry.attributes.uv; + const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 ); + + const _direction = new Vector3(); - for ( let i = 0; i < uv.count; i ++ ) { + for ( let j = 0, jl = uv.count; j < jl; ++ j ) { - uv.setY( i, 1 - uv.getY( i ) ); + _direction.fromBufferAttribute( position, j ).normalize(); + + const u = _direction.x; + const v = _direction.y; + const w = _direction.z; + + uvw.setXYZ( j, u, v, w ); } + geometry.deleteAttribute( 'uv' ); + geometry.setAttribute( 'uvw', uvw ); + return geometry; } +function createSliceGeometry( texture, width, height, depth ) { + + const sliceCount = getImageCount( texture ); + + const geometries = []; + + for ( let i = 0; i < sliceCount; ++ i ) { + + const geometry = new PlaneGeometry( width, height ); + + if ( sliceCount > 1 ) { + + geometry.translate( 0, 0, depth * ( i / ( sliceCount - 1 ) - 0.5 ) ); + + } + + const uv = geometry.attributes.uv; + const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 ); + + for ( let j = 0, jl = uv.count; j < jl; ++ j ) { + + const u = uv.getX( j ); + const v = texture.flipY ? uv.getY( j ) : 1 - uv.getY( j ); + const w = sliceCount === 1 + ? 1 + : texture.isDataArrayTexture || texture.isCompressedArrayTexture + ? i + : i / ( sliceCount - 1 ); + + uvw.setXYZ( j, u, v, w ); + + } + + geometry.deleteAttribute( 'uv' ); + geometry.setAttribute( 'uvw', uvw ); + + geometries.push( geometry ); + + } + + return mergeGeometries( geometries ); + +} + export { TextureHelper }; diff --git a/examples/jsm/loaders/KTX2Loader.js b/examples/jsm/loaders/KTX2Loader.js index 9d1eaf6f2f9bd5..b44d81b2d85ef4 100644 --- a/examples/jsm/loaders/KTX2Loader.js +++ b/examples/jsm/loaders/KTX2Loader.js @@ -867,7 +867,7 @@ async function createRawTexture( container ) { if ( container.faceCount === 6 ) { - texture = new DataCubeTexture( mipmaps ); + texture = new DataCubeTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight ); } else if ( container.layerCount > 1 ) { @@ -905,7 +905,15 @@ async function createRawTexture( container ) { } - texture.mipmaps = mipmaps; + if ( texture.isDataCubeTexture ) { + + texture.mipmaps = mipmaps.map( ( mip ) => new DataCubeTexture( mip.data, mip.width, mip.height ) ); + + } else { + + texture.mipmaps = mipmaps; + + } texture.type = TYPE_MAP[ vkFormat ]; texture.format = FORMAT_MAP[ vkFormat ]; diff --git a/examples/webgl_loader_texture_ktx2.html b/examples/webgl_loader_texture_ktx2.html index 6531ccdaa21904..5c7aa837395d1f 100644 --- a/examples/webgl_loader_texture_ktx2.html +++ b/examples/webgl_loader_texture_ktx2.html @@ -59,12 +59,16 @@ '3D / RGBA32 LUT': '3d_rgba32_lut.ktx2', // Cube + 'Cube / BasisU ETC1S': 'cubemap_etc1s.ktx2', + 'Cube / BasisU UASTC': 'cubemap_uastc.ktx2', 'Cube / RGBA8 sRGB': 'cubemap_rgba8.ktx2', 'Cube / RGBA8 Linear': 'cubemap_rgba8_linear.ktx2', 'Cube / RGBA16 Linear': 'cubemap_rgba16_linear.ktx2', 'Cube / RGBA32 Linear': 'cubemap_rgba32_linear.ktx2', // Array + 'Array / BasisU ETC1S': 'array_etc1s.ktx2', + 'Array / BasisU UASTC': 'array_uastc.ktx2', 'Array / RGBA8 sRGB': 'array_rgba8.ktx2', 'Array / RGBA8 Linear': 'array_rgba8_linear.ktx2', 'Array / RGBA16 Linear': 'array_rgba16_linear.ktx2', diff --git a/src/textures/DataCubeTexture.js b/src/textures/DataCubeTexture.js index 95ddda4b461e92..c4d221878a2caa 100644 --- a/src/textures/DataCubeTexture.js +++ b/src/textures/DataCubeTexture.js @@ -3,14 +3,14 @@ import { CubeReflectionMapping } from '../constants.js'; class DataCubeTexture extends Texture { - constructor( data ) { + constructor( data, width, height ) { super( data, CubeReflectionMapping ); this.isDataCubeTexture = true; this.isCubeTexture = true; - this.image = { data, width: data[ 0 ].width, height: data[ 0 ].height }; + this.image = { data: data, width: width, height: height }; this.generateMipmaps = false; this.flipY = false;