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

Three.js+TypeScript中MeshStandardMaterial的uTime uniform动画更新失败

问题修复:MeshStandardMaterial自定义Uniform动画更新报错

错误原因

onBeforeCompile回调并非在材质创建时立即执行,而是在该材质第一次被渲染到画布时才触发。动画循环会在页面加载后立刻启动,此时material.userData.shader还未被赋值,直接访问material.userData.shader.uniforms就会触发Cannot read properties of undefined错误。

修复方案

方案1:动画循环中添加存在性检查(快速修复)

在更新Uniform前,先判断shader实例是否已初始化,避免空指针报错:

const animate = (time: number) => {
  const timeDivided = time / 10000;
  // 先确认shader实例已存在
  if (material.userData.shader) {
    material.userData.shader.uniforms.uTime.value = timeDivided;
  }
  renderer.render(scene, camera);
};

方案2:独立管理Uniforms对象(推荐,更稳健)

不依赖onBeforeCompile的执行时机,自行维护Uniforms对象,避免异步初始化导致的问题:

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import vertexShaderParse from "./shaders/vertexParse.glsl";
import vertexMain from "./shaders/vertexMain.glsl";
import textureImage from "./assets/images/test-image-1.jpeg";

// 初始化渲染器、场景、相机等代码保持不变...

// 1. 提前创建自定义Uniforms对象,独立管理
const customUniforms = {
  uTime: { value: 0 },
  uRadius: { value: 0.8 },
  uTexture: { value: null }
};

// 2. 提前加载纹理,避免在onBeforeCompile中异步加载
const textureLoader = new THREE.TextureLoader();
textureLoader.load(textureImage, (texture) => {
  customUniforms.uTexture.value = texture;
});

const geometry = new THREE.IcosahedronGeometry(1, 250);
const material = new THREE.MeshStandardMaterial();
material.onBeforeCompile = (shader) => {
  // 3. 将自定义Uniforms合并到shader的Uniforms中
  Object.assign(shader.uniforms, customUniforms);

  // 原有着色器替换逻辑保持不变
  const parseVertexString = `#include <displacementmap_pars_vertex>`;
  shader.vertexShader = shader.vertexShader.replace(
    parseVertexString,
    parseVertexString + vertexShaderParse
  );

  const mainVertexString = `#include <displacementmap_vertex>`;
  shader.vertexShader = shader.vertexShader.replace(
    mainVertexString,
    mainVertexString + vertexMain
  );
};

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const animate = (time: number) => {
  const timeDivided = time / 10000;
  // 4. 直接更新自己维护的Uniforms,无需依赖shader实例
  customUniforms.uTime.value = timeDivided;
  renderer.render(scene, camera);
};
renderer.setAnimationLoop(animate);

额外GLSL代码修复

你提供的vertexMain.glsl中存在未定义变量desaturatedImage,需要补充灰度转换逻辑,否则GLSL编译会报错:

vec3 image = texture2D(uTexture, uv).xyz;
// 补充灰度转换定义
float desaturatedImage = dot(image, vec3(0.299, 0.587, 0.114));

vec3 coords = normal;
coords.y += uTime;
coords += desaturatedImage / 5.;
float wavePattern = getWave(coords);
vDisplacement = wavePattern;

// 后续代码保持不变...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 07:00:02