使用Three.js OrbitControls时如何保持物体颜色混合效果不变?
问题:固定矩形的顶点颜色不随相机旋转变化
我通过自定义顶点法线,让矩形面呈现多种颜色,但使用OrbitControls旋转相机时,颜色会随视角变化。期望无论如何旋转,颜色都保持固定。
原代码如下:
/* import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js'; */ import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js'; let camera, scene, renderer, controls; init(); render(); function init() { let width = $('#container').width(); let height = $('#container').height(); renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width,height); $("#container").html(renderer.domElement); camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); camera.position.z = 4; scene = new THREE.Scene(); const geometry = new THREE.BufferGeometry(); const vertices = [ -2, -1, 0, 2, -1, 0, 2, 1, 0, -2, 1, 0 ]; const indices = [ 0, 1, 2, // first triangle 2, 3, 0 // second triangle ]; const normals = [ 1,0,0, // bottom left 0,0,1, // bottom right 0,0,1, // top right 1,0,0 // top left ]; geometry.setIndex(indices); geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)); const material = new THREE.MeshNormalMaterial({vertexColors:true,flatShading:false}); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); controls = new OrbitControls( camera, renderer.domElement ); controls.target.set(0, 0, 0); controls.update(); animate(); } function animate() { requestAnimationFrame( animate ); controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true render(); } function render() { renderer.render(scene, camera); }
原因分析
MeshNormalMaterial的颜色是通过法线与相机视图方向的夹角计算生成的,法线会被转换到视图空间后再映射为颜色。当相机旋转时,法线与视图方向的相对角度改变,颜色自然会跟着变化。这不是法线被修改,而是材质的颜色计算逻辑依赖于视角。
解决方案
要实现固定颜色,需要直接定义顶点颜色,而非依赖法线的视角相关计算。具体步骤:
- 移除自定义法线属性,添加顶点颜色数组(RGB值范围0-1)
- 使用不依赖视角的材质(如
MeshBasicMaterial),并开启顶点颜色支持
修改后的代码
/* import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js'; */ import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js'; let camera, scene, renderer, controls; init(); render(); function init() { let width = $('#container').width(); let height = $('#container').height(); renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width,height); $("#container").html(renderer.domElement); camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10); camera.position.z = 4; scene = new THREE.Scene(); const geometry = new THREE.BufferGeometry(); const vertices = [ -2, -1, 0, 2, -1, 0, 2, 1, 0, -2, 1, 0 ]; const indices = [ 0, 1, 2, // first triangle 2, 3, 0 // second triangle ]; // 定义顶点颜色,对应原法线想要的颜色(RGB 0-1范围) const colors = [ 1, 0, 0, // 红色(对应原法线1,0,0) 0, 0, 1, // 蓝色(对应原法线0,0,1) 0, 0, 1, // 蓝色 1, 0, 0 // 红色 ]; geometry.setIndex(indices); geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); // 添加顶点颜色属性 // 使用MeshBasicMaterial,开启顶点颜色 const material = new THREE.MeshBasicMaterial({vertexColors: THREE.VertexColors}); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); controls = new OrbitControls( camera, renderer.domElement ); controls.target.set(0, 0, 0); controls.update(); animate(); } function animate() { requestAnimationFrame( animate ); controls.update(); render(); } function render() { renderer.render(scene, camera); }
内容的提问来源于stack exchange,提问作者pash_tranda
相关产品推荐
相关产品推荐

