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
相关产品推荐
相关产品推荐

