You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Three.js球体纹理映射报错:非2的幂图片问题解决

解决Three.js中“image is not power of two”报错及球体不显示问题

你碰到的问题其实是两个点共同导致的:非2的幂次纹理的兼容性限制,以及场景缺少光源导致Phong材质的球体无法被渲染出来。我来一步步帮你解决:

1. 关于“image is not power of two”的报错

你用的Three.js r93版本对非2的幂次(POT,比如2、4、8、16...1024这类尺寸)的纹理支持有局限,WebGL规范里,旧版浏览器对非POT纹理的处理有严格要求,这就是控制台抛出错误的原因。

你有两个修复选项:

  • 选项一:修改图片尺寸:把你的地球纹理改成最接近的2的幂次尺寸,比如1024x512,这是最稳妥的方案,能避免所有兼容性问题。
  • 选项二:配置纹理参数:如果不想改图片,直接给纹理添加以下配置,强制Three.js正确处理非POT纹理:
    material.map = THREE.ImageUtils.loadTexture("https://upload.wikimedia.org/wikipedia/commons/c/c4/Earthmap1000x500compac.jpg");
    // 新增配置
    material.map.wrapS = THREE.ClampToEdgeWrapping;
    material.map.wrapT = THREE.ClampToEdgeWrapping;
    material.map.generateMipmaps = false;
    
    另外提一句:THREE.ImageUtils在新版本Three.js里已经被废弃,后续升级的话建议改用THREE.TextureLoader(),用法是new THREE.TextureLoader().load(...)

2. 球体不显示的核心原因:缺少光源

你使用的MeshPhongMaterial是一种需要光照才能显示的材质——没有光源的话,它会完全呈现黑色,自然在画布上看不到任何内容。

给场景添加两个光源就能解决这个问题,在scene.add(earthMesh)之后插入这段代码:

// 环境光:照亮整个场景,避免暗部完全黑掉
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);

// 平行光:模拟太阳光,让球体产生明暗和纹理细节
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);

3. 额外优化:调整相机位置

你当前设置的camera.position.z = 10,而球体半径只有0.5,这样球体在画布上会非常小。建议改成camera.position.z = 3,和教程里的设置一致,这样地球的显示大小会更合适。

完整修改后的代码

把这些调整整合后,你的代码应该是这样的:

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script type="text/javascript">
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
window.addEventListener('resize', function() {
    var width = window.innerWidth;
    var height = window.innerHeight;
    renderer.setSize(width, height);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
})
controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.SphereGeometry(0.5, 32, 32)
var material = new THREE.MeshPhongMaterial();
var earthMesh = new THREE.Mesh(geometry, material)
scene.add(earthMesh)

// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 3, 5);
scene.add(directionalLight);

// 配置非POT纹理
material.map = THREE.ImageUtils.loadTexture("https://upload.wikimedia.org/wikipedia/commons/c/c4/Earthmap1000x500compac.jpg");
material.map.wrapS = THREE.ClampToEdgeWrapping;
material.map.wrapT = THREE.ClampToEdgeWrapping;
material.map.generateMipmaps = false;

camera.position.z = 3; // 调整相机位置

//game logic
var update = function() {
};
//draw scene
var render = function() {
    renderer.render(scene, camera);
}
// run game loop (update, render, repeat)
var GameLoop = function() {
    requestAnimationFrame(GameLoop);
    update();
    render();
}
GameLoop();
</script>

现在运行代码,你应该能看到可以拖拽旋转的地球纹理球体了。

内容的提问来源于stack exchange,提问作者Wai Yan Hein

火山引擎 最新活动