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

使用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的颜色是通过法线与相机视图方向的夹角计算生成的,法线会被转换到视图空间后再映射为颜色。当相机旋转时,法线与视图方向的相对角度改变,颜色自然会跟着变化。这不是法线被修改,而是材质的颜色计算逻辑依赖于视角。

解决方案

要实现固定颜色,需要直接定义顶点颜色,而非依赖法线的视角相关计算。具体步骤:

  1. 移除自定义法线属性,添加顶点颜色数组(RGB值范围0-1)
  2. 使用不依赖视角的材质(如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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:32:11