-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Examples: Add WebGPU KTX2 test demo. #31714
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
Merged
+302
−0
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,301 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <title>three.js WebGPU - KTX2 texture loader</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"> | ||
| <style> | ||
| * { | ||
| box-sizing: border-box; | ||
| -moz-box-sizing: border-box; | ||
| } | ||
|
|
||
| body { | ||
| background-color: #fff; | ||
| color: #444; | ||
| } | ||
|
|
||
| a { | ||
| color: #08f; | ||
| } | ||
|
|
||
| #content { | ||
| position: absolute; | ||
| top: 0; width: 100%; | ||
| z-index: 1; | ||
| padding: 3em 0 0 0; | ||
| } | ||
|
|
||
| section { | ||
| padding: 1em; | ||
| } | ||
|
|
||
| #c { | ||
| position: absolute; | ||
| left: 0; | ||
| width: 100%; | ||
| height: 100%; | ||
| } | ||
|
|
||
| section .description { | ||
| max-width: 50em; | ||
| text-wrap: pretty; | ||
| } | ||
|
|
||
| .list-item { | ||
| display: inline-block; | ||
| margin: 1em; | ||
| padding: 1em; | ||
| box-shadow: 1px 2px 4px 0px rgba(0,0,0,0.25); | ||
| } | ||
|
|
||
| .list-item > div:nth-child(1) { | ||
| width: 200px; | ||
| height: 200px; | ||
| } | ||
|
|
||
| .list-item > div:nth-child(2) { | ||
| color: #888; | ||
| font-family: sans-serif; | ||
| width: 200px; | ||
| margin-top: 0.5em; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
|
|
||
| <canvas id="c"></canvas> | ||
|
|
||
| <div id="content"> | ||
| <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - KTX2 texture loader</div> | ||
| </div> | ||
|
|
||
| <script type="importmap"> | ||
| { | ||
| "imports": { | ||
| "three": "../build/three.webgpu.js", | ||
| "three/webgpu": "../build/three.webgpu.js", | ||
| "three/tsl": "../build/three.tsl.js", | ||
| "three/addons/": "./jsm/" | ||
| } | ||
| } | ||
| </script> | ||
|
|
||
| <script type="module"> | ||
|
|
||
| import * as THREE from 'three'; | ||
|
|
||
| import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js'; | ||
|
|
||
| let canvas, renderer; | ||
|
|
||
| const scenes = []; | ||
|
|
||
| const sections = [ | ||
| { | ||
| title: 'Uncompressed', | ||
| description: 'Uncompressed formats (rgba8, rgba16, rgba32) load as THREE.DataTexture objects.' | ||
| + ' Lossless, easy to read/write, uncompressed on GPU, optionally compressed over the network.', | ||
| textures: [ | ||
| { path: '2d_rgba8.ktx2' }, | ||
| { path: '2d_rgba8_linear.ktx2' }, | ||
| { path: '2d_rgba16_linear.ktx2' }, | ||
| { path: '2d_rgba32_linear.ktx2' }, | ||
| { path: '2d_rgb9e5_linear.ktx2' }, | ||
| { path: '2d_r11g11b10_linear.ktx2' }, | ||
| ] | ||
| }, | ||
| { | ||
| title: 'Compressed', | ||
| description: 'Compressed formats (ASTC, BCn, ...) load as THREE.CompressedTexture objects,' | ||
| + ' reducing memory cost. Requires native support on the device GPU: no single compressed' | ||
| + ' format is supported on every device.', | ||
| textures: [ | ||
| { path: '2d_astc4x4.ktx2' }, | ||
| { path: '2d_etc1.ktx2' }, | ||
| { path: '2d_etc2.ktx2' }, | ||
| // { path: '2d_bc1.ktx2' }, TODO: Add support for WebGPU | ||
| { path: '2d_bc3.ktx2' }, | ||
| // { path: '2d_bc5.ktx2' }, | ||
| // { path: '2d_bc7.ktx2' }, TODO: Add support for WebGPU | ||
| ] | ||
| }, | ||
|
|
||
| { | ||
| title: 'Universal', | ||
| description: 'Basis Universal textures are specialized intermediate formats supporting fast' | ||
| + ' runtime transcoding into other GPU texture compression formats. After transcoding,' | ||
| + ' universal textures can be used on any device at reduced memory cost.', | ||
| textures: [ | ||
| { path: '2d_etc1s.ktx2' }, | ||
| { path: '2d_uastc.ktx2' }, | ||
| ] | ||
| }, | ||
| ]; | ||
|
|
||
| init(); | ||
|
|
||
| async function init() { | ||
|
|
||
| canvas = document.getElementById( 'c' ); | ||
|
|
||
| renderer = new THREE.WebGPURenderer( { canvas, antialias: true, forceWebGL: false } ); | ||
| renderer.setClearColor( 0xffffff, 1 ); | ||
| renderer.setPixelRatio( window.devicePixelRatio ); | ||
|
|
||
| await renderer.init(); | ||
|
|
||
| const loader = new KTX2Loader() | ||
| .setTranscoderPath( 'jsm/libs/basis/' ) | ||
| .setPath( 'textures/ktx2/' ) | ||
| .detectSupport( renderer ); | ||
|
|
||
| const geometry = flipY( new THREE.PlaneGeometry( 1, 1 ) ); | ||
|
|
||
| const content = document.getElementById( 'content' ); | ||
|
|
||
| for ( const section of sections ) { | ||
|
|
||
| const sectionElement = document.createElement( 'section' ); | ||
|
|
||
| const sectionHeader = document.createElement( 'h2' ); | ||
| sectionHeader.textContent = section.title; | ||
| sectionElement.appendChild( sectionHeader ); | ||
|
|
||
| const sectionDescription = document.createElement( 'p' ); | ||
| sectionDescription.className = 'description'; | ||
| sectionDescription.textContent = section.description; | ||
| sectionElement.appendChild( sectionDescription ); | ||
|
|
||
| for ( const { path, supported } of section.textures ) { | ||
|
|
||
| const scene = new THREE.Scene(); | ||
|
|
||
| // make a list item | ||
| const element = document.createElement( 'div' ); | ||
| element.className = 'list-item'; | ||
|
|
||
| const sceneElement = document.createElement( 'div' ); | ||
| element.appendChild( sceneElement ); | ||
|
|
||
| const labelElement = document.createElement( 'div' ); | ||
| labelElement.innerText = 'file: ' + path; | ||
| element.appendChild( labelElement ); | ||
|
|
||
| // the element that represents the area we want to render the scene | ||
| scene.userData.element = sceneElement; | ||
| sectionElement.appendChild( element ); | ||
|
|
||
| const camera = new THREE.PerspectiveCamera( 50, 1, 1, 10 ); | ||
| camera.position.z = 2; | ||
| scene.userData.camera = camera; | ||
|
|
||
| try { | ||
|
|
||
| const texture = await loader.loadAsync( supported === false ? 'fail_load.ktx2' : path ); | ||
| const mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) ); | ||
|
|
||
| labelElement.innerText += '\ncolorSpace: ' + texture.colorSpace; | ||
|
|
||
| scene.add( mesh ); | ||
| scenes.push( scene ); | ||
|
|
||
| } catch ( e ) { | ||
|
|
||
| console.error( `Failed to load ${path}`, e ); | ||
|
|
||
| } | ||
|
|
||
|
|
||
| } | ||
|
|
||
| content.appendChild( sectionElement ); | ||
|
|
||
| } | ||
|
|
||
| renderer.setAnimationLoop( animate ); | ||
|
|
||
|
|
||
| } | ||
|
|
||
| function updateSize() { | ||
|
|
||
| const width = canvas.clientWidth; | ||
| const height = canvas.clientHeight; | ||
|
|
||
| if ( canvas.width !== width || canvas.height !== height ) { | ||
|
|
||
| renderer.setSize( width, height, false ); | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| // Rewrite UVs for `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; | ||
|
|
||
| } | ||
|
|
||
| function animate() { | ||
|
|
||
| updateSize(); | ||
|
|
||
| canvas.style.transform = `translateY(${window.scrollY}px)`; | ||
|
|
||
| renderer.setClearColor( 0xffffff ); | ||
| renderer.setScissorTest( false ); | ||
| renderer.clear(); | ||
|
|
||
| renderer.setClearColor( 0xe0e0e0 ); | ||
| renderer.setScissorTest( true ); | ||
|
|
||
| scenes.forEach( function ( scene ) { | ||
|
|
||
| // get the element that is a place holder for where we want to | ||
| // draw the scene | ||
| const element = scene.userData.element; | ||
|
|
||
| // get its position relative to the page's viewport | ||
| const rect = element.getBoundingClientRect(); | ||
|
|
||
| // check if it's offscreen. If so skip it | ||
| if ( rect.top < 0 || rect.bottom > renderer.domElement.clientHeight || | ||
| rect.left < 0 || rect.right > renderer.domElement.clientWidth ) { | ||
|
|
||
| return; // it's off screen | ||
|
|
||
| } | ||
|
|
||
| // set the viewport | ||
| const width = rect.right - rect.left; | ||
| const height = rect.bottom - rect.top; | ||
| const left = rect.left; | ||
| const top = rect.top; | ||
|
|
||
| renderer.setViewport( left, top, width, height ); | ||
| renderer.setScissor( left, top, width, height ); | ||
|
|
||
| const camera = scene.userData.camera; | ||
|
|
||
| renderer.render( scene, camera ); | ||
|
|
||
| } ); | ||
|
|
||
| } | ||
|
|
||
| </script> | ||
|
|
||
| </body> | ||
| </html> | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@donmccurdy I'll add TODOs here for #31690 (comment).