Unity GLTF导出器材质/纹理嵌入异常问题排查
GLTF导出材质全白问题排查与解决
问题描述
测试GLTF导出器包时,将简单门窗对象拖入场景导出后,模型未显示对应材质,呈现全白状态。之前遇到过FBX导出器类似问题,通过重映射材质解决,想了解GLTF是否适用该方法或有其他解决方案。
导出前后对比
- 导出前:

- 导出后:

导出器核心代码片段
public GLTFSceneExporter(Transform[] rootTransforms, ExportOptions options) { _exportOptions = options; var metalGlossChannelSwapShader = Resources.Load("MetalGlossChannelSwap", typeof(Shader)) as Shader; _metalGlossChannelSwapMaterial = new Material(metalGlossChannelSwapShader); var normalChannelShader = Resources.Load("NormalChannel", typeof(Shader)) as Shader; _normalChannelMaterial = new Material(normalChannelShader); _rootTransforms = rootTransforms; _root = new GLTFRoot { Accessors = new List<Accessor>(), Asset = new Asset { Version = "2.0" }, Buffers = new List<GLTFBuffer>(), BufferViews = new List<BufferView>(), Cameras = new List<GLTFCamera>(), Images = new List<GLTFImage>(), Materials = new List<GLTFMaterial>(), Meshes = new List<GLTFMesh>(), Nodes = new List<Node>(), Samplers = new List<Sampler>(), Scenes = new List<GLTFScene>(), Textures = new List<GLTFTexture>() }; _imageInfos = new List<ImageInfo>(); _materials = new List<Material>(); _textures = new List<Texture>(); _buffer = new GLTFBuffer(); _bufferId = new BufferId { Id = _root.Buffers.Count, Root = _root }; _root.Buffers.Add(_buffer); } private void ExportNormalTexture(Texture2D texture, string outputPath) { var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); Graphics.Blit(texture, destRenderTexture, _normalChannelMaterial); var exportTexture = new Texture2D(texture.width, texture.height); exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); exportTexture.Apply(); var finalFilenamePath = ConstructImageFilenamePath(texture, outputPath); File.WriteAllBytes(finalFilenamePath, exportTexture.EncodeToPNG()); RenderTexture.ReleaseTemporary(destRenderTexture); if (Application.isEditor) { GameObject.DestroyImmediate(exportTexture); } else { GameObject.Destroy(exportTexture); } } private void ExportTexture(Texture2D texture, string outputPath) { var destRenderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB); Graphics.Blit(texture, destRenderTexture); var exportTexture = new Texture2D(texture.width, texture.height); exportTexture.ReadPixels(new Rect(0, 0, destRenderTexture.width, destRenderTexture.height), 0, 0); exportTexture.Apply(); var finalFilenamePath = ConstructImageFilenamePath(texture, outputPath); File.WriteAllBytes(finalFilenamePath, exportTexture.EncodeToPNG()); RenderTexture.ReleaseTemporary(destRenderTexture); if (Application.isEditor) { GameObject.DestroyImmediate(exportTexture); } else { GameObject.Destroy(exportTexture); } } private MaterialId ExportMaterial(Material materialObj) { MaterialId id = GetMaterialId(_root, materialObj); //if material has already been exported, return the existing id if (id != null) { return id; } //else create a new GLTF material var material = new GLTFMaterial(); if (ExportNames) { material.Name = materialObj.name; } //if the material has the property of cutoff, if (materialObj.HasProperty("_Cutoff")) { //extract the cutoff value from the material and apply it to the GLTF material material.AlphaCutoff = materialObj.GetFloat("_Cutoff"); } switch (materialObj.GetTag("RenderType", false, "")) { case "TransparentCutout": //if the tag is transparentcutout, the GLTF material alphamode is set to AlphaMode.mask, which will have a binary transparency affect material.AlphaMode = AlphaMode.MASK; material.Name = material.Name + "_C"; break; case "Transparent": //if the tag is transparent, the GLTF material alphamode is set to AlphaMode.blend, which will have smooth transparency (allows objects to be see thru) material.AlphaMode = AlphaMode.BLEND; material.Name = material.Name + "_T"; break; default: //if no tags match, default is opaque, which means nothing can be see thru material.AlphaMode = AlphaMode.OPAQUE; break; } material.DoubleSided = materialObj.HasProperty("_Cull") && materialObj.GetInt("_Cull") == (float)CullMode.Off; if (materialObj.HasProperty("_EmissionColor")) { material.EmissiveFactor = materialObj.GetColor("_EmissionColor").ToNumericsColorRaw(); } if (materialObj.HasProperty("_EmissionMap")) { var emissionTex = materialObj.GetTexture("_EmissionMap"); if (emissionTex != null) { if(emissionTex is Texture2D) { material.EmissiveTexture = ExportTextureInfo(emissionTex, TextureMapType.Emission); ExportTextureTransform(material.EmissiveTexture, materialObj, "_EmissionMap"); } else { Debug.LogErrorFormat("Can't export a {0} emissive texture in material {1}", emissionTex.GetType(), materialObj.name); } } } if (materialObj.HasProperty("_BumpMap")) { var normalTex = materialObj.GetTexture("_BumpMap"); if (normalTex != null) { if(normalTex is Texture2D) { material.NormalTexture = ExportNormalTextureInfo(normalTex, TextureMapType.Bump, materialObj); ExportTextureTransform(material.NormalTexture, materialObj, "_BumpMap"); } else { Debug.LogErrorFormat("Can't export a {0} normal texture in material {1}", normalTex.GetType(), materialObj.name); } } } if (materialObj.HasProperty("_OcclusionMap")) { var occTex = materialObj.GetTexture("_OcclusionMap"); if (occTex != null) { if(occTex is Texture2D) { material.OcclusionTexture = ExportOcclusionTextureInfo(occTex, TextureMapType.Occlusion, materialObj); ExportTextureTransform(material.OcclusionTexture, materialObj, "_OcclusionMap"); } else { Debug.LogErrorFormat("Can't export a {0} occlusion texture in material {1}", occTex.GetType(), materialObj.name); } } } if (IsPBRMetallicRoughness(materialObj)) { material.PbrMetallicRoughness = ExportPBRMetallicRoughness(materialObj); } else if (IsCommonConstant(materialObj)) { material.CommonConstant = ExportCommonConstant(materialObj); } _materials.Add(materialObj); id = new MaterialId { Id = _root.Materials.Count, Root = _root }; _root.Materials.Add(material); return id; }
解决方案
1. 修复材质类型覆盖逻辑
从代码看,导出器仅处理PBR金属粗糙度和通用常量两类材质,若你的门窗材质不属于这两类,会生成无基础颜色/纹理的GLTF材质,导致全白。
处理方式:
- 检查
IsPBRMetallicRoughness和IsCommonConstant的判定规则,确保覆盖你的材质类型; - 补充Unity标准材质的导出分支,示例代码:
else if (materialObj.shader.name == "Standard") { var pbr = new PbrMetallicRoughness(); // 映射Unity标准材质参数到GLTF PBR结构 pbr.BaseColorFactor = materialObj.GetColor("_Color").ToNumericsColorRaw(); if (materialObj.HasProperty("_MainTex")) { var mainTex = materialObj.GetTexture("_MainTex") as Texture2D; if (mainTex != null) { pbr.BaseColorTexture = ExportTextureInfo(mainTex, TextureMapType.BaseColor); ExportTextureTransform(pbr.BaseColorTexture, materialObj, "_MainTex"); } } // Unity的Glossiness与GLTF的Roughness反向,需转换 pbr.MetallicFactor = materialObj.GetFloat("_Metallic"); pbr.RoughnessFactor = 1 - materialObj.GetFloat("_Glossiness"); material.PbrMetallicRoughness = pbr; }
2. 验证纹理导出与引用
- 确认
ConstructImageFilenamePath生成的路径正确,纹理文件与GLTF文件目录匹配; - 检查
ExportTextureInfo是否正确将纹理关联到GLTF材质的对应属性(如基础颜色纹理)。
3. 材质重映射(兼容FBX解决方案思路)
GLTF同样支持材质重映射,导出前将非兼容材质替换为导出器支持的类型:
// 遍历所有渲染器替换材质 foreach (var renderer in rootTransforms.SelectMany(t => t.GetComponentsInChildren<Renderer>())) { List<Material> newMaterials = new List<Material>(); foreach (var mat in renderer.sharedMaterials) { if (!IsPBRMetallicRoughness(mat) && !IsCommonConstant(mat)) { // 创建临时PBR材质并复制原参数 Material tempMat = new Material(Shader.Find("Standard")); tempMat.CopyPropertiesFromMaterial(mat); newMaterials.Add(tempMat); } else { newMaterials.Add(mat); } } renderer.sharedMaterials = newMaterials.ToArray(); }
4. 调试验证
- 导出后打开GLTF文件,检查
materials节点是否包含pbrMetallicRoughness或commonConstant属性,以及textures引用是否有效; - 使用GLTF Viewer加载导出文件,查看控制台报错定位缺失资源或属性。
内容的提问来源于stack exchange,提问作者ferb
相关产品推荐
相关产品推荐

