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

