Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) KTX2Loader: Add support for u8, f16, and f32 array and cube textures #26642

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 44 additions & 7 deletions examples/jsm/loaders/KTX2Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
CompressedArrayTexture,
CompressedCubeTexture,
Data3DTexture,
DataArrayTexture,
DataCubeTexture,
DataTexture,
DisplayP3ColorSpace,
FileLoader,
Expand Down Expand Up @@ -796,7 +798,6 @@ async function createRawTexture( container ) {

const mipmaps = [];


for ( let levelIndex = 0; levelIndex < container.levels.length; levelIndex ++ ) {

const levelWidth = Math.max( 1, container.pixelWidth >> levelIndex );
Expand Down Expand Up @@ -864,19 +865,55 @@ async function createRawTexture( container ) {

if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {

texture = container.pixelDepth === 0
? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
: new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );
if ( container.faceCount === 6 ) {

texture = new DataCubeTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight );

} else if ( container.layerCount > 1 ) {

texture = new DataArrayTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.layerCount );

} else if ( container.pixelDepth === 0 ) {

texture = new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight );

} else {

texture = new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );

}

} else {

if ( container.pixelDepth > 0 ) throw new Error( 'THREE.KTX2Loader: Unsupported pixelDepth.' );
if ( container.faceCount === 6 ) {

texture = new CompressedCubeTexture( mipmaps );

texture = new CompressedTexture( mipmaps, container.pixelWidth, container.pixelHeight );
} else if ( container.layerCount > 1 ) {

texture = new CompressedArrayTexture( mipmaps, container.pixelWidth, container.pixelHeight, container.layerCount );

} else if ( container.pixelDepth === 0 ) {

texture = new CompressedTexture( mipmaps, container.pixelWidth, container.pixelHeight );

} else {

throw new Error( 'THREE.KTX2Loader: Unsupported pixelDepth.' );

}

}

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 ];
Expand Down
Binary file modified examples/textures/compressed/2d_rgba16_linear.ktx2
Binary file not shown.
Binary file modified examples/textures/compressed/2d_rgba32_linear.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/3d_etc1s.ktx2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added examples/textures/compressed/3d_rgba32_lut.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/3d_rgba8.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/3d_rgba8_linear.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/3d_uastc.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/array_etc1s.ktx2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added examples/textures/compressed/array_rgba8.ktx2
Binary file not shown.
Binary file not shown.
Binary file added examples/textures/compressed/array_uastc.ktx2
Binary file not shown.
Binary file added examples/textures/compressed/cubemap_etc1s.ktx2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added examples/textures/compressed/cubemap_rgba8.ktx2
Binary file not shown.
Binary file not shown.
Binary file added examples/textures/compressed/cubemap_uastc.ktx2
Binary file not shown.
89 changes: 49 additions & 40 deletions examples/webgl_loader_texture_ktx2.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl - KTX2 texture loader<br />
<a href="http://github.khronos.org/KTX-Specification/" target="_blank" rel="noopener">KTX2</a> with
<a href="https://github.com/binomialLLC/basis_universal" target="_blank">Basis Universal GPU Texture Codec</a>
<a href="http://github.khronos.org/KTX-Specification/" target="_blank" rel="noopener">KTX2</a>
with various layouts and compression
</div>

<script type="importmap">
Expand All @@ -29,19 +29,46 @@

import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { TextureHelper } from 'three/addons/helpers/TextureHelper.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

let camera, scene, renderer, controls, loader, material;
let camera, scene, renderer, controls, loader, helper;

