threejs使用纹理贴图时首次渲染一片黑的问题

一开始,我们的代码是这样写的:

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// 创建一个纹理加载器
const loader = new THREE.TextureLoader();
// 得到一个纹理对象
const texture = loader.load('./img/diqiu.png');

// 创建立方体
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshPhongMaterial({
  map: texture // 设置颜色贴图
});
// material.map = texture

// 使用形状和材质创建一个网格物体
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)

// 创建光
const light = new THREE.PointLight(0xffffff, 1, 0, 0);
light.position.set(10, 10, 10)
scene.add(light)

// 设置光源辅助对象
const pointLightHelper = new THREE.PointLightHelper(light, 1);
scene.add(pointLightHelper);

// 创建相机
let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(10, 10, 10)
camera.lookAt(0, 0, 0)

// 创建 三维坐标轴
const axesHelper = new THREE.AxesHelper(10);
// 将坐标轴加入到场景中
scene.add(axesHelper);


// 创建渲染对象
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 执行渲染
renderer.render(scene, camera);
document.body.appendChild(renderer.domElement);


// 创建轨道控制器对象
const controls = new OrbitControls(camera, renderer.domElement)
controls.addEventListener('change', function () {
  // 重新执行渲染操作
  renderer.render(scene, camera);
})

然后在浏览器中访问我们的页面,发现贴图并没有生效,而是一片黑,不过只要稍微动一下就出效果了,最开始我想难道是光源的问题?不过很快就被我否掉了,因为如果是光源的问题,不会稍微动一点点就出效果。我就感觉,可能是因为渲染的时候,贴图并没有生效。这可能就是创建贴图对象的时候,并没有成功,我这时候就猜测这个 load() 函数可能是个异步执行的,就是这句:

const texture = loader.load('./img/diqiu.png');

查看他们官方文档(https://threejs.org/docs/index.html#api/zh/loaders/TextureLoader),给的例子都是使用的回调函数,我感觉我的猜测可能没错,于是我随便写了点代码验证了一下:

console.log(111);
const texture = loader.load('./img/diqiu.png', function () {
  console.log(222);
});
console.log(333);

果然,猜的没错,执行结果是:111、333、222

既然问题出在这里,那么问题就比较好解决了,比如:将首次渲染操作写到回调函数中:

// 创建渲染对象
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);

// 创建一个纹理加载器
const loader = new THREE.TextureLoader();
// 得到一个纹理对象
const texture = loader.load('./img/diqiu.png', function () {
  // 执行渲染
  renderer.render(scene, camera);
  document.body.appendChild(renderer.domElement);
});

完整的代码可以参考:

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

// 创建渲染对象
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);

// 创建一个纹理加载器
const loader = new THREE.TextureLoader();
// 得到一个纹理对象
const texture = loader.load('./img/diqiu.png', function () {
  // 执行渲染
  renderer.render(scene, camera);
  document.body.appendChild(renderer.domElement);
});

// 创建立方体
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshPhongMaterial({
  map: texture // 设置颜色贴图
});
// material.map = texture

// 使用形状和材质创建一个网格物体
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)

// 创建光
const light = new THREE.PointLight(0xffffff, 1, 0, 0);
light.position.set(10, 10, 10)
scene.add(light)

// 设置光源辅助对象
const pointLightHelper = new THREE.PointLightHelper(light, 1);
scene.add(pointLightHelper);

// 创建相机
let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(10, 10, 10)
camera.lookAt(0, 0, 0)

// 创建 三维坐标轴
const axesHelper = new THREE.AxesHelper(10);
// 将坐标轴加入到场景中
scene.add(axesHelper);


// 创建轨道控制器对象
const controls = new OrbitControls(camera, renderer.domElement)
controls.addEventListener('change', function () {
  // 重新执行渲染操作
  renderer.render(scene, camera);
})


使用异步函数处理


其实纹理加载器还有个.loadAsync() 方法,它可以返回一个promise对象,因此我们可以使用 async和await去编写我们的代码:

// 得到一个纹理对象 使用异步函数
const texture = await loader.loadAsync('./img/diqiu.png');

完整点的代码可以是这样的:

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);

(async () => {
    // 创建一个纹理加载器
    const loader = new THREE.TextureLoader();
    // 得到一个纹理对象 使用异步函数
    const texture = await loader.loadAsync('./img/diqiu.png');
    // 创建立方体
    const geometry = new THREE.BoxGeometry(2, 2, 2);
    const material = new THREE.MeshPhongMaterial({
        map: texture // 设置颜色贴图
    });
    
    // material.map = texture
    
    // 使用形状和材质创建一个网格物体
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube)
    
    // 创建光
    const light = new THREE.PointLight(0xffffff, 1, 0, 0);
    light.position.set(10, 10, 10)
    scene.add(light)
    
    // 设置光源辅助对象
    const pointLightHelper = new THREE.PointLightHelper(light, 1);
    scene.add(pointLightHelper);
    
    // 创建相机
    let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(10, 10, 10)
    camera.lookAt(0, 0, 0)
    
    // 创建 三维坐标轴
    const axesHelper = new THREE.AxesHelper(10);
    // 将坐标轴加入到场景中
    scene.add(axesHelper);
    
    
    // 创建渲染对象
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    // 执行渲染
    renderer.render(scene, camera);
    document.body.appendChild(renderer.domElement);
    
    // 创建轨道控制器对象
    const controls = new OrbitControls(camera, renderer.domElement)
    controls.addEventListener('change', function () {
        // 重新执行渲染操作
        renderer.render(scene, camera);
    })

})()



微信 遇到疑问可加微信进行反映