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

