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

在Next.js中使用@react-three/fiber无法渲染Three.js线框对象

问题描述

我参考一个CodeSandbox示例,尝试在Next.js环境中使用Three.js结合@react-three/fiber创建可移动的线框平面,但适配到自己的环境后,对象无法渲染且未抛出任何报错。

我已尝试更换多种材质、添加灯光对象、改用mesh替代group、调整相机的不同配置,同时试过已废弃的'react-three-fiber'库和当前版本,但问题仍未解决。请问可能是什么原因?

我的代码

import React, { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import * as THREE from "three";

const size = 50;
const divisions = 50;
const halfsize = size / 2;
const vertices = new Float32Array((divisions + 1) * 12).map((v, i) => {
  const step = -halfsize + (Math.trunc(i / 12) * size) / divisions;
  switch (i % 12) {
    case 0:
      return -halfsize;
    case 1:
      return 0;
    case 2:
      return step;
    case 3:
      return halfsize;
    case 4:
      return 0;
    case 5:
      return step;
    case 6:
      return step;
    case 7:
      return 0;
    case 8:
      return -halfsize;
    case 9:
      return step;
    case 10:
      return 0;
    case 11:
      return halfsize;
    default:
      return null;
  }
});

const colors = new Float32Array((divisions + 1) * 12).map(() => 0.3);

const Obj = () => {
  const meshRef = useRef();

  useFrame(() => {
    if (meshRef.current) {
      meshRef.current.position.z =
        ((meshRef.current.position.z + 0.05) % 2) - 20;
      meshRef.current.position.y = -2;
      meshRef.current.rotation.y = 0; //Math.PI / 4
      meshRef.current.rotation.x = 0; //Math.PI / 8
      meshRef.current.rotation.z = 0; //Math.PI / 4
    }
  });

  return (
    <group ref={meshRef}>
      <lineSegments>
        <bufferGeometry attach="geometry">
          <bufferAttribute
            attachObject={["attributes", "position"]}
            count={vertices.length / 3}
            array={vertices}
            itemSize={3}
          />
        </bufferGeometry>
        <lineBasicMaterial attach="material" color="orange" />
      </lineSegments>
    </group>
  );
};
const SynthwaveScene = () => (
  <div className="canvas-container">
    <Canvas>
      <Obj />
    </Canvas>
  </div>
);

问题排查与解决

1. 顶点数组类型错误

new Float32Array(...).map()会返回普通JavaScript数组,而Three.js的BufferAttribute要求传入TypedArray(如Float32Array)作为array参数。直接使用map会导致类型不匹配,需改用循环直接操作Float32Array:

const vertices = useMemo(() => {
  const arr = new Float32Array((divisions + 1) * 12);
  for (let i = 0; i < arr.length; i++) {
    const step = -halfsize + (Math.trunc(i / 12) * size) / divisions;
    switch (i % 12) {
      case 0: arr[i] = -halfsize; break;
      case 1: arr[i] = 0; break;
      case 2: arr[i] = step; break;
      case 3: arr[i] = halfsize; break;
      case 4: arr[i] = 0; break;
      case 5: arr[i] = step; break;
      case 6: arr[i] = step; break;
      case 7: arr[i] = 0; break;
      case 8: arr[i] = -halfsize; break;
      case 9: arr[i] = step; break;
      case 10: arr[i] = 0; break;
      case 11: arr[i] = halfsize; break;
    }
  }
  return arr;
}, []);

2. 对象位置超出相机视野

useFrame中设置的position.z = ((...)-20)会让对象初始z轴位置约为-19.95,而R3F默认相机位置是(0,0,5)且看向(0,0,0),此时对象处于相机极远视野外,加上y=-2的偏移,会完全被视锥体裁切。

可以调整对象z轴偏移,或显式设置相机参数:

// 调整对象z轴位置,靠近相机
meshRef.current.position.z = ((meshRef.current.position.z + 0.05) % 2) - 5;

// 或在Canvas中设置相机
<Canvas camera={{ position: [0, 3, 10], fov: 60 }}>
  <Obj />
</Canvas>

3. BufferAttribute用法不规范

R3F中推荐直接通过Three.js API创建并缓存BufferGeometry,比JSX嵌套更可靠:

const geometry = useMemo(() => {
  const geo = new THREE.BufferGeometry();
  geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
  return geo;
}, [vertices]);

// 组件中使用
<lineSegments geometry={geometry}>
  <lineBasicMaterial color="orange" />
</lineSegments>

4. 容器无明确尺寸

如果canvas-container未设置宽高,Canvas会被渲染为0尺寸,导致看不到内容,需添加样式:

<div className="canvas-container" style={{ width: '100vw', height: '100vh' }}>
  <Canvas>...</Canvas>
</div>

修复后完整代码示例

import React, { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import * as THREE from "three";

const size = 50;
const divisions = 50;
const halfsize = size / 2;

const Obj = () => {
  const meshRef = useRef();

  const vertices = useMemo(() => {
    const arr = new Float32Array((divisions + 1) * 12);
    for (let i = 0; i < arr.length; i++) {
      const step = -halfsize + (Math.trunc(i / 12) * size) / divisions;
      switch (i % 12) {
        case 0: arr[i] = -halfsize; break;
        case 1: arr[i] = 0; break;
        case 2: arr[i] = step; break;
        case 3: arr[i] = halfsize; break;
        case 4: arr[i] = 0; break;
        case 5: arr[i] = step; break;
        case 6: arr[i] = step; break;
        case 7: arr[i] = 0; break;
        case 8: arr[i] = -halfsize; break;
        case 9: arr[i] = step; break;
        case 10: arr[i] = 0; break;
        case 11: arr[i] = halfsize; break;
      }
    }
    return arr;
  }, []);

  const geometry = useMemo(() => {
    const geo = new THREE.BufferGeometry();
    geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
    return geo;
  }, [vertices]);

  useFrame(() => {
    if (meshRef.current) {
      meshRef.current.position.z = ((meshRef.current.position.z + 0.05) % 2) - 5;
      meshRef.current.position.y = -2;
    }
  });

  return (
    <group ref={meshRef}>
      <lineSegments geometry={geometry}>
        <lineBasicMaterial color="orange" />
      </lineSegments>
    </group>
  );
};

const SynthwaveScene = () => (
  <div className="canvas-container" style={{ width: '100vw', height: '100vh' }}>
    <Canvas camera={{ position: [0, 3, 10], fov: 60 }}>
      <ambientLight intensity={0.5} />
      <Obj />
    </Canvas>
  </div>
);

export default SynthwaveScene;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 04:55:23