Three.js: BasisTextureLoader:下一步

创建于 2019-05-22  ·  60评论  ·  资料来源: mrdoob/three.js

https://github.com/mrdoob/three.js/pull/16522 中添加了对 .basis 纹理的初始支持

  • [x] 清理代码中剩余的 TODO
  • [x] 应用 eslint 修复
  • [x] 添加文档
  • [x] 添加示例
  • [x] 添加setMaxWorkers()方法
  • [x] 支持 mipmap
  • [] 修复 iOS 中的 mipmap 支持
  • [] 使用@austinEng 建议的引导程序重新编译 Basis 转码器
  • [] 从load()同步返回纹理(没有 alpha?)
  • [ ] 支持阿尔法
  • [ ] 支持用户可配置的转码输出格式
  • [x] 添加 ES 模块
Enhancement Loaders

最有用的评论

这种解决方法(在调用转码之前的工作人员中)似乎对我有用。 这当然需要在转码器中修复,但在 JS 中很容易验证:

var mipWidth = basisFile.getImageWidth( 0, mip );
var mipHeight = basisFile.getImageHeight( 0, mip );
var mipSize = basisFile.getImageTranscodedSizeInBytes( 0, mip, config.format );

if ( config.pvrtcSupported ) {

    // Basis incorrectly computes mip sizes for PVRTC, let's fix them up using the spec:
    // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
    mipSize = Math.floor((Math.max(mipWidth, 8) * Math.max(mipHeight, 8) * 4 + 7) / 8);

}

var dst = new Uint8Array( mipSize );

所有60条评论

我还没有详细研究代码,但是我们可以像其他纹理加载器一样从load同步返回texture吗?

我认为这有利于一致性。 如果我们不同步返回纹理,用户需要在回调中调用material.needsUpdate = true (如果已经开始动画循环)。

var material = new XXXMaterial();
textureLoader.load(..., function (texture) {
  material.map = texture;
   // .map is from null to non-null.
   // User needs to call material.needsUpdate = true here if already started animation loop
   // because whether material.map is null or not affects the final shader code.
  material.needsUpdate = true;
});

我还没有检查以确认它适用于THREE.CompressedTexture ,但如果是这样,我同意那将是最好的。 👍

其他清理:分配给纹理的属性有点随意(从 Basis 演示中遗留下来),例如flipY=false 。 工人中有一个未使用的startTime变量。

如果我是对的,似乎不支持 mipmaps。 转码器不支持? 或者 loader 还没有实现 mipmaps 支持?

一个.basis文件可以包含多个 mipmap 级别,是的。 我认为转码器已经支持它,但我还没有测试过。 BasisTextureLoader 尚不支持它。

我们也应该更新到新的/较小版本的转码器: https :

是的,转码器应该支持它。 您将 mip 级别作为levelIndex传递给transcodeImage
https://github.com/BinomialLLC/basis_universal/blob/master/webgl/transcoder/basis_wrappers.cpp#L197

谢谢你的解释。

如果还有加载器不支持的任何其他功能,但转码器支持,你知道,请添加到 TODO 列表。 我们可以帮助实施。

做了一个例子。 #16553 可能太简单了,如果合并了,请随时增强/替换。

在转码器具有但 THREE.BasisTextureLoader 没有的其他功能上,一个关键区别是转码器可以输出许多其他格式:

https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/BasisTextureLoader.js#L264 -L273

老实说,我不知道什么时候使用所有这些。 我希望用户有时会想要控制它,而在其他情况下,加载器(例如 GLTFLoader)将根据纹理的目的做出决定。 例如,它可能为material.map (基色)选择与material.aoMap (环境遮挡)不同的压缩格式。

支持这一点的最明显方法是为detectSupport( renderer )添加一个替代方案:

// Let loader decide, based on device capabilities:
loader.detectSupport( renderer );

// Or, choose a particular format:
loader.setFormat( THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC4 );

这有一个潜在的问题——如果我正在加载多个纹理,我们可能不想将它们全部转码为相同的格式。 我们可以创建多个加载器,但是这样就很难重用现有的 Web Workers(这很重要)。 我们可以将格式传递到load()方法中,但是它与 TextureLoader 不向后兼容。 我想最好的办法可能是确保这样做......

loader.setFormat( THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC4 );
var fooTex = loader.load( 'foo.basis' );

loader.setFormat( THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC1 );
var barTex = loader.load( 'bar.basis' );

... 将始终对每个纹理应用正确的格式,即使解码是异步的。

相关: http :

另一个注意事项,只是为了在某处进行跟踪: examples/js/libs/basis中的 JS 包装器包含与 Basis 存储库中的版本相比的微小更改。 第一个声明 ( var Module ) 仅替换为Module以启用 Web Worker 中使用的延迟初始化。 这可能可以通过不同的标志编译转码器或通过https://github.com/BinomialLLC/basis_universal/issues/21以不同的方式完成

BasisTextureLoader 是否应该与 glTF 协同工作? 我尝试将纹理手动编码为 .basis 格式,并将 BasisTextureLoader 添加为这样的加载器:

var basisLoader = new THREE.BasisTextureLoader();
basisLoader.setTranscoderPath( 'basis/' );
basisLoader.detectSupport( renderer );

THREE.Loader.Handlers.add( /\.basis$/i, basisLoader );

但是纹理渲染不正确,控制台有以下输出:

[.WebGL-000002B68031CEF0]RENDER WARNING: texture bound to texture unit 1 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering.

@zeux描述相同的问题

我调试了这个,发生这种情况是因为:

  1. glTF 加载器通常将 mag 过滤设置为 LinearMipMapLinearFilter
  2. BasisTextureLoader 不支持 mipmap
  3. webGL 需要完整的 mip 链:-/

@zeux未得到官方支持——核心 glTF 规范仅允许 JPEG 和 PNG 纹理。 但是对 Basis 的扩展(通过 KTX2 包装器)是我们计划向格式添加压缩纹理支持的机制。 请参阅https://github.com/KhronosGroup/glTF/pull/1612以获取扩展草案(此处仅前两个相关)并随时添加反馈。

也就是说,BasisTextureLoader 中缺少 mipmap 支持纯粹是因为我们还没有接触到它。 据我所知,转码器本身应该支持这一点。

提交 PR 以修复 mipmap 支持 - 只要您使用-mipmap选项转换纹理,只要您添加上面列出的加载器,glTF 加载器就可以工作,至少在桌面上是这样。 我无法让它在移动设备(iPhone 或 Android)上运行,但是带有旋转立方体的 Threejs.org 示例也无法在 iPhone 上运行,因此这可能是一个单独的问题。

我无法让它在移动设备(iPhone 或 Android)上运行

来自基础文档 -

例如,在 iOS 上,您只能对 PVRTC1 使用 2 次纹理维度的平方,而今天 Basis 无法为您做任何可以解决此限制的事情。 (我们将很快支持将较小的非 pow2 纹理转码为 2 个 PVRTC1 纹理的更大功率的能力。)

我们在这个演示中使用了一个 512x768 的纹理,可能应该用符合该约束的东西替换它。

好的 - 这是有道理的。 FWIW 我正在测试的 Android 手机在多个 WebGL 演示中存在大量问题,而不仅仅是与基础纹理相关 - 不同的 Android 手机运行得很好。 所以是的,它可能只是在 iOS 上有问题的二次幂限制。

BasisTextureLoader 的各种更新来自https://github.com/mrdoob/three.js/pull/16675。

我们可能还应该考虑如何支持 alpha ...... Basis 文档详细介绍了这些选项 (https://github.com/BinomialLLC/basis_universal/#how-to-use-the-system) 但对于某些这涉及多个转码输出的设备:

仅限 ETC1 设备/API:转码为两个 ETC1 纹理并在着色器中对其进行采样。 您可以使用一个两倍高的 ETC1 纹理,也可以使用两个单独的 ETC1 纹理。

到目前为止,API 匹配TextureLoader ,它需要更改(或具有替代 API)以支持从单个.basis纹理返回多个转码输出。

https://github.com/mrdoob/three.js/pull/16686 中更改为平方的 2 次幂纹理修复了 iOS 上的演示,但 mipmap 不起作用:

INVALID_VALUE:compressedTexImage2D:ArrayBufferView 的长度对于维度不正确。

我们需要为 PVRTC 的 mipmap 做任何特别的事情吗?

最后几个mips之一会发生这种情况吗?

我没有足够仔细地调试来确定哪些 mips,但是错误被打印了 3 次,最后三个确实具有相同的缓冲区大小。 🤔

我怀疑 Basis 没有正确计算最后几个 mips 的大小(以字节为单位)。 对于 PVRTC1 4bpp,块的尺寸四舍五入为 8,因此 4x4、2x2 和 1x1 应与 8x8 大小相同。 我认为 Basis 转码器四舍五入到 4x4。 所有尺寸为 8x8、4x4、2x2、1x1 的图像必须占用 32 个字节; 我认为对于 4x4 及以下,Basis 假设它只是 1 个 4x4 块,即 8 个字节,并为您提供 8 个字节而不是 32 个。@richgel999

计算图像大小的公式在这里: https :

   For PVRTC 4BPP formats the imageSize is calculated as:
     ( max(width, 8) * max(height, 8) * 4 + 7) / 8

这种解决方法(在调用转码之前的工作人员中)似乎对我有用。 这当然需要在转码器中修复,但在 JS 中很容易验证:

var mipWidth = basisFile.getImageWidth( 0, mip );
var mipHeight = basisFile.getImageHeight( 0, mip );
var mipSize = basisFile.getImageTranscodedSizeInBytes( 0, mip, config.format );

if ( config.pvrtcSupported ) {

    // Basis incorrectly computes mip sizes for PVRTC, let's fix them up using the spec:
    // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
    mipSize = Math.floor((Math.max(mipWidth, 8) * Math.max(mipHeight, 8) * 4 + 7) / 8);

}

var dst = new Uint8Array( mipSize );

谢谢! 我会尽快解决这个问题。

应该修复 PVRTC1 mipmap 大小错误。 我们修复了转码器,以便它可以清除小(小于 8 像素宽/高)mips 上的额外字节。 我们修复了包装器以返回正确的尺寸。 如果有任何问题,请告诉我,我会尽快修复它们。

@donmccurdy您能否将“从load方法同步返回texture ”添加到待办事项列表中? (如 takahirox 上面建议的那样)

@Ben-Mack 添加。 请注意,因为带有 alpha 通道的纹理可能会转码为多个纹理,而且我们不会同步知道这一点,所以我们需要不同的 API。

@richgel999谢谢! 我在重建 WASM 转码器时遇到了麻烦,因为https://github.com/BinomialLLC/basis_universal/commit/ab722fa2e18536f9a1d5f33814f3088232446d52只更新了webgl/transcoder/basis_wrappers.cpp 。 在 macOS 上编译:

$ emcmake cmake ../
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/donmccurdy/Documents/Projects/basis_universal/webgl/transcoder/build

$ make
[ 33%] Linking CXX executable basis_transcoder.js
Traceback (most recent call last):
  File "/Users/donmccurdy/Documents/Projects/emsdk/emscripten/1.37.22/em++", line 16, in <module>
    emcc.run()
  File "/Users/donmccurdy/Documents/Projects/emsdk/emscripten/1.37.22/emcc.py", line 882, in run
    exec 'shared.Settings.' + key + ' = ' + value in globals(), locals()
  File "<string>", line 1, in <module>
NameError: name 'emmalloc' is not defined
make[2]: *** [basis_transcoder.js] Error 1
make[1]: *** [CMakeFiles/basis_transcoder.js.dir/all] Error 2
make: *** [all] Error 2

理论上我们应该能够更新转码器并用这个纹理替换当前的PavingStones.basis纹理,其中包括 mipmap:

铺路石.basis.zip

编辑:糟糕,这可能与https://github.com/BinomialLLC/basis_universal/pull/27有关

@donmccurdy我相信这需要合并https://github.com/BinomialLLC/basis_universal/pull/27 ,希望这能很快发生。 此外,您的 emscripten 版本可能早于 emmalloc 的存在,我认为目前属于 Three.js 的文件是用 1.38.31 构建的。

当我尝试同时加载多个基础文件时发生此错误:

Uncaught (in promise) DOMException: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 0 is already neutered.

要重现,只需调用load方法 2 次,例如:

loader.load( 'textures/compressed/PavingStones.basis');
loader.load( 'textures/compressed/PavingStones.basis');

@Ben-Mack 可能仍然值得修复,但该错误只是因为您加载了两次完全相同的纹理,并且加载器重用了无法同时传输给两个工作器的 ArrayBuffer。 下面的代码运行,代价是做两倍的工作:

loader.load( 'textures/compressed/PavingStones.basis?v=1' );
loader.load( 'textures/compressed/PavingStones.basis?v=2' );

@donmccurdy对于 alpha 的支持,例如:

load(...) // as usual
//then :  
loader.getRGBTexture() //return by the load function as usual
loader.getAlphaTexture() //can be use as alphaMap

也可以在 mipmap 模型上添加支持 alpha 的选项:
loader.generateAlpha = true //default

@ Makio64类似的东西! 大多数threejs加载器不是有状态的(一个加载器可以并行处理多个事情)所以也许:

const [ map ]           = loader.loadRGB( 'foo.basis', { ...options } );
const [ map, alphaMap ] = loader.loadRGBA( 'bar.basis', { ...options } );

^在这两种情况下,尤其是 alpha,我认为可能有足够多的不同方法来做事情,需要对方法进行一些配置。

或者我们可以在新方法上异步进行,而不是:

loader.loadRGBA( 'bar.basis', { ...options }, ( map, alphaMap ) => {
  // ...
} );

异步解决方案看起来更容易与 worker 一起实现,并与其他 Threejs 的加载器保持一致。

我只能加载分辨率为 768 x 512 或 384 x 256 等的纹理。使用 Three.js 和 BasisTextureLoader 无法加载任何其他分辨率并发出警告:
“绑定到纹理单元 0 的纹理不可渲染。它可能不是 2 的幂并且具有不兼容的纹理过滤。”

@mozg4D请参阅Basis 文档,尤其是 iOS 中需要二次幂的纹理。 如果文档与您看到的行为不匹配,请提交新错误。 也有可能选择的纹理过滤不兼容,在这种情况下,我们仍然需要一个演示,一个新的错误会有所帮助。 谢谢!

@donmccurdy会很快发布 Basis alpha 支持吗?

我想我发现了一个错误:仅在 iOS 上,纹理几何边缘有一种奇怪的“纹理发光”效果:#17597

有没有其他人遇到过这种情况?

我认为在 iOS 上它可能会使用 PVRTC。 这是否发生在早期版本 (r108) 中?
也许您可以尝试在https://github.com/BinomialLLC/basis_universal 中提交问题

这是否发生在早期版本 (r108) 中?

我提交的错误是针对 r108 的。

也许您可以尝试在https://github.com/BinomialLLC/basis_universal 中提交问题

刚刚在这样做: https :

我想我发现了一个错误:仅在 iOS 上,纹理几何边缘有一种奇怪的“纹理发光”效果:#17597

我在base_universal github上回复了。 我们将抓取纹理,看看发生了什么。 最有可能的是,这是与未正确设置环绕/钳位寻址转码标志相关的伪影,或者是由我们的实时 PVRTC1 编码器引起的伪影。 如果其中任何一个是问题,通常都有解决方法。 我们还可以提高 PVRTC1 质量,但代价是更多的转码 CPU 时间/内存。

我已经在https://github.com/BinomialLLC/basis_universal/issues/78#issuecomment -536159690 上发布了更新——其中包含截图。

它显示了非旋转立方体的环绕问题。

我发现了另一个错误(但很可能无关): https :

我发现了另一个错误(但很可能无关): #17546(评论)

已在 #17622 中修复。

在 mipmap 方面,有没有一种方法可以正确加载基础纹理文件(在 .gltf 文件中引用)而不必将 mipmap 嵌入到 .basis 文件中?

当我用-mipmap生成 .basis 文件时,我可以让它加载 proplery ,但是这会向 .basis 文件添加很多文件大小 - 但是当我生成没有-mipmap的 .basis 文件时选项,它只是在使用 Threejs 的浏览器中显示为黑色。

在 mipmap 方面,有没有一种方法可以正确加载基础纹理文件(在 .gltf 文件中引用)而不必将 mipmap 嵌入到 .basis 文件中?

当我用-mipmap生成 .basis 文件时,我可以让它加载 proplery ,但是这会向 .basis 文件添加很多文件大小 - 但是当我生成没有-mipmap的 .basis 文件时选项,它只是在使用 Threejs 的浏览器中显示为黑色。

现在,您可以禁用 mipmaps 来显示纹理:
https://discourse.threejs.org/t/compressed-texture-workflow-gltf-basis/10039/12?u=johannesdeml
https://github.com/JohannesDeml/three.js/commit/909d9cc6dc9192f398df7455f52b7e71e3bf61e2

这当然不支持 mipmap,但如果您的目标只是显示纹理,这对您来说可能是一个简单的解决方案。

这似乎不再起作用了,当只检测到 1 个 mipmap 时,看起来 BasisTextureLoader 现在也有类似的代码(设置 min/magFilter = LinearFilter)(https://github.com/mrdoob/three.js /blob/e66e86901abd84ffc260fea9665170631a2b0493/examples/js/loaders/BasisTextureLoader.js#L170-L171) - 这是没有-mipmap选项的basisu生成的。 然而,它仍然是黑色的。

你能分享文件吗? 我最近在上周这样做,尽管它是在.ktx2容器中的 Basis 而不是.basis ...

只是为了确认 - 您知道您无法在运行时生成这些 mipmap,并且必须处理所涉及的插值约束?

当然,感谢您的观看!

body_green.basis.zip

只是为了确认 - 您知道您无法在运行时生成这些 mipmap,并且必须处理所涉及的插值约束?

是的,我也发现了这一点,有点遗憾,因为与压缩的 jpg 相比,我在较低的 .basis 文件大小中看到的大部分增益都丢失了 - 我知道 GPU 内存增益仍然存在,但我主要是专注于纹理的下载/传输大小。

只是为了确认 - 您知道您无法在运行时生成这些 mipmap,并且必须处理所涉及的插值约束?

使用基础而不是 jpg/png 时总是这样吗?

将 6 个纹理作为立方体贴图的面部列表打包到单个基础文件中的用例如何,因为格式支持/在自述文件中提到了这一点。 PMREM 生成器在这里没用,基础文件应该为每个生成的纹理提供 mipmaps 吗?

在 ThreeJS 中提供这个打包的纹理数据用作立方体贴图怎么样? 通常你会为每个单独的纹理传递参数? (我还没有研究这个纹理加载器支持,看看是否可以使用具有多个纹理的基础文件)另一种可能是通过 KTX2,它可能更适合提供用作立方体贴图的面部列表打包基础文件?

关于这种用法的最后一个问题是在讨论 alpha 处理时,这些纹理可以编码为 RGBE 或 RGBM(ThreeJS 支持 M7 和 M16)。 我还没有调查这些基础的压缩程度,但它们可能有与已知的法线贴图类似的问题。 alpha 通道数据在那里得到支持是相当重要的,我不确定它们将被转码为什么压缩纹理格式,有些可能会产生非常糟糕的结果,因为下面的链接文章地址。

例如,ARM 写了一篇关于 ASTC 和 RGBM 问题的文章。 文章提到的 Unity 引擎使用 RGBM5,它在大多数情况下应该覆盖合适的 HDR 范围,如果数据符合范围限制,则较低的乘数可能会比较高的乘数常数产生更少的压缩质量问题。

抱歉,我不知道这些问题的答案。 您可能会在 Basis Universal 或 KTX GitHub 存储库中获得更好的运气。 我知道 KTX2 旨在支持带有 Basis 有效载荷的立方体贴图。

我在这里提出它们,因为它们似乎与使用 ThreeJS 支持相关的案例在这里讨论。

Basis 可以将立方体贴图的所有 6 个纹理存储在单个基础文件中。 具有基础支持的 KTX2 可以更好地支持具有多个纹理但单个文件的立方体贴图。

ThreeJS 未来能否处理它尚不清楚。 也许需要提供 6 个不同的基础文件,或者获得具有基础加载器支持的 KTX2。

RGBM5 需要一个单独的 PR,它只是当前存在的 RGBM7 或 RGBM16,或者是一个采用统一调整乘数值的变体。 主要的重要部分是需要适当处理 alpha 以正确压缩,因此如本期前面讨论的那样能够对此进行一些控制是另一个有用的示例,类似于法线贴图及其线性/非颜色编码,根据压缩支持,也可能将格式拆分为两种可能的纹理(从 X 的单色 RGB 和基础文件中的 Y 的 alpha)。

Three.js 目前支持 Basis Universal。 几周前发布了一种新的基础格式 UASTC。 在此之前,我认为 Basis 不支持单个文件中的立方体贴图。 欢迎提出请求。

KTX2 加载器正在进行中,使用https://github.com/mrdoob/three.js/pull/18490。

基础自述文件的旧版本(UASTC 之前,但仍存在于当前自述文件中)显示了将多个纹理打包成单个基础文件功能的提及:

基础文件支持非均匀纹理数组,因此立方体贴图、体积纹理、纹理数组、mipmap 级别、视频序列或任意纹理“平铺”可以存储在单个文件中。 压缩器能够利用整个文件中的颜色和图案相关性,因此可以将具有 mipmap 的多个图像非常有效地存储在单个文件中。

欢迎提出请求。

我不知道从哪里开始,目前我也没有空闲时间。 也许一旦这个问题进展到完成并且 KTX2 加载器准备就绪,就可以解决压缩/打包立方体贴图支持。 如果到那时我有空闲时间,我很乐意尝试贡献一个 PR :)

大家好,我在 iOS 上遇到了一个间歇性问题(aawww maaaan!)
基本上纹理有时加载良好,有时不出现。
控制台没有错误,加载进度是 100%,所以绝对不是网络问题。
在官方示例 (https://threejs.org/examples/?q=basis#webgl_loader_texture_basis) 和我自己的项目中进行了测试。 在我的 iPhone X 和 Safari 上,纹理有时会出现,有时不会。
在 iPhone 6、Safari 上,它永远不会出现。
所有其他人看起来都很好。
会是什么呢?

[编辑] 刚刚在 Mac OS 上的 Safari 上遇到了同样的问题,仍然断断续续

@igghera听起来这可能是一个错误,特别是如果它发生在官方示例中。 你介意为此开一个新问题吗? 谢谢!

如何在BasisTextureLoader添加对二维数组纹理的支持? 我稍微研究了一下,但我不确定如何继续。

更改transcode函数以从basisFile.getNumImages()返回的计数中循环图像看起来很简单,但看起来有以下三件事需要解决:

  1. compressedTexImage3D不存在THREE.WebGLState并且将需要通过对一个选项gl.TEXTURE_2D_ARRAYWebGLRenderingContext.compressedTexImage3D
  2. Basis 的 API 为每个图像索引返回单独的 mipmap,但是您需要一个大的纹理 blob 来绑定sampler2DArray
  3. CompressedTexture需要一种方法来关联每个图像索引的 mipmap。 对于 200 个深度数组图像和 6 个 mipmap 级别,这将是 1200 个 mipmap 条目,因此mipmaps中的CompressedTexture成为一个跨步数组。 或者有什么方法可以让 WebGL 抽象出这些细节?

然后,对于视频纹理,它可能需要不同的实现。 与其一次性批量转码,不如让基础文件句柄保持打开状态,并通过某种方式请求下一帧。

此页面是否有帮助?
0 / 5 - 0 等级