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

ThreeJS:不含纹理的glTF模型出现Shader UV未定义问题

问题根源:ThreeJS的内置材质着色器采用按需生成逻辑,只有当材质关联了需要UV采样的属性(如map、aoMap等)时,才会自动定义USE_UV宏,生成vUv可变变量的声明、顶点侧UV赋值、片元侧UV接收的完整逻辑。你在Blender中移除测试纹理后导出的glTF材质不携带任何纹理属性,ThreeJS解析后不会开启USE_UV宏,哪怕几何体存在UV属性,着色器中也不会生成vUv相关代码,因此你注入的代码引用vUv时就会报未定义错误。

解决方法

方案1:手动开启UV宏定义(最简洁)

直接给材质添加USE_UV宏强制开启UV逻辑,无需修改着色器注入代码:

gltf.scene.traverse(child => {
  if (child.isMesh && child.material) {
    child.material.defines = child.material.defines || {}
    // 开启UV宏,强制着色器生成vUv相关逻辑
    child.material.defines.USE_UV = ''
    // *必须设置needsUpdate才能让宏定义改动生效*
    child.material.needsUpdate = true
    // 后续绑定你的onBeforeCompile逻辑和动态纹理即可
  }
})

如果你的业务使用第二套UV集,对应添加USE_UV2 = ''宏即可。

方案2:手动注入vUv完整逻辑

如果你不想依赖ThreeJS的内置宏逻辑,可以在onBeforeCompile中自行补充vUv的完整声明和赋值逻辑,适配所有场景:

material.onBeforeCompile = shader => {
  // 顶点着色器补充vUv声明
  shader.vertexShader = `
    #ifndef USE_UV
    varying vec2 vUv;
    #endif
  ` + shader.vertexShader

  // 顶点着色器main函数中补充UV赋值
  shader.vertexShader = shader.vertexShader.replace(
    'void main() {',
    `void main() {
      #ifndef USE_UV
      vUv = uv;
      #endif
    `
  )

  // 片元着色器补充vUv声明
  shader.fragmentShader = `
    #ifndef USE_UV
    varying vec2 vUv;
    #endif
  ` + shader.fragmentShader

  // 你原有注入动态纹理、自定义着色器的逻辑照常编写
  shader.uniforms.dynamicTex = { value: yourCustomTexture }
}

方案3:加载时丢弃glTF自带纹理(适配性最高)

如果不想修改材质或着色器逻辑,可以保留Blender中的测试纹理导出,在ThreeJS加载完成后直接释放自带的测试纹理资源,不会出现重复加载或内存占用问题:

gltf.scene.traverse(child => {
  if (child.isMesh && child.material) {
    // 释放自带测试纹理的显存资源
    if (child.material.map) {
      child.material.map.dispose()
      child.material.map = null
    }
    // 你的动态纹理赋值、onBeforeCompile逻辑照常执行即可
  }
})

该方案下材质初始携带纹理属性,ThreeJS会自动开启USE_UV宏,不会出现vUv未定义错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 10:21:01