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

如何在React Three Fiber的InstancedMesh中正确使用Sprite实现星系背景?

问题描述

在基于NextJS与React Three Fiber的项目中,通过InstancedMesh渲染实例化球体实现了星空背景的Galaxy组件。为提升性能并更换视觉风格,尝试将球体替换为Sprite,但修改后场景无法渲染,Sprite未正常显示。目标是保留实例化渲染方式,使用Sprite实现星空效果。

原代码(球体版本)

function Galaxy(props:GalaxyProps){
  const meshRef = useRef<THREE.InstancedMesh>(null)
  const starsPos = useMemo<THREE.Vector3[]>(()=>{
    return generatePositions(props.count)
  }, [props.count])

  const starObject = useMemo(()=>new THREE.Object3D(), [])
  const material = useMemo(()=>new THREE.MeshBasicMaterial({color:"#ffff7f"}), [])
  const geometry = useMemo(()=>new THREE.SphereGeometry(1, 10, 5), [])

  useEffect(()=>{
    for (let i =0; i < starsPos.length; i++){
      starObject.position.copy(starsPos[i])
      starObject.updateMatrix()
      meshRef.current!.setMatrixAt(i, starObject.matrix)
    }
    meshRef.current!.instanceMatrix.needsUpdate = true
  },[starObject, starsPos])
  
  return <instancedMesh ref={meshRef} args={[geometry, material, starsPos.length]}
  ></instancedMesh>
}

修改后的代码(Sprite版本,未正常工作)

function Galaxy(props:GalaxyProps){
  const meshRef = useRef<THREE.InstancedMesh>(null)
  const starsPos = useMemo<THREE.Vector3[]>(()=>{
    return generatePositions(props.count)
  }, [props.count])

  const starObject = useMemo(()=>new THREE.Sprite(), [])
  const texture = useTexture('/images/star.png')
  const material = useMemo(()=>new THREE.SpriteMaterial({map: texture, color:'ffff7f'}), [texture])

  useEffect(()=>{
    for (let i =0; i < starsPos.length; i++){
      starObject.position.copy(starsPos[i])
      starObject.updateMatrix()
      meshRef.current!.setMatrixAt(i, starObject.matrix)
    }
    meshRef.current!.instanceMatrix.needsUpdate = true
  },[starObject, starsPos])
  
  return <instancedMesh ref={meshRef} args={[starObject.geometry, material, starsPos.length]}
  ></instancedMesh>
}
解决方案

以下是修正后的代码,解决了Sprite实例化不显示的问题:

function Galaxy(props: GalaxyProps) {
  const meshRef = useRef<THREE.InstancedMesh>(null);
  const starsPos = useMemo<THREE.Vector3[]>(() => generatePositions(props.count), [props.count]);
  // 获取场景相机,用于让星星始终朝向镜头
  const camera = useThree((state) => state.camera);

  // 使用PlaneGeometry替代Sprite内部几何体,InstancedMesh需要标准BufferGeometry
  const geometry = useMemo(() => new THREE.PlaneGeometry(1, 1), []);
  const texture = useTexture('/images/star.png');
  const material = useMemo(() => new THREE.SpriteMaterial({
    map: texture,
    color: '#ffff7f', // 补全颜色值的#前缀
    depthWrite: false, // 关闭深度写入,避免星星互相遮挡
    transparent: true // 启用透明,适配纹理的透明通道
  }), [texture]);

  const starObject = useMemo(() => new THREE.Object3D(), []);

  useEffect(() => {
    if (!meshRef.current) return;

    for (let i = 0; i < starsPos.length; i++) {
      starObject.position.copy(starsPos[i]);
      // 让每个星星实例朝向相机,模拟Sprite的自动朝向行为
      starObject.lookAt(camera.position);
      starObject.updateMatrix();
      meshRef.current!.setMatrixAt(i, starObject.matrix);
    }
    meshRef.current!.instanceMatrix.needsUpdate = true;
  }, [starObject, starsPos, camera]);

  return <instancedMesh ref={meshRef} args={[geometry, material, starsPos.length]} />;
}

关键修正点

  • 几何体替换:InstancedMesh无法兼容Sprite的内部几何体,改用PlaneGeometry(1,1)作为替代,这是Sprite的核心几何结构。
  • 颜色值修复:将color:'ffff7f'改为color:'#ffff7f',补全颜色值的标准前缀。
  • 相机朝向处理:Sprite默认自动面向相机,但InstancedMesh实例不会自动处理,因此通过lookAt(camera.position)让每个星星实例始终朝向镜头。
  • 渲染参数优化:添加depthWrite: false避免星星之间的遮挡问题,开启transparent: true适配带透明通道的星星纹理。
  • 纹理路径验证:确保/images/star.png放置在NextJS项目的public文件夹下,保证useTexture能正确加载资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 06:12:10