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

React Three Fiber中Line几何体无法投射阴影如何解决

ThreeJS Line对象无法投射阴影问题解决

问题现象

  • 使用Mesh搭配BoxBufferGeometry时,开启castShadow、receiveShadow属性后阴影投射功能正常
  • 替换为Line搭配BufferGeometry绘制曲线时,无法产生任何阴影,且Line对象本身不直接暴露阴影相关配置属性
  • 问题相关核心代码如下:
    • App入口配置:
import React from 'react'
import { Canvas } from '@react-three/fiber'
import Curve from './components/Curve';
import Controls from './components/Controls';
import Plane from './components/Plane';
import * as THREE from 'three';


const App = () => {
    return (
        <div className="App">
            <Canvas shadows colorManagement camera={{position: [5,5,5]}} onCreated={({gl}) => {
                gl.shadowMap.enabled = true;
                gl.shadowMap.type = THREE.PCFShadowMap;
                gl.setPixelRatio(window.devicePixelRatio || 2);
            }}>
                <spotLight 
                    position={[0,70,0]}
                    color="white"
                    intensity={1.5}
                    castShadow 
                    shadowMapHeight={1024}
                    shadowMapWidth={1024}
                    shadowCameraNear={200}
                    shadowCameraFar={2000}
                    shadowBias={ - 0.00022} 
                    angle={0.01}
                />
                <ambientLight color="#f0f0f0" />
                <Controls />
                <Curve />
                <Plane />
                <gridHelper args={[30, 100, 'white', 'white']} position={[0,-3.99,0]} receiveShadow />
            </Canvas>
        </div>
    );
}

export default App;
  • 曲线组件(无法投射阴影的Line实现):
import React, { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';

const Curve = () => {
    const curve = new THREE.CubicBezierCurve3
        (
            new THREE.Vector3( -10, 0, 0 ),
            new THREE.Vector3( -5, 15, 0 ),
            new THREE.Vector3( 20, 15, 0 ),
            new THREE.Vector3( 10, 0, 0 ) 
        ); 

    const points = curve.getPoints( 50 );

    const ref = useRef()
    useLayoutEffect(() => {void ref.current.setFromPoints(points); }, [points])

    return (
    <line>
        <bufferGeometry ref={ref} />
        <lineBasicMaterial color={0xd2452b} />
      </line>

       
    );
};

export default Curve;
  • 接收阴影的平面组件:
import React from 'react';


const Plane = () => 
    <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0,-4,0]} receiveShadow>
        <planeGeometry 
            attach="geometry"
            args={[1024,1024]}
        />
        <shadowMaterial attach="material" color="#000000" opacity="1" />
    </mesh>

export default Plane;
  • 可正常投射阴影的Mesh参考实现:
import React, { useState } from 'react';
import { useSpring, a } from '@react-spring/three';

const Box = () => {

    const [hovered, setHovered] = useState(false);
    const [active, setActive] = useState(false);
    const props  = useSpring({
        scale: active ? 2 : 1,
        color: hovered ? 'red' : 'gray',
    });

    return (
        <a.mesh 
            onPointerOver={() => setHovered(true)}
            onPointerOut={() => setHovered(false)}
            onClick={() => setActive(!active)}
            scale={props.scale}
            castShadow
        >
            <boxBufferGeometry 
                attach="geometry"
                args={[1,1,1]}
            />
            <a.meshPhysicalMaterial attach="material" color={props.color} />
        </a.mesh>
    );
};

export default Box;

核心原因

ThreeJS原生的阴影映射系统,默认只会对由三角面组成的Mesh类对象做阴影计算,Line、Points这类非面渲染对象默认不参与阴影投射流程,所以就算手动给Line加上castShadow = true也不会生效。

解决方案

方案1:用网格细管替代原生Line(推荐,新手友好)

放弃原生无厚度的Line对象,用Mesh搭配TubeGeometry沿着曲线生成有极细厚度的管状网格,这种实现本质是普通Mesh,直接开启castShadow就能正常投射阴影,实现成本最低。
修改后的Curve组件代码:

import React, { useMemo } from 'react';
import * as THREE from 'three';

const Curve = () => {
    // 缓存曲线实例
    const curve = useMemo(() => new THREE.CubicBezierCurve3(
        new THREE.Vector3( -10, 0, 0 ),
        new THREE.Vector3( -5, 15, 0 ),
        new THREE.Vector3( 20, 15, 0 ),
        new THREE.Vector3( 10, 0, 0 ) 
    ), [])
    // 生成细管几何:参数分别为曲线、分段数、线条半径(粗细)、径向分段、是否闭合
    const tubeGeo = useMemo(() => new THREE.TubeGeometry(curve, 50, 0.05, 6, false), [curve])

    return (
        <mesh geometry={tubeGeo} castShadow>
            <meshStandardMaterial color={0xd2452b} />
        </mesh>
    );
};

export default Curve;

如果是在react-three-fiber生态下,也可以直接用@react-three/drei提供的Line组件,这个组件底层也是基于Mesh实现的带厚度线条,直接加castShadow属性即可生效。

方案2:自定义Line的阴影渲染逻辑(不推荐)

如果必须使用原生无厚度Line,需要手动重写Line对象的阴影深度写入方法,强制让Line参与阴影通道的渲染,这种方案需要修改ThreeJS底层渲染流程,兼容性差,新手不建议尝试。

额外配置修正

你当前代码里的聚光灯阴影相机参数配置错误:shadowCameraNear设置为200,但聚光灯位置在Y轴70单位处,曲线最高点距离聚光灯仅55单位,小于近裁剪面值,物体完全在阴影相机的可视范围外,就算换成Mesh也会出现阴影丢失问题,需要把shadowCameraNear改小到1~10区间即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:54:28