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

React Three Fiber中Shader动画不渲染及自定义材质报错问题

问题与解决方案:旗帜动画不更新及自定义材质报错

问题1:uTime随时间更新但旗帜动画未生效

问题描述

编写了接收uFrequency和uTime参数的顶点着色器实现旗帜动画,逻辑正确,但uTime随时间更新时平面没有动画效果。

原因分析

  1. 时间更新机制不合理:用setInterval+useState每1秒更新一次时间,而useFrame是每帧执行,导致时间更新不及时;且useState更新异步,useFrame中获取的时间值不是最新的。
  2. 缺少片元着色器:原代码仅传入顶点着色器,没有片元着色器,物体无法正确渲染出颜色,也是动画看似失效的潜在原因。

解决代码修改

替换时间更新逻辑,改用THREE.Clock实时计算时间,并补充基础片元着色器:

import React, { useRef } from 'react'
import portalVertexShader from "../shaders/vertex.glsl"
import * as THREE from 'three';
import { useFrame } from 'react-three-fiber';
import { Plane } from '@react-three/drei';

export default function Flag() {
    const materialRef = useRef();
    const clock = useRef(new THREE.Clock());

    useFrame(() => {
        // 实时获取已流逝的时间(秒)
        const elapsedTime = clock.current.getElapsedTime();
        materialRef.current.uniforms.uTime.value = elapsedTime;
    }); 

    return (
        <Plane args={[1, 1, 32, 32]} rotation={[1.5, 0, 0]}>
            <shaderMaterial 
                ref={materialRef}
                side={THREE.DoubleSide}
                uniforms={{
                    uTime: { value: 0 },
                    uFrequency: { value: new THREE.Vector2(10, 20) }
                }} 
                vertexShader={portalVertexShader} 
                // 补充基础片元着色器让物体可见
                fragmentShader={`
                    void main() {
                        gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0);
                    }
                `}
            />
        </Plane>
    )
}

关键说明

  • 移除定时器和useState,用THREE.Clock在useFrame中实时计算时间,确保每帧都能获取最新的uTime值。
  • 片元着色器负责像素颜色输出,缺失会导致物体无法正常显示,必须补充。

问题2:自定义shaderMaterial报错TypeError: Class constructor material cannot be invoked without 'new'

问题描述

尝试通过shaderMaterial创建自定义材质组件并使用时,触发上述错误。

原因分析

  1. 未正确从@react-three/drei导入shaderMaterial和extend函数。
  2. 使用自定义材质组件时,未遵循React组件首字母大写的命名规范,导致被当作原生HTML标签处理。

解决代码修改

确保正确导入并规范使用自定义材质:

// 正确导入所需工具
import { shaderMaterial, extend } from '@react-three/drei';
import portalVertexShader from "../shaders/vertex.glsl"
import portalFragmentShader from "../shaders/fragment.glsl"
import * as THREE from 'three';

// 创建自定义材质
const PortalMaterial = shaderMaterial(
    {
        uTime: 0,
        uColorStart: new THREE.Color('#ffffff'),
        uColorEnd: new THREE.Color('#000000')
    },
    portalVertexShader,
    portalFragmentShader
);

// 扩展Three.js材质库
extend({ PortalMaterial });

// 组件中使用自定义材质
export default function Flag() {
    const materialRef = useRef();
    const clock = useRef(new THREE.Clock());

    useFrame(() => {
        const elapsedTime = clock.current.getElapsedTime();
        // 自定义材质可直接通过属性赋值uniforms
        materialRef.current.uTime = elapsedTime;
    }); 

    return (
        <Plane args={[1, 1, 32, 32]} rotation={[1.5, 0, 0]}>
            <PortalMaterial ref={materialRef} side={THREE.DoubleSide} />
        </Plane>
    )
}

关键说明

  • 必须从@react-three/drei导入shaderMaterial和extend,确保依赖正确。
  • 自定义材质组件名必须首字母大写,符合React组件命名规则。
  • 自定义材质的uniforms可直接通过组件实例属性赋值,无需手动访问uniforms对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 16:54:51