const SAMPLES = {
'BasisU ETC1S': '2d_etc1s.ktx2',
'BasisU UASTC': '2d_uastc.ktx2',
'RGBA8 sRGB': '2d_rgba8.ktx2',
'RGBA8 Linear': '2d_rgba8_linear.ktx2',
// 'RGBA8 Display P3': '2d_rgba8_displayp3.ktx2',
'RGBA16 Linear': '2d_rgba16_linear.ktx2',
'RGBA32 Linear': '2d_rgba32_linear.ktx2',
'ASTC 6x6 (mobile)': '2d_astc_6x6.ktx2',
// 2D
'2D / BasisU ETC1S': '2d_etc1s.ktx2',
'2D / BasisU UASTC': '2d_uastc.ktx2',
'2D / RGBA8 sRGB': '2d_rgba8.ktx2',
'2D / RGBA8 Linear': '2d_rgba8_linear.ktx2',
// '2D / RGBA8 Display P3': '2d_rgba8_displayp3.ktx2',
'2D / RGBA16 Linear': '2d_rgba16_linear.ktx2',
'2D / RGBA32 Linear': '2d_rgba32_linear.ktx2',
'2D / ASTC 6x6 (mobile)': '2d_astc_6x6.ktx2',

// 3D
'3D / BasisU ETC1S': '3d_etc1s.ktx2',
'3D / BasisU UASTC': '3d_uastc.ktx2',
'3D / RGBA8 sRGB': '3d_rgba8.ktx2',
'3D / RGBA8 Linear': '3d_rgba8_linear.ktx2',
'3D / RGBA16 Linear': '3d_rgba16_linear.ktx2',
'3D / RGBA32 Linear': '3d_rgba32_linear.ktx2',
'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',
'Array / RGBA32 Linear': 'array_rgba32_linear.ktx2',
};

const FORMAT_LABELS = {
Expand Down Expand Up @@ -87,7 +114,6 @@
window.addEventListener( 'resize', onWindowResize );

scene = new THREE.Scene();
scene.background = new THREE.Color( 0x202020 );

camera = new THREE.PerspectiveCamera( 60, width / height, 0.1, 100 );
camera.position.set( 0, 0, 2.5 );
Expand All @@ -96,16 +122,6 @@

controls = new OrbitControls( camera, renderer.domElement );

// PlaneGeometry UVs assume flipY=true, which compressed textures don't support.
const geometry = flipY( new THREE.PlaneGeometry() );
material = new THREE.MeshBasicMaterial( {
color: 0xFFFFFF,
side: THREE.DoubleSide,
transparent: true,
} );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

loader = new KTX2Loader()
.setTranscoderPath( 'jsm/libs/basis/' )
.detectSupport( renderer );
Expand Down Expand Up @@ -146,10 +162,19 @@

const texture = await loader.loadAsync( `./textures/compressed/${path}` );
texture.minFilter = THREE.NearestMipmapNearestFilter;
texture.needsUpdate = true;

if ( helper ) {

scene.remove( helper );
helper.dispose();

}

material.map = texture;
material.needsUpdate = true;
helper = new TextureHelper( texture );
scene.add( helper );

console.info( `class: ${ texture.constructor.name }` );
console.info( `format: ${ FORMAT_LABELS[ texture.format ] }` );
console.info( `type: ${ TYPE_LABELS[ texture.type ] }` );
console.info( `colorSpace: ${ texture.colorSpace }` );
Expand All @@ -162,22 +187,6 @@

// NOTE: Call `loader.dispose()` when finished loading textures.


}

/** Correct UVs to be compatible with `flipY=false` textures. */
function flipY( geometry ) {

const uv = geometry.attributes.uv;

for ( let i = 0; i < uv.count; i ++ ) {

uv.setY( i, 1 - uv.getY( i ) );

}

return geometry;

}

</script>
Expand Down
1 change: 1 addition & 0 deletions src/Three.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export { FramebufferTexture } from './textures/FramebufferTexture.js';
export { Source } from './textures/Source.js';
export { DataTexture } from './textures/DataTexture.js';
export { DataArrayTexture } from './textures/DataArrayTexture.js';
export { DataCubeTexture } from './textures/DataCubeTexture.js';
export { Data3DTexture } from './textures/Data3DTexture.js';
export { CompressedTexture } from './textures/CompressedTexture.js';
export { CompressedArrayTexture } from './textures/CompressedArrayTexture.js';
Expand Down
35 changes: 35 additions & 0 deletions src/textures/DataCubeTexture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Texture } from './Texture.js';
import { CubeReflectionMapping } from '../constants.js';

class DataCubeTexture extends Texture {

constructor( data, width, height ) {

super( data, CubeReflectionMapping );

this.isDataCubeTexture = true;
this.isCubeTexture = true;

this.image = { data: data, width: width, height: height };

this.generateMipmaps = false;
this.flipY = false;
this.unpackAlignment = 1;

}

get images() {

return this.image;

}

set images( value ) {

this.image = value;

}

}

export { DataCubeTexture };
Loading