一行代码解决Three.js中只能在一侧看到物体的问题

项目场景:

因为该项目比较复杂庞大,在此就简单介绍一下:

通过Three.js创建若干个物体进行了组装,从而形成了一个类似眼球模拟模型的项目,用户可以通过拖动鼠标来达到控制视角(摄像机)的目的,以此来观察整个眼球状态。


Image1 Three.js眼球模型

注:下面所说的正视为从红线正轴往瞳孔(黑色圆形)看去的视角,左视为从蓝线正轴往负轴看去,右视则与其相反


问题描述

左视该眼球可以看到红色的圆形平面,但是左视则发现红色平面消失。


Image2 左视可以正常看到红色的圆形


Image3 右视发现红色圆形消失

创建外部白色球体、内部蓝色不规则球体、红色平面代码:

js 复制代码
    createSphere() {
      const radius = 2.2;
      const outerGeometry = new THREE.SphereGeometry(radius, 120, 120);
      const outerMaterial = new THREE.MeshPhongMaterial({
        color: 0xffffff, // 定义外部的球为白色
        transparent: true,
        opacity: 0.6, // 降低不透明度以减少反射
        metalness: 0.5,
        roughness: 0.3,
      });
      const outerSphere = new THREE.Mesh(outerGeometry, outerMaterial);

      // 创建一个具有水平曲面的不完全球体
      const waterSurfaceGeometry = new THREE.CircleGeometry(radius - 0.1)
      const innerGeometry = new THREE.SphereGeometry(
        radius - 0.1,
        240,
        240,
        0,
        2 * Math.PI,
        Math.PI,
        Math.PI / 2
      );
      const innerMaterial = new THREE.MeshPhongMaterial({
        color: 0x02c0f5, // 定义内部的球体为蓝色
        opacity: 1,
        metalness: 0.5,
        roughness: 0.3,
      });
      const waterSurfaceMaterial = new THREE.MeshPhongMaterial({
        color: 0xf60404, // 定义去曲面水面为红色
        opacity: 1,
        transparent:false, // 设置成不透明
        metalness: 0.5,
        roughness: 0.3,
      });
      const innerSphere = new THREE.Mesh(innerGeometry, innerMaterial);
      const waterSurface = new THREE.Mesh(waterSurfaceGeometry, waterSurfaceMaterial)
      innerSphere.scale.set(1, 1, 1); 
      innerSphere.add(waterSurface)

      this.outerSphere = outerSphere;
      this.innerSphere = innerSphere;

      const sphereGroup = new THREE.Group();
      sphereGroup.add(outerSphere);
      sphereGroup.add(innerSphere);
      this.sphere = sphereGroup; 
      this.innerSphere = innerSphere; 

      return sphereGroup;
    },

原因分析:

  1. 材质透明度问题:通过调整内部圆形的材质透明度,使其更透明,这样可以确保在摄像机视角不理想的情况下仍然能够看到内部。
  2. 光照效果:通过调整光照效果,可以改变内部圆形的明暗度,使其更加清晰可见。
  3. 内部圆形的尺寸:通过调整内部圆形的尺寸,使其在不同视角下都能够完整显示。
  4. 摄像机位置:确保摄像机的位置不会完全遮挡要显示的内容。可以尝试将摄像机向后移动或调整其位置,使其不会完全遮挡内部的圆形。

很明显,上面四种解决方案都不可行,首先红色平面的transparent属性为false,并且不透明度也为1;其次肯定不是光照问题,因为白色外部球体和蓝色内部球体都能正常显示;最后更不是摄像机位置问题,无论怎么调整方位都不能显示出红色圆形。


解决方案:

因此我们需要使用双面渲染 ,双面渲染能够确保从内部看到外部的表面。默认情况下,Three.js 只会渲染面的正面,通过启用 side: THREE.DoubleSide 可以使其渲染双面。

所以我们只需要在上面的代码中添加一行就能解决这个问题。

js 复制代码
      const waterSurfaceMaterial = new THREE.MeshPhongMaterial({
        color: 0xf60404, // 定义去曲面水面为红色
        opacity: 1,
        transparent:false, // 设置成不透明
        metalness: 0.5,
        roughness: 0.3,
        side: THREE.DoubleSide  // 允许双面渲染
      });

最后我们来看看效果!!


Image4 右视能够正常看到红色平面