You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js场景3D模型environment环境贴图设置后不生效问题

问题背景

我在基于WebGL的ThreeJS场景中导入glTF模型时,发现在线预览平台上环境参数对渲染效果影响极大:

  • 开启环境贴图的渲染效果:
    开启环境贴图的渲染效果
  • 未配置环境贴图的渲染效果:
    无环境贴图的渲染效果

最初的实现逻辑是加载JPG格式纹理作为环境贴图,再加载glTF模型,把纹理赋值给模型的scene.environment属性,核心代码如下:

import * as THREE from "three";
import { GLTFLoader } from "GLTFLoader";
// import { EXRLoader } from "EXRLoader";

export class AppController {

    constructor(app) {
        this.app = app;
    }
    
    animate = () => {
        requestAnimationFrame( this.animate );
        this.renderer.render( this.scene, this.camera );
    }
    
    init = () => {
        console.log('AppController.init()');
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

        this.scene.background = new THREE.Color( 0x00ffff );

        const light = new THREE.AmbientLight( 0x404040 ); // soft white light
        this.scene.add( light );

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
        this.renderer.outputEncoding = THREE.sRGBEncoding;
        document.body.appendChild( this.renderer.domElement );
        this.camera.position.z = 100;

        const fthis = this;
        new THREE.TextureLoader().load("./assets/images/env.jpg",
        function ( texture ) {
            fthis.scene.environment = texture;
            fthis.loadModel(texture);
        },
        undefined,
        function ( err ) {
            console.error( err );
        });
        this.animate();
    }
    
    loadModel = (texture) => {

        const loader = new GLTFLoader();
        const modelPath = 'assets/models/Pebble_Logo_Extrude.gltf';
        const fthis = this;
        loader.load( modelPath, function ( gltf ) {
            console.log('gltf', gltf);
            fthis.pebbleLogo = gltf;
            fthis.pebbleLogo.scene.environment = texture;
            fthis.pebbleLogo.scene.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    if(child.material !== null){
                        console.log("setting envmap");
                        child.material.envMap = texture;
                        child.material.metalness = 0.0;
                        child.material.envMapIntensity = 2.0;
                        child.material.needsUpdate = true;
                    }
                }
            });
            fthis.scene.add( fthis.pebbleLogo.scene );
        }, undefined, function ( error ) {
            console.error( error );
        } );
    }
}

代码运行后实际渲染效果如下,环境贴图完全没有生效:
代码实际运行渲染效果

之前看到旧文章提到需要用EXRLoader处理环境贴图,但文章发布时间较早,当前官方文档没有相关说明,不确定该方案是否适用。


解决方案

环境贴图不生效和EXRLoader没有直接关系,EXR只是高动态范围(HDR)纹理的一种格式,用JPG格式的低动态范围(LDR)全景图也能实现环境反射,问题核心是直接把普通2D等距柱状全景纹理赋值给环境属性的操作不符合Three.js的PBR渲染逻辑——glTF模型默认使用PBR材质,这类材质无法直接采样普通2D全景纹理做环境反射,必须使用预滤波处理后的环境立方体贴图。
修复只需要做3处调整:

  • 初始化渲染器之后,创建PMREMGenerator实例,专门用来把普通全景纹理转换成PBR材质可用的预滤波环境贴图
  • 纹理加载完成后,先通过PMREMGenerator处理成可用的环境贴图,再赋值给scene.environment,不需要手动遍历每个网格材质单独设置envMap,场景全局设置后所有PBR材质会自动应用环境贴图
  • 给加载的JPG环境纹理设置正确的颜色空间,避免颜色显示异常

修正后的核心代码如下:

init = () => {
    console.log('AppController.init()');
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

    this.scene.background = new THREE.Color( 0x00ffff );

    const light = new THREE.AmbientLight( 0x404040 );
    this.scene.add( light );

    this.renderer = new THREE.WebGLRenderer();
    this.renderer.setSize( window.innerWidth, window.innerHeight );
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.renderer.outputEncoding = THREE.sRGBEncoding;
    // 创建PMREM生成器
    this.pmremGenerator = new THREE.PMREMGenerator(this.renderer);
    document.body.appendChild( this.renderer.domElement );
    this.camera.position.z = 100;

    const fthis = this;
    new THREE.TextureLoader().load("./assets/images/env.jpg",
    function ( texture ) {
        // 设置纹理正确的颜色空间
        texture.colorSpace = THREE.SRGBColorSpace;
        // 将普通全景纹理转换为PBR可用的环境贴图
        const envMap = fthis.pmremGenerator.fromEquirectangular(texture).texture;
        // 原普通纹理使用完后释放内存
        texture.dispose();
        fthis.scene.environment = envMap;
        fthis.loadModel();
    },
    undefined,
    function ( err ) {
        console.error( err );
    });
    this.animate();
}

loadModel = () => {
    const loader = new GLTFLoader();
    const modelPath = 'assets/models/Pebble_Logo_Extrude.gltf';
    const fthis = this;
    loader.load( modelPath, function ( gltf ) {
        console.log('gltf', gltf);
        fthis.pebbleLogo = gltf;
        // 不需要重复给模型子场景设置environment,也不需要遍历材质手动赋值envMap
        // 模型加入场景后会自动继承scene的全局environment配置
        // 如需调整反射强度,可遍历材质单独设置envMapIntensity
        fthis.pebbleLogo.scene.traverse(function (child) {
            if (child instanceof THREE.Mesh && child.material) {
                child.material.envMapIntensity = 2.0;
                // 不要强制修改metalness参数,保留glTF模型自带的材质属性即可
            }
        });
        fthis.scene.add( fthis.pebbleLogo.scene );
    }, undefined, function ( error ) {
        console.error( error );
    } );
}

补充:如果后续要使用HDR/EXR格式的环境贴图,只需要把TextureLoader换成对应的EXRLoader/RGBELoader即可,后续的PMREM处理逻辑完全一致,不需要额外调整。


内容的提问来源于stack exchange,提问作者Lee Probert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:48:11