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

如何在Three.js(R3F)中让平行光照亮球体的完整半面?

问题描述

需要用两盏平行光照亮球体:一盏置于前方模拟太阳光照亮正面,另一盏置于后方模拟城市灯光照亮背面,计划通过图层控制光照对材质的作用,但目前两盏平行光无法分别照亮球体对应的完整半面。已尝试调整光照强度、缩放比例、光照距离等参数,问题仍未解决。

简化后的代码示例:

export default function Earth(): JSX.Element {
  const earthMapRef = useRef<any>();
  const frontLightRef = useRef<any>();
  const backLightRef = useRef<any>();

  useHelper(frontLightRef, DirectionalLightHelper, 1, 'red');
  useHelper(backLightRef, DirectionalLightHelper, 1, 'red');

  return (
    <Fragment>
      <PivotControls anchor={[0, 0, 0]} depthTest={false} scale={0.5} visible={true}>
        <mesh ref={earthMapRef} position={[0, 0, 0]}>
          <sphereGeometry args={[1, 64, 64]} />
          <meshStandardMaterial />
        </mesh>
      </PivotControls>

      <directionalLight
        ref={frontLightRef}
        position={[0, 0, 5]}
        scale={[3, 3, 3]}
        intensity={Math.PI}
        color="#FFFFFF"
      />

      <directionalLight
        ref={backLightRef}
        position={[0, 0, -5]}
        scale={[3, 3, 3]}
        intensity={Math.PI}
        color="#FFFFFF"
      />
    </Fragment>
  );
}

效果截图:
球体光照效果截图

解决方案

问题根源

  1. 平行光scale属性无效:DirectionalLight是无限远光源,不存在缩放属性,设置后不会改变光照范围。
  2. 未配置图层隔离:你提到用图层控制光照,但代码中未给灯光和球体分配对应图层,导致两盏灯默认作用于所有图层(图层0),同时照亮整个球体,无法实现半面独立光照。
  3. 单Mesh无法区分图层接收:单个球体的正反两面属于同一图层,无法让不同灯光仅照射其中一面。

解决方法

  1. 拆分球体为正反两个Mesh:分别创建仅显示正面和背面的球体,分配到不同图层:
    • 正面Mesh设置side: FrontSide,加入图层1
    • 背面Mesh设置side: BackSide,加入图层2
  2. 给灯光分配对应图层:让前灯仅作用于图层1,后灯仅作用于图层2
  3. 移除无效参数:删除平行光的scale属性

修改后的代码

import { Fragment, useRef } from 'react';
import { PivotControls, useHelper, mesh, sphereGeometry, meshStandardMaterial, directionalLight } from '@react-three/fiber';
import { DirectionalLightHelper } from 'three';

export default function Earth(): JSX.Element {
  const frontSphereRef = useRef<any>();
  const backSphereRef = useRef<any>();
  const frontLightRef = useRef<any>();
  const backLightRef = useRef<any>();

  useHelper(frontLightRef, DirectionalLightHelper, 1, 'red');
  useHelper(backLightRef, DirectionalLightHelper, 1, 'blue'); // 换颜色方便区分灯光

  return (
    <Fragment>
      <PivotControls anchor={[0, 0, 0]} depthTest={false} scale={0.5} visible={true}>
        {/* 正面球体:仅显示正面,归属于图层1 */}
        <mesh ref={frontSphereRef} position={[0, 0, 0]} layers={1}>
          <sphereGeometry args={[1, 64, 64]} />
          <meshStandardMaterial side={0} /> {/* FrontSide 对应数值0 */}
        </mesh>
        {/* 背面球体:仅显示背面,归属于图层2 */}
        <mesh ref={backSphereRef} position={[0, 0, 0]} layers={2}>
          <sphereGeometry args={[1, 64, 64]} />
          <meshStandardMaterial side={1} /> {/* BackSide 对应数值1 */}
        </mesh>
      </PivotControls>

      {/* 前方平行光:仅作用于图层1 */}
      <directionalLight
        ref={frontLightRef}
        position={[0, 0, 5]}
        intensity={Math.PI}
        color="#FFFFFF"
        layers={1}
      />

      {/* 后方平行光:仅作用于图层2 */}
      <directionalLight
        ref={backLightRef}
        position={[0, 0, -5]}
        intensity={Math.PI}
        color="#FFFFFF"
        layers={2}
      />
    </Fragment>
  );
}

补充说明

  • 若相机曾修改过图层设置,需确保开启图层1和2的可见性:camera.layers.enable(1)、camera.layers.enable(2)(默认相机显示所有图层,一般无需额外设置)
  • 若想避免创建两个Mesh,可以通过自定义着色器修改材质,让正反两面分别接收不同图层的灯光,但上述方法更简单易维护

内容的提问来源于stack exchange,提问作者Guillaume

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:58:14