From dc59c08fa5c47e3b3f9459049e815dd96abb2996 Mon Sep 17 00:00:00 2001 From: Felix Herbst Date: Thu, 24 Nov 2022 23:52:14 +0100 Subject: [PATCH] refactor: move to TextureUtils class and use that in GLTFExporter, respect sRGB vs. Linear, cache some generated objects --- examples/jsm/exporters/GLTFExporter.js | 72 +++------------------- examples/jsm/utils/TextureUtils.js | 85 ++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 63 deletions(-) create mode 100644 examples/jsm/utils/TextureUtils.js diff --git a/examples/jsm/exporters/GLTFExporter.js b/examples/jsm/exporters/GLTFExporter.js index 02b55faf6d3fc0..4504b6ba2cac30 100644 --- a/examples/jsm/exporters/GLTFExporter.js +++ b/examples/jsm/exporters/GLTFExporter.js @@ -20,16 +20,10 @@ import { Scene, Source, sRGBEncoding, - Texture, CompressedTexture, - PlaneGeometry, - ShaderMaterial, - Mesh, - PerspectiveCamera, - WebGLRenderer, - Uniform, Vector3 } from 'three'; +import { decompress } from './../utils/TextureUtils.js'; class GLTFExporter { @@ -545,13 +539,6 @@ class GLTFWriter { } - // Clean up in case we had to create a temporary renderer for blitting compressed textures. - if (this.temporaryRenderer) { - - this.temporaryRenderer.dispose(); - - } - } /** @@ -730,49 +717,6 @@ class GLTFWriter { } - buildReadableTexture( map, maxTextureSize ) { - - const fullscreenQuadGeometry = new PlaneGeometry( 2, 2, 1, 1 ); - const fullscreenQuadMaterial = new ShaderMaterial( { - uniforms: { blitTexture: new Uniform( map ) }, - vertexShader: ` - varying vec2 vUv; - void main(){ - vUv = uv; - gl_Position = vec4(position.xy * 1.0,0.,.999999); - }`, - fragmentShader: ` - uniform sampler2D blitTexture; - varying vec2 vUv; - void main(){ - gl_FragColor = vec4(vUv.xy, 0, 1); - gl_FragColor = texture2D( blitTexture, vUv); - }` - } ); - - const fullscreenQuad = new Mesh( fullscreenQuadGeometry, fullscreenQuadMaterial ); - fullscreenQuad.frustrumCulled = false; - - const temporaryCam = new PerspectiveCamera(); - const temporaryScene = new Scene(); - temporaryScene.add( fullscreenQuad ); - - if (!this.temporaryRenderer) { - - this.temporaryRenderer = new WebGLRenderer( { antialias: false } ); - - } - - this.temporaryRenderer.setSize( Math.min(map.image.width, maxTextureSize), Math.min(map.image.height, maxTextureSize) ); - this.temporaryRenderer.clear(); - this.temporaryRenderer.render( temporaryScene, temporaryCam ); - - const readableTexture = new Texture( this.temporaryRenderer.domElement ); - readableTexture.userData.mimeType = 'image/png'; - return readableTexture; - - } - buildMetalRoughTexture( metalnessMap, roughnessMap ) { if ( metalnessMap === roughnessMap ) return metalnessMap; @@ -801,17 +745,17 @@ class GLTFWriter { console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' ); - if ( typeof CompressedTexture !== 'undefined') { + if ( typeof CompressedTexture !== 'undefined' ) { if ( metalnessMap instanceof CompressedTexture ) { - metalnessMap = this.buildReadableTexture( metalnessMap ); + metalnessMap = decompress( metalnessMap ); } if ( roughnessMap instanceof CompressedTexture ) { - roughnessMap = this.buildReadableTexture( roughnessMap ); + roughnessMap = decompress( roughnessMap ); } @@ -1209,6 +1153,7 @@ class GLTFWriter { console.error( 'GLTFExporter: Only RGBAFormat is supported.', image ); } + if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) { console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image ); @@ -1335,13 +1280,14 @@ class GLTFWriter { let modifiedMap = map; // make non-readable textures (e.g. CompressedTexture) readable by blitting them into a new texture - // TODO: how to detect that a texture isn't readable? if ( typeof CompressedTexture !== 'undefined' && map instanceof CompressedTexture ) { - modifiedMap = this.buildReadableTexture( map, options.maxTextureSize ); + modifiedMap = decompress( map, options.maxTextureSize ); + // remove from cache, as the underlaying canvas is always the same between decompressed textures + cache.images.delete( modifiedMap.image ); } - + let mimeType = modifiedMap.userData.mimeType; if ( mimeType === 'image/webp' ) mimeType = 'image/png'; diff --git a/examples/jsm/utils/TextureUtils.js b/examples/jsm/utils/TextureUtils.js new file mode 100644 index 00000000000000..8f5d44e4d27e30 --- /dev/null +++ b/examples/jsm/utils/TextureUtils.js @@ -0,0 +1,85 @@ +import { + PlaneGeometry, + ShaderMaterial, + Uniform, + Mesh, + PerspectiveCamera, + Scene, + WebGLRenderer, + Texture, + sRGBEncoding +} from 'three'; + +let temporaryRenderer; +let fullscreenQuadGeometry; +let fullscreenQuadMaterial; +let fullscreenQuad; + +function decompress( texture, maxTextureSize, renderer ) { + + if ( !fullscreenQuadGeometry ) fullscreenQuadGeometry = new PlaneGeometry( 2, 2, 1, 1 ); + if ( !fullscreenQuadMaterial ) fullscreenQuadMaterial = new ShaderMaterial( { + uniforms: { blitTexture: new Uniform( texture ) }, + vertexShader: ` + varying vec2 vUv; + void main(){ + vUv = uv; + gl_Position = vec4(position.xy * 1.0,0.,.999999); + }`, + fragmentShader: ` + uniform sampler2D blitTexture; + varying vec2 vUv; + + // took from threejs 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4 + vec4 conv_LinearTosRGB( in vec4 value ) { + return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); + } + + void main(){ + gl_FragColor = vec4(vUv.xy, 0, 1); + #ifdef IS_SRGB + gl_FragColor = conv_LinearTosRGB( texture2D( blitTexture, vUv) ); + #else + gl_FragColor = texture2D( blitTexture, vUv); + #endif + }` + } ); + + fullscreenQuadMaterial.uniforms.blitTexture.value = texture; + fullscreenQuadMaterial.defines.IS_SRGB = texture.encoding == sRGBEncoding; + + if ( !fullscreenQuad ) { + + fullscreenQuad = new Mesh( fullscreenQuadGeometry, fullscreenQuadMaterial ); + fullscreenQuad.frustrumCulled = false; + + } + + const temporaryCam = new PerspectiveCamera(); + const temporaryScene = new Scene(); + temporaryScene.add( fullscreenQuad ); + + if ( !renderer ) { + + if ( !temporaryRenderer ) + temporaryRenderer = new WebGLRenderer( { antialias: false } ); + + renderer = temporaryRenderer; + + } + + renderer.setSize( Math.min(texture.image.width, maxTextureSize), Math.min(texture.image.height, maxTextureSize) ); + renderer.clear(); + renderer.render( temporaryScene, temporaryCam ); + + const readableTexture = new Texture( renderer.domElement ); + readableTexture.userData.mimeType = 'image/png'; + console.log(readableTexture, readableTexture.image) + + return readableTexture; + +} + +export { + decompress +} \ No newline at end of file