首页 > 解决方案 > Three.js PMREMGenerator 有不正确的纹理过滤

问题描述

我在三个场景(使用 AFrame)中需要环境映射来进行照明。使用标准 HDR 立方体贴图,我得到以下结果:

具有 HDR Cubemap 的场景

就基于粗糙度的模糊而言,这是正确的,因为我生成了 mipmap,并且 minFilter 设置为 LinearMipmapLinearFilter。这种方法的问题是没有应用环境照明——场景中的光,定向光,是唯一向场景提供任何照明信息的东西。不幸的是,无论 HDRI 有多亮,这都会导致完全黑色的阴影。

但是,如果除了上述之外,我还使用 Three 的 PMREMGenerator,环境照明问题就解决了。不幸的是,结果是这样的:

带有 PMREM 贴图的场景

如此处所示,纹理过滤现在不正常了。根据 PMREMGen 脚本本身留下的评论。

此类从 cubeMap 环境纹理生成预过滤的 Mipmapped Radiance Environment Map (PMREM)。这允许根据材料粗糙度快速访问不同级别的模糊。它被打包成一种特殊的 CubeUV 格式,允许我们执行自定义插值,以便我们可以支持非线性格式,例如 RGBE。与传统的 mipmap 链不同,它只会在相同 LOD_MIN 分辨率下下降更多过滤的“mips”,并与更高的粗糙度级别相关联。通过这种方式,我们可以在限制采样计算的同时保持分辨率以平滑地插入漫反射光照。

...这让我相信输出应该被平滑,就像我的第一个例子一样。

这是我的第一个示例的代码:

const ctl = new HDRCubeTextureLoader();
ctl.setPath(hdrPath);
ctl.setDataType(THREE.UnsignedByteType);

const hdrUrl = [
  `${src}/px.hdr`,
  `${src}/nx.hdr`,
  `${src}/py.hdr`,
  `${src}/ny.hdr`,
  `${src}/pz.hdr`,
  `${src}/nz.hdr`
];
const hdrSky = ctl.load(hdrUrl, tex => this.skies[src] = hdrSky );

// Then later...

obj.material.envMap = this.skies[this.skySources[index]];
obj.material.needsUpdate = true;

这是我的第二个示例的代码:

const ctl = new HDRCubeTextureLoader();
ctl.setPath(hdrPath);
ctl.setDataType(THREE.UnsignedByteType);

const hdrUrl = [
  `${src}/px.hdr`,
  `${src}/nx.hdr`,
  `${src}/py.hdr`,
  `${src}/ny.hdr`,
  `${src}/pz.hdr`,
  `${src}/nz.hdr`
];

const pmremGen = new PMREMGenerator(this.el.sceneEl.renderer);
pmremGen.compileCubemapShader();

const hdrSky = ctl.load(hdrUrl, tex => {
    const hdrRenderTarget = pmremGen.fromCubemap(hdrSky);
    this.skies[src] = hdrRenderTarget.texture;
});

// Then later...

obj.material.envMap = this.skies[this.skySources[index]];
obj.material.needsUpdate = true;

我似乎在过滤方面遇到了障碍。即使我在 PMREMGenerator.js 中显式更改过滤类型并打开 mipmap 生成,结果似乎也是一样的。这是一个使用 PMREMGenerator 没有任何问题的官方示例:https ://threejs.org/examples/webgl_materials_envmaps_hdr.html

作为结束语,我会注意到我们正在使用 Three.js r111(并且有一些原因我们不能完全切换它),所以我从最新版本的 Three 中引入了 PMREMGenerator 的当前版本。写作(r122 - 需要更高版本,因为 r111 的写法完全不同)。因此,如果这一切都是由版本之间的某些冲突引起的,我不会感到惊讶。

编辑:只需将生成的 envmap 作为标准地图放在某些平面上,令我惊讶的是,甚至没有一个模糊的 LODS 出现。这是我的样子:

损坏的 LODS

它应该是这样的(不要介意圆环结):

正确的 LODS

编辑:我现在找到了一种解决方法(本质上,不使用 PMREMGenerator),但如果发现解决方案,我会保留它。

标签: three.js3daframe

解决方案


我相信问题出在你在fromCubemap(). 现在您的代码没有使用tex传递给加载回调的 cubetexture 变量。尝试这个:

const hdrSky = ctl.load(hdrUrl, tex => {
    // use tex instead of hdrSky
    const hdrRenderTarget = pmremGen.fromCubemap(tex);
    this.skies[src] = hdrRenderTarget.texture;
});

推荐阅读