Refactoring Material Exporters

This commit is contained in:
Masataka SUMI
2022-11-03 02:01:47 +09:00
parent 18b9b1a285
commit 8af4eaecf5
32 changed files with 812 additions and 616 deletions

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UniGLTF
@@ -30,43 +31,23 @@ namespace UniGLTF
{
public virtual string GetGltfMaterialTypeFromUnityShaderName(string shaderName)
{
if (shaderName == "Standard")
if (BuiltinRPGltfMaterialExporter.SupportedShaderNames.Contains(shaderName))
{
return "pbr";
}
if (MaterialExporter.IsUnlit(shaderName))
{
return "unlit";
return "gltf";
}
return null;
}
public virtual IEnumerable<(string propertyName, Texture texture)> EnumerateTextureProperties(Material m)
{
// main color
yield return (MaterialExporter.COLOR_TEXTURE_PROP, m.GetTexture(MaterialExporter.COLOR_TEXTURE_PROP));
if (GetGltfMaterialTypeFromUnityShaderName(m.shader.name) == "unlit")
foreach (var texturePropertyName in m.GetTexturePropertyNames())
{
yield break;
}
// PBR
if (m.HasProperty(MaterialExporter.METALLIC_TEX_PROP))
{
yield return (MaterialExporter.METALLIC_TEX_PROP, m.GetTexture(MaterialExporter.METALLIC_TEX_PROP));
}
if (m.HasProperty(MaterialExporter.NORMAL_TEX_PROP))
{
yield return (MaterialExporter.NORMAL_TEX_PROP, m.GetTexture(MaterialExporter.NORMAL_TEX_PROP));
}
if (m.HasProperty(MaterialExporter.EMISSION_TEX_PROP))
{
yield return (MaterialExporter.EMISSION_TEX_PROP, m.GetTexture(MaterialExporter.EMISSION_TEX_PROP));
}
if (m.HasProperty(MaterialExporter.OCCLUSION_TEX_PROP))
{
yield return (MaterialExporter.OCCLUSION_TEX_PROP, m.GetTexture(MaterialExporter.OCCLUSION_TEX_PROP));
var tex = m.GetTexture(texturePropertyName);
if (tex != null)
{
yield return (texturePropertyName, tex);
}
}
}
}

View File

@@ -0,0 +1,22 @@
using UnityEngine;
using VRMShaders;
namespace UniGLTF
{
/// <summary>
/// 非対応のシェーダでも空のマテリアルを出力する.
/// </summary>
public static class BuiltinRPFallbackMaterialExporter
{
public static glTFMaterial ExportMaterial(Material src, ITextureExporter textureExporter)
{
var dst = new glTFMaterial
{
name = src.name,
pbrMetallicRoughness = new glTFPbrMetallicRoughness(),
};
return dst;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 983277a982364497b7491610ac4868f0
timeCreated: 1667405705

View File

@@ -0,0 +1,72 @@
using System;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace UniGLTF
{
/// <summary>
/// Built-in RP で一般的に用いられる Unlit Shader をエクスポートすることを試みる。
/// </summary>
public static class BuiltinRPGenericUnlitMaterialExporter
{
private const string ColorFactorPropertyName = "_Color";
private const string ColorTexturePropertyName = "_MainTex";
private const string CutoffPropertyName = "_Cutoff";
public static bool TryExportMaterial(Material src, glTFBlendMode blendMode, ITextureExporter textureExporter, out glTFMaterial dst)
{
dst = glTF_KHR_materials_unlit.CreateDefault();
dst.name = src.name;
ExportRenderingSettings(src, blendMode, dst);
ExportBaseColor(src, blendMode, textureExporter, dst);
return true;
}
private static void ExportRenderingSettings(Material src, glTFBlendMode blendMode, glTFMaterial dst)
{
switch (blendMode)
{
case glTFBlendMode.OPAQUE:
dst.alphaMode = glTFBlendMode.OPAQUE.ToString();
break;
case glTFBlendMode.MASK:
dst.alphaMode = glTFBlendMode.MASK.ToString();
dst.alphaCutoff = src.GetFloat(CutoffPropertyName);
break;
case glTFBlendMode.BLEND:
dst.alphaMode = glTFBlendMode.BLEND.ToString();
break;
default:
throw new ArgumentOutOfRangeException(nameof(blendMode), blendMode, null);
}
}
private static void ExportBaseColor(Material src, glTFBlendMode blendMode, ITextureExporter textureExporter, glTFMaterial dst)
{
if (src.HasProperty(ColorFactorPropertyName))
{
dst.pbrMetallicRoughness.baseColorFactor = src.GetColor(ColorFactorPropertyName).ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
}
if (src.HasProperty(ColorTexturePropertyName))
{
// Don't export alpha channel if material was OPAQUE
var unnecessaryAlpha = blendMode == glTFBlendMode.OPAQUE;
var index = textureExporter.RegisterExportingAsSRgb(src.GetTexture(ColorTexturePropertyName), !unnecessaryAlpha);
if (index != -1)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
{
index = index,
};
MaterialExportUtils.ExportTextureTransform(src, dst.pbrMetallicRoughness.baseColorTexture, ColorTexturePropertyName);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c4ed66ba3f44b28a1d303cdf073dc2b
timeCreated: 1667402621

View File

@@ -0,0 +1,46 @@
using UnityEngine;
using VRMShaders;
namespace UniGLTF
{
public class BuiltinRPGltfMaterialExporter : IMaterialExporter
{
public static readonly string[] SupportedShaderNames =
{
BuiltinRPStandardMaterialExporter.TargetShaderName,
BuiltinRPUniUnlitMaterialExporter.TargetShaderName,
"Unlit/Color",
"Unlit/Texture",
"Unlit/Transparent",
"Unlit/Transparent Cutout",
};
public virtual glTFMaterial ExportMaterial(Material m, ITextureExporter textureExporter, GltfExportSettings settings)
{
glTFMaterial dst;
switch (m.shader.name)
{
case BuiltinRPStandardMaterialExporter.TargetShaderName:
if (BuiltinRPStandardMaterialExporter.TryExportMaterial(m, textureExporter, out dst)) return dst;
break;
case BuiltinRPUniUnlitMaterialExporter.TargetShaderName:
if (BuiltinRPUniUnlitMaterialExporter.TryExportMaterial(m, textureExporter, out dst)) return dst;
break;
case "Unlit/Color":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(m, glTFBlendMode.OPAQUE, textureExporter, out dst)) return dst;
break;
case "Unlit/Texture":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(m, glTFBlendMode.OPAQUE, textureExporter, out dst)) return dst;
break;
case "Unlit/Transparent":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(m, glTFBlendMode.BLEND, textureExporter, out dst)) return dst;
break;
case "Unlit/Transparent Cutout":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(m, glTFBlendMode.MASK, textureExporter, out dst)) return dst;
break;
}
return BuiltinRPFallbackMaterialExporter.ExportMaterial(m, textureExporter);
}
}
}

View File

@@ -0,0 +1,215 @@
using System;
using System.Linq;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace UniGLTF
{
public static class BuiltinRPStandardMaterialExporter
{
public const string TargetShaderName = "Standard";
private const string ColorTexturePropertyName = "_MainTex";
private const string MetallicGlossTexturePropertyName = "_MetallicGlossMap";
private const string NormalTexturePropertyName = "_BumpMap";
private const string EmissionTexturePropertyName = "_EmissionMap";
private const string OcclusionTexturePropertyName = "_OcclusionMap";
public static bool TryExportMaterial(Material src, ITextureExporter textureExporter, out glTFMaterial dst)
{
if (src.shader.name != TargetShaderName)
{
dst = default;
return false;
}
dst = new glTFMaterial
{
name = src.name,
pbrMetallicRoughness = new glTFPbrMetallicRoughness(),
};
ExportRenderingSettings(src, dst);
ExportBaseColor(src, textureExporter, dst);
ExportEmission(src, textureExporter, dst);
ExportNormal(src, textureExporter, dst);
ExportOcclusionMetallicRoughness(src, textureExporter, dst);
return true;
}
private static void ExportRenderingSettings(Material src, glTFMaterial dst)
{
switch (src.GetTag("RenderType", true))
{
case "Transparent":
dst.alphaMode = glTFBlendMode.BLEND.ToString();
break;
case "TransparentCutout":
dst.alphaMode = glTFBlendMode.MASK.ToString();
dst.alphaCutoff = src.GetFloat("_Cutoff");
break;
default:
dst.alphaMode = glTFBlendMode.OPAQUE.ToString();
break;
}
}
private static void ExportBaseColor(Material src, ITextureExporter textureExporter, glTFMaterial dst)
{
if (src.HasProperty("_Color"))
{
dst.pbrMetallicRoughness.baseColorFactor = src.GetColor("_Color").ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
}
if (src.HasProperty(ColorTexturePropertyName))
{
// Don't export alpha channel if material was OPAQUE
var unnecessaryAlpha = string.Equals(dst.alphaMode, "OPAQUE", StringComparison.Ordinal);
var index = textureExporter.RegisterExportingAsSRgb(src.GetTexture(ColorTexturePropertyName), !unnecessaryAlpha);
if (index != -1)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
{
index = index,
};
ExportMainTextureTransform(src, dst.pbrMetallicRoughness.baseColorTexture);
}
}
}
/// <summary>
/// Occlusion, Metallic, Roughness
/// </summary>
private static void ExportOcclusionMetallicRoughness(Material src, ITextureExporter textureExporter, glTFMaterial dst)
{
Texture metallicSmoothTexture = default;
float smoothness = 1.0f;
var textuerNames = src.GetTexturePropertyNames();
if (textuerNames.Contains(MetallicGlossTexturePropertyName))
{
if (src.HasProperty("_GlossMapScale"))
{
smoothness = src.GetFloat("_GlossMapScale");
}
metallicSmoothTexture = src.GetTexture(MetallicGlossTexturePropertyName);
}
Texture occlusionTexture = default;
var occlusionStrength = 1.0f;
if (textuerNames.Contains(OcclusionTexturePropertyName))
{
occlusionTexture = src.GetTexture(OcclusionTexturePropertyName);
if (occlusionTexture != null && src.HasProperty("_OcclusionStrength"))
{
occlusionStrength = src.GetFloat("_OcclusionStrength");
}
}
int index = textureExporter.RegisterExportingAsCombinedGltfPbrParameterTextureFromUnityStandardTextures(metallicSmoothTexture, smoothness, occlusionTexture);
if (index != -1 && metallicSmoothTexture != null)
{
dst.pbrMetallicRoughness.metallicRoughnessTexture =
new glTFMaterialMetallicRoughnessTextureInfo()
{
index = index,
};
ExportMainTextureTransform(src, dst.pbrMetallicRoughness.metallicRoughnessTexture);
// Set 1.0f as hard-coded. See: https://github.com/dwango/UniVRM/issues/212.
dst.pbrMetallicRoughness.metallicFactor = 1.0f;
dst.pbrMetallicRoughness.roughnessFactor = 1.0f;
}
else
{
if (src.HasProperty("_Metallic"))
{
dst.pbrMetallicRoughness.metallicFactor = src.GetFloat("_Metallic");
}
if (src.HasProperty("_Glossiness"))
{
dst.pbrMetallicRoughness.roughnessFactor = 1.0f - src.GetFloat("_Glossiness");
}
}
if (index != -1 && occlusionTexture != null)
{
dst.occlusionTexture = new glTFMaterialOcclusionTextureInfo()
{
index = index,
strength = occlusionStrength,
};
ExportMainTextureTransform(src, dst.occlusionTexture);
}
}
private static void ExportNormal(Material src, ITextureExporter textureExporter, glTFMaterial dst)
{
if (src.HasProperty(NormalTexturePropertyName))
{
var index = textureExporter.RegisterExportingAsNormal(src.GetTexture(NormalTexturePropertyName));
if (index != -1)
{
dst.normalTexture = new glTFMaterialNormalTextureInfo()
{
index = index,
};
ExportMainTextureTransform(src, dst.normalTexture);
}
if (index != -1 && src.HasProperty("_BumpScale"))
{
dst.normalTexture.scale = src.GetFloat("_BumpScale");
}
}
}
private static void ExportEmission(Material src, ITextureExporter textureExporter, glTFMaterial dst)
{
if (src.IsKeywordEnabled("_EMISSION") == false)
{
return;
}
if (src.HasProperty("_EmissionColor"))
{
var color = src.GetColor("_EmissionColor");
if (color.maxColorComponent > 1)
{
var maxColorComponent = color.maxColorComponent;
color /= maxColorComponent;
UniGLTF.glTF_KHR_materials_emissive_strength.Serialize(ref dst.extensions, maxColorComponent);
}
dst.emissiveFactor = color.ToFloat3(ColorSpace.Linear, ColorSpace.Linear);
}
if (src.HasProperty(EmissionTexturePropertyName))
{
var index = textureExporter.RegisterExportingAsSRgb(src.GetTexture(EmissionTexturePropertyName), needsAlpha: false);
if (index != -1)
{
dst.emissiveTexture = new glTFMaterialEmissiveTextureInfo()
{
index = index,
};
ExportMainTextureTransform(src, dst.emissiveTexture);
}
}
}
private static void ExportMainTextureTransform(Material src, glTFTextureInfo targetTextureInfo)
{
MaterialExportUtils.ExportTextureTransform(src, targetTextureInfo, ColorTexturePropertyName);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec789661012740afbe8b5422aadc5fa0
timeCreated: 1667399599

View File

@@ -0,0 +1,83 @@
using System;
using UniGLTF.UniUnlit;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace UniGLTF
{
public static class BuiltinRPUniUnlitMaterialExporter
{
public const string TargetShaderName = UniUnlit.UniUnlitUtil.ShaderName;
public static bool TryExportMaterial(Material src, ITextureExporter textureExporter, out glTFMaterial dst)
{
if (src.shader.name != TargetShaderName)
{
dst = default;
return false;
}
dst = glTF_KHR_materials_unlit.CreateDefault();
dst.name = src.name;
ExportRenderingSettings(src, dst);
ExportBaseColor(src, textureExporter, dst);
return true;
}
private static void ExportRenderingSettings(Material src, glTFMaterial dst)
{
switch (UniUnlitUtil.GetRenderMode(src))
{
case UniUnlitRenderMode.Opaque:
dst.alphaMode = glTFBlendMode.OPAQUE.ToString();
break;
case UniUnlitRenderMode.Cutout:
dst.alphaMode = glTFBlendMode.MASK.ToString();
dst.alphaCutoff = src.GetFloat(UniUnlitUtil.PropNameCutoff);
break;
case UniUnlitRenderMode.Transparent:
dst.alphaMode = glTFBlendMode.BLEND.ToString();
break;
default:
throw new ArgumentOutOfRangeException();
}
switch (UniUnlitUtil.GetCullMode(src))
{
case UniUnlitCullMode.Off:
dst.doubleSided = true;
break;
case UniUnlitCullMode.Back:
dst.doubleSided = false;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private static void ExportBaseColor(Material src, ITextureExporter textureExporter, glTFMaterial dst)
{
if (src.HasProperty(UniUnlitUtil.PropNameColor))
{
dst.pbrMetallicRoughness.baseColorFactor = src.GetColor(UniUnlitUtil.PropNameColor).ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
}
if (src.HasProperty(UniUnlitUtil.PropNameMainTex))
{
var index = textureExporter.RegisterExportingAsSRgb(src.GetTexture(UniUnlitUtil.PropNameMainTex), UniUnlitUtil.GetRenderMode(src) != UniUnlitRenderMode.Opaque);
if (index != -1)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
{
index = index,
};
MaterialExportUtils.ExportTextureTransform(src, dst.pbrMetallicRoughness.baseColorTexture, UniUnlitUtil.PropNameMainTex);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4a8ebb5c42054bd7be905f2dcb595457
timeCreated: 1667403411

View File

@@ -0,0 +1,20 @@
using System;
using UnityEngine;
namespace UniGLTF
{
public static class MaterialExportUtils
{
public static void ExportTextureTransform(Material src, glTFTextureInfo dstTextureInfo, string targetTextureName)
{
if (dstTextureInfo != null && src.HasProperty(targetTextureName))
{
var offset = src.GetTextureOffset(targetTextureName);
var scale = src.GetTextureScale(targetTextureName);
(scale, offset) = TextureTransform.VerticalFlipScaleOffset(scale, offset);
glTF_KHR_texture_transform.Serialize(dstTextureInfo, (offset.x, offset.y), (scale.x, scale.y));
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4bbc9904e5fe4176b74d39eeb2318bd6
timeCreated: 1667400475

View File

@@ -1,339 +0,0 @@
using System;
using System.Linq;
using UniGLTF.UniUnlit;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace UniGLTF
{
public enum glTFBlendMode
{
OPAQUE,
MASK,
BLEND
}
public class MaterialExporter : IMaterialExporter
{
public virtual glTFMaterial ExportMaterial(Material m, ITextureExporter textureExporter, GltfExportSettings settings)
{
var material = CreateMaterial(m);
// common params
material.name = m.name;
Export_Color(m, textureExporter, material);
Export_Emission(m, textureExporter, material);
Export_Normal(m, textureExporter, material);
Export_OcclusionMetallicRoughness(m, textureExporter, material);
return material;
}
public const string COLOR_TEXTURE_PROP = "_MainTex";
public const string METALLIC_TEX_PROP = "_MetallicGlossMap";
public const string NORMAL_TEX_PROP = "_BumpMap";
public const string EMISSION_TEX_PROP = "_EmissionMap";
public const string OCCLUSION_TEX_PROP = "_OcclusionMap";
static void Export_Color(Material m, ITextureExporter textureManager, glTFMaterial material)
{
if (m.HasProperty("_Color"))
{
material.pbrMetallicRoughness.baseColorFactor = m.GetColor("_Color").ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
}
if (m.HasProperty(COLOR_TEXTURE_PROP))
{
// Don't export alpha channel if material was OPAQUE
var unnecessaryAlpha = string.Equals(material.alphaMode, "OPAQUE", StringComparison.Ordinal);
var index = textureManager.RegisterExportingAsSRgb(m.GetTexture(COLOR_TEXTURE_PROP), !unnecessaryAlpha);
if (index != -1)
{
material.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
{
index = index,
};
Export_MainTextureTransform(m, material.pbrMetallicRoughness.baseColorTexture);
}
}
}
/// <summary>
/// Occlusion, Metallic, Roughness
/// </summary>
/// <param name="m"></param>
/// <param name="textureExporter"></param>
/// <param name="material"></param>
static void Export_OcclusionMetallicRoughness(Material m, ITextureExporter textureExporter, glTFMaterial material)
{
Texture metallicSmoothTexture = default;
float smoothness = 1.0f;
var textuerNames = m.GetTexturePropertyNames();
if (textuerNames.Contains(METALLIC_TEX_PROP))
{
if (m.HasProperty("_GlossMapScale"))
{
smoothness = m.GetFloat("_GlossMapScale");
}
metallicSmoothTexture = m.GetTexture(METALLIC_TEX_PROP);
}
Texture occlusionTexture = default;
var occlusionStrength = 1.0f;
if (textuerNames.Contains(OCCLUSION_TEX_PROP))
{
occlusionTexture = m.GetTexture(OCCLUSION_TEX_PROP);
if (occlusionTexture != null && m.HasProperty("_OcclusionStrength"))
{
occlusionStrength = m.GetFloat("_OcclusionStrength");
}
}
int index = textureExporter.RegisterExportingAsCombinedGltfPbrParameterTextureFromUnityStandardTextures(metallicSmoothTexture, smoothness, occlusionTexture);
if (index != -1 && metallicSmoothTexture != null)
{
material.pbrMetallicRoughness.metallicRoughnessTexture =
new glTFMaterialMetallicRoughnessTextureInfo()
{
index = index,
};
Export_MainTextureTransform(m, material.pbrMetallicRoughness.metallicRoughnessTexture);
// Set 1.0f as hard-coded. See: https://github.com/dwango/UniVRM/issues/212.
material.pbrMetallicRoughness.metallicFactor = 1.0f;
material.pbrMetallicRoughness.roughnessFactor = 1.0f;
}
else
{
if (m.HasProperty("_Metallic"))
{
material.pbrMetallicRoughness.metallicFactor = m.GetFloat("_Metallic");
}
if (m.HasProperty("_Glossiness"))
{
material.pbrMetallicRoughness.roughnessFactor = 1.0f - m.GetFloat("_Glossiness");
}
}
if (index != -1 && occlusionTexture != null)
{
material.occlusionTexture = new glTFMaterialOcclusionTextureInfo()
{
index = index,
strength = occlusionStrength,
};
Export_MainTextureTransform(m, material.occlusionTexture);
}
}
static void Export_Normal(Material m, ITextureExporter textureExporter, glTFMaterial material)
{
if (m.HasProperty(NORMAL_TEX_PROP))
{
var index = textureExporter.RegisterExportingAsNormal(m.GetTexture(NORMAL_TEX_PROP));
if (index != -1)
{
material.normalTexture = new glTFMaterialNormalTextureInfo()
{
index = index,
};
Export_MainTextureTransform(m, material.normalTexture);
}
if (index != -1 && m.HasProperty("_BumpScale"))
{
material.normalTexture.scale = m.GetFloat("_BumpScale");
}
}
}
static void Export_Emission(Material m, ITextureExporter textureExporter, glTFMaterial material)
{
if (m.IsKeywordEnabled("_EMISSION") == false)
{
return;
}
if (m.HasProperty("_EmissionColor"))
{
var color = m.GetColor("_EmissionColor");
if (color.maxColorComponent > 1)
{
var maxColorComponent = color.maxColorComponent;
color /= maxColorComponent;
UniGLTF.glTF_KHR_materials_emissive_strength.Serialize(ref material.extensions, maxColorComponent);
}
material.emissiveFactor = color.ToFloat3(ColorSpace.Linear, ColorSpace.Linear);
}
if (m.HasProperty(EMISSION_TEX_PROP))
{
var index = textureExporter.RegisterExportingAsSRgb(m.GetTexture(EMISSION_TEX_PROP), needsAlpha: false);
if (index != -1)
{
material.emissiveTexture = new glTFMaterialEmissiveTextureInfo()
{
index = index,
};
Export_MainTextureTransform(m, material.emissiveTexture);
}
}
}
static void Export_MainTextureTransform(Material m, glTFTextureInfo textureInfo)
{
Export_TextureTransform(m, textureInfo, COLOR_TEXTURE_PROP);
}
static void Export_TextureTransform(Material m, glTFTextureInfo textureInfo, string propertyName)
{
if (textureInfo != null && m.HasProperty(propertyName))
{
var offset = m.GetTextureOffset(propertyName);
var scale = m.GetTextureScale(propertyName);
(scale, offset) = TextureTransform.VerticalFlipScaleOffset(scale, offset);
glTF_KHR_texture_transform.Serialize(textureInfo, (offset.x, offset.y), (scale.x, scale.y));
}
}
public static bool IsUnlit(string shaderName)
{
switch (shaderName)
{
case "Unlit/Color":
case "Unlit/Texture":
case "Unlit/Transparent":
case "Unlit/Transparent Cutout":
case UniUnlit.UniUnlitUtil.ShaderName:
return true;
default:
return false;
}
}
protected virtual glTFMaterial CreateMaterial(Material m)
{
switch (m.shader.name)
{
case "Unlit/Color":
return Export_UnlitColor(m);
case "Unlit/Texture":
return Export_UnlitTexture(m);
case "Unlit/Transparent":
return Export_UnlitTransparent(m);
case "Unlit/Transparent Cutout":
return Export_UnlitCutout(m);
case UniUnlit.UniUnlitUtil.ShaderName:
return Export_UniUnlit(m);
default:
return Export_Standard(m);
}
}
static glTFMaterial Export_UnlitColor(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
return material;
}
static glTFMaterial Export_UnlitTexture(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
return material;
}
static glTFMaterial Export_UnlitTransparent(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = glTFBlendMode.BLEND.ToString();
return material;
}
static glTFMaterial Export_UnlitCutout(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = glTFBlendMode.MASK.ToString();
material.alphaCutoff = m.GetFloat("_Cutoff");
return material;
}
private glTFMaterial Export_UniUnlit(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
var renderMode = UniUnlit.UniUnlitUtil.GetRenderMode(m);
if (renderMode == UniUnlitRenderMode.Opaque)
{
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
}
else if (renderMode == UniUnlitRenderMode.Transparent)
{
material.alphaMode = glTFBlendMode.BLEND.ToString();
}
else if (renderMode == UniUnlitRenderMode.Cutout)
{
material.alphaMode = glTFBlendMode.MASK.ToString();
material.alphaCutoff = m.GetFloat("_Cutoff");
}
else
{
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
}
var cullMode = UniUnlit.UniUnlitUtil.GetCullMode(m);
if (cullMode == UniUnlitCullMode.Off)
{
material.doubleSided = true;
}
else
{
material.doubleSided = false;
}
return material;
}
static glTFMaterial Export_Standard(Material m)
{
var material = new glTFMaterial
{
pbrMetallicRoughness = new glTFPbrMetallicRoughness(),
};
switch (m.GetTag("RenderType", true))
{
case "Transparent":
material.alphaMode = glTFBlendMode.BLEND.ToString();
break;
case "TransparentCutout":
material.alphaMode = glTFBlendMode.MASK.ToString();
material.alphaCutoff = m.GetFloat("_Cutoff");
break;
default:
material.alphaMode = glTFBlendMode.OPAQUE.ToString();
break;
}
return material;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace UniGLTF
{
public enum glTFBlendMode
{
OPAQUE,
MASK,
BLEND
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59e2e94f37164b3c87324eea9ba98006
timeCreated: 1667402917

View File

@@ -48,7 +48,7 @@ namespace UniGLTF
protected virtual IMaterialExporter CreateMaterialExporter()
{
return new MaterialExporter();
return new BuiltinRPGltfMaterialExporter();
}
protected ITextureExporter TextureExporter => _textureExporter;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRM
@@ -10,10 +11,9 @@ namespace VRM
{
public override string GetGltfMaterialTypeFromUnityShaderName(string shaderName)
{
var name = VRMMaterialExporter.VrmMaterialName(shaderName);
if (!string.IsNullOrEmpty(name))
if (BuiltinRPVrmMaterialExporter.SupportedShaderNames.Contains(shaderName))
{
return name;
return "VRM0X";
}
return base.GetGltfMaterialTypeFromUnityShaderName(shaderName);
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c0d00129f7e844259bb416832e692737
timeCreated: 1667405900

View File

@@ -0,0 +1,124 @@
using System;
using MToon;
using UniGLTF;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
using RenderMode = MToon.RenderMode;
namespace VRM
{
/// <summary>
/// VRM/MToon のマテリアル情報をエクスポートする。
/// ただし VRM 0.x としては VRM extension 内の materialProperties に記録されているデータが正である。
/// したがって、ここで出力するデータはあくまで VRM を表示できない glTF ビューワでの見た目をある程度保証するために作成するものである。
/// </summary>
public static class BuiltinRPVrmMToonMaterialExporter
{
public const string TargetShaderName = MToon.Utils.ShaderName;
public static bool TryExportMaterial(Material src, ITextureExporter textureExporter, out glTFMaterial dst)
{
if (src.shader.name != TargetShaderName)
{
dst = default;
return false;
}
var srcProps = MToon.Utils.GetMToonParametersFromMaterial(src);
dst = glTF_KHR_materials_unlit.CreateDefault();
dst.name = src.name;
ExportRenderingSettings(srcProps, dst);
ExportBaseColor(src, srcProps, textureExporter, dst);
ExportEmission(src, srcProps, textureExporter, dst);
return true;
}
private static void ExportRenderingSettings(MToonDefinition src, glTFMaterial dst)
{
switch (src.Rendering.RenderMode)
{
case RenderMode.Opaque:
dst.alphaMode = glTFBlendMode.OPAQUE.ToString();
break;
case RenderMode.Cutout:
dst.alphaMode = glTFBlendMode.MASK.ToString();
dst.alphaCutoff = src.Color.CutoutThresholdValue;
break;
case RenderMode.Transparent:
dst.alphaMode = glTFBlendMode.BLEND.ToString();
break;
case RenderMode.TransparentWithZWrite:
// NOTE: Ambiguous but better.
dst.alphaMode = glTFBlendMode.BLEND.ToString();
break;
default:
throw new ArgumentOutOfRangeException();
}
switch (src.Rendering.CullMode)
{
case CullMode.Off:
dst.doubleSided = true;
break;
case CullMode.Front:
// NOTE: Ambiguous but better.
dst.doubleSided = true;
break;
case CullMode.Back:
dst.doubleSided = false;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private static void ExportBaseColor(Material srcMaterial, MToonDefinition src, ITextureExporter textureExporter, glTFMaterial dst)
{
dst.pbrMetallicRoughness.baseColorFactor = src.Color.LitColor.ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
if (src.Color.LitMultiplyTexture != null)
{
var index = textureExporter.RegisterExportingAsSRgb(src.Color.LitMultiplyTexture, src.Rendering.RenderMode != RenderMode.Opaque);
if (index != -1)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
{
index = index,
};
ExportMainTextureTransform(srcMaterial, dst.pbrMetallicRoughness.baseColorTexture);
}
}
}
private static void ExportEmission(Material srcMaterial, MToonDefinition src, ITextureExporter textureExporter, glTFMaterial dst)
{
var emissionFactor = src.Emission.EmissionColor;
if (emissionFactor.maxColorComponent > 1)
{
emissionFactor /= emissionFactor.maxColorComponent;
}
dst.emissiveFactor = emissionFactor.ToFloat3(ColorSpace.Linear, ColorSpace.Linear);
if (src.Emission.EmissionMultiplyTexture != null)
{
var index = textureExporter.RegisterExportingAsSRgb(src.Emission.EmissionMultiplyTexture, needsAlpha: false);
if (index != -1)
{
dst.emissiveTexture = new glTFMaterialEmissiveTextureInfo()
{
index = index,
};
ExportMainTextureTransform(srcMaterial, dst.emissiveTexture);
}
}
}
private static void ExportMainTextureTransform(Material src, glTFTextureInfo targetTextureInfo)
{
MaterialExportUtils.ExportTextureTransform(src, targetTextureInfo, MToon.Utils.PropMainTex);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1afab05a32ff435082b4ae879730886d
timeCreated: 1667405966

View File

@@ -0,0 +1,44 @@
using System;
using UniGLTF;
using UnityEngine;
using VRMShaders;
namespace VRM
{
public class BuiltinRPVrmMaterialExporter : BuiltinRPGltfMaterialExporter
{
public static readonly string[] SupportedShaderNames =
{
BuiltinRPVrmMToonMaterialExporter.TargetShaderName,
"VRM/UnlitTexture",
"VRM/UnlitTransparent",
"VRM/UnlitCutout",
"VRM/UnlitTransparentZWrite",
};
public override glTFMaterial ExportMaterial(Material src, ITextureExporter textureExporter, GltfExportSettings settings)
{
glTFMaterial dst = default;
switch (src.shader.name)
{
case BuiltinRPVrmMToonMaterialExporter.TargetShaderName:
if (BuiltinRPVrmMToonMaterialExporter.TryExportMaterial(src, textureExporter, out dst)) return dst;
break;
case "VRM/UnlitTexture":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(src, glTFBlendMode.OPAQUE, textureExporter, out dst)) return dst;
break;
case "VRM/UnlitTransparent":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(src, glTFBlendMode.BLEND, textureExporter, out dst)) return dst;
break;
case "VRM/UnlitCutout":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(src, glTFBlendMode.MASK, textureExporter, out dst)) return dst;
break;
case "VRM/UnlitTransparentZWrite":
if (BuiltinRPGenericUnlitMaterialExporter.TryExportMaterial(src, glTFBlendMode.BLEND, textureExporter, out dst)) return dst;
break;
}
return base.ExportMaterial(src, textureExporter, settings);
}
}
}

View File

@@ -0,0 +1,130 @@
using System;
using UniGLTF;
using UniGLTF.ShaderPropExporter;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace VRM
{
/// <summary>
/// VRM/MToon のマテリアル情報をエクスポートする。
/// VRM extension 内の materialProperties に記録するデータを用意する。
/// </summary>
public static class VrmExtensionMaterialPropertyExporter
{
private static readonly string[] ExportingTags =
{
"RenderType",
// "Queue",
};
public static glTF_VRM_Material ExportMaterial(Material m, ITextureExporter textureExporter)
{
var material = new glTF_VRM_Material
{
name = m.name,
shader = m.shader.name,
renderQueue = m.renderQueue,
};
if (m.shader.name != MToon.Utils.ShaderName)
{
material.shader = glTF_VRM_Material.VRM_USE_GLTFSHADER;
return material;
}
var prop = PreShaderPropExporter.GetPropsForMToon();
if (prop == null)
{
throw new Exception("arienai");
}
else
{
foreach (var keyword in m.shaderKeywords)
{
material.keywordMap.Add(keyword, m.IsKeywordEnabled(keyword));
}
// get properties
//material.SetProp(prop);
foreach (var kv in prop.Properties)
{
switch (kv.ShaderPropertyType)
{
case ShaderPropertyType.Color:
{
// No color conversion. Because color property is serialized to raw float array.
var value = m.GetColor(kv.Key).ToFloat4(ColorSpace.Linear, ColorSpace.Linear);
material.vectorProperties.Add(kv.Key, value);
}
break;
case ShaderPropertyType.Range:
case ShaderPropertyType.Float:
{
var value = m.GetFloat(kv.Key);
material.floatProperties.Add(kv.Key, value);
}
break;
case ShaderPropertyType.TexEnv:
{
var texture = m.GetTexture(kv.Key);
if (texture != null)
{
var value = -1;
var isNormalMap = kv.Key == "_BumpMap";
if (isNormalMap)
{
value = textureExporter.RegisterExportingAsNormal(texture);
}
else
{
var needsAlpha = kv.Key == "_MainTex";
value = textureExporter.RegisterExportingAsSRgb(texture, needsAlpha);
}
if (value == -1)
{
Debug.LogFormat("not found {0}", texture.name);
}
else
{
material.textureProperties.Add(kv.Key, value);
}
}
// offset & scaling
var offset = m.GetTextureOffset(kv.Key);
var scaling = m.GetTextureScale(kv.Key);
material.vectorProperties.Add(kv.Key,
new float[] { offset.x, offset.y, scaling.x, scaling.y });
}
break;
case ShaderPropertyType.Vector:
{
var value = m.GetVector(kv.Key).ToArray();
material.vectorProperties.Add(kv.Key, value);
}
break;
default:
throw new NotImplementedException();
}
}
}
foreach (var tag in ExportingTags)
{
var value = m.GetTag(tag, false);
if (!String.IsNullOrEmpty(value))
{
material.tagMap.Add(tag, value);
}
}
return material;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 656dd5e643384d7489070cc3759d2f45
timeCreated: 1667405286

View File

@@ -43,7 +43,7 @@ namespace VRM
protected override IMaterialExporter CreateMaterialExporter()
{
return new VRMMaterialExporter();
return new BuiltinRPVrmMaterialExporter();
}
public override void ExportExtensions(ITextureSerializer textureSerializer)
@@ -216,7 +216,7 @@ namespace VRM
// materials
foreach (var m in Materials)
{
VRM.materialProperties.Add(VRMMaterialExporter.CreateFromMaterial(m, TextureExporter));
VRM.materialProperties.Add(VrmExtensionMaterialPropertyExporter.ExportMaterial(m, TextureExporter));
}
// Serialize VRM

View File

@@ -1,237 +0,0 @@
using System;
using System.Linq;
using UniGLTF;
using UniGLTF.ShaderPropExporter;
using UnityEngine;
using VRMShaders;
using ColorSpace = VRMShaders.ColorSpace;
namespace VRM
{
public class VRMMaterialExporter : MaterialExporter
{
public static string VrmMaterialName(string shaderName)
{
switch (shaderName)
{
case "VRM/UnlitTexture":
case "VRM/UnlitTransparent":
case "VRM/UnlitCutout":
case "VRM/UnlitTransparentZWrite":
return "KHR_materials_unlit";
case "VRM/MToon":
return "MToon";
default:
return null;
}
}
protected override glTFMaterial CreateMaterial(Material m)
{
switch (m.shader.name)
{
case "VRM/UnlitTexture":
return Export_VRMUnlitTexture(m);
case "VRM/UnlitTransparent":
return Export_VRMUnlitTransparent(m);
case "VRM/UnlitCutout":
return Export_VRMUnlitCutout(m);
case "VRM/UnlitTransparentZWrite":
return Export_VRMUnlitTransparentZWrite(m);
case "VRM/MToon":
return Export_VRMMToon(m);
default:
return base.CreateMaterial(m);
}
}
static glTFMaterial Export_VRMUnlitTexture(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = "OPAQUE";
return material;
}
static glTFMaterial Export_VRMUnlitTransparent(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = "BLEND";
return material;
}
static glTFMaterial Export_VRMUnlitCutout(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = "MASK";
return material;
}
static glTFMaterial Export_VRMUnlitTransparentZWrite(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
material.alphaMode = "BLEND";
return material;
}
static glTFMaterial Export_VRMMToon(Material m)
{
var material = glTF_KHR_materials_unlit.CreateDefault();
switch (m.GetTag("RenderType", true))
{
case "Transparent":
material.alphaMode = "BLEND";
break;
case "TransparentCutout":
material.alphaMode = "MASK";
material.alphaCutoff = m.GetFloat("_Cutoff");
break;
default:
material.alphaMode = "OPAQUE";
break;
}
switch ((int)m.GetFloat("_CullMode"))
{
case 0:
material.doubleSided = true;
break;
case 1:
Debug.LogWarning("ignore cull front");
break;
case 2:
// cull back
break;
default:
throw new NotImplementedException();
}
return material;
}
#region CreateFromMaterial
static readonly string[] TAGS = new string[]{
"RenderType",
// "Queue",
};
public static glTF_VRM_Material CreateFromMaterial(Material m, ITextureExporter textureExporter)
{
var material = new glTF_VRM_Material
{
name = m.name,
shader = m.shader.name,
renderQueue = m.renderQueue,
};
if (m.shader.name != MToon.Utils.ShaderName)
{
material.shader = glTF_VRM_Material.VRM_USE_GLTFSHADER;
return material;
}
var prop = PreShaderPropExporter.GetPropsForMToon();
if (prop == null)
{
throw new Exception("arienai");
}
else
{
foreach (var keyword in m.shaderKeywords)
{
material.keywordMap.Add(keyword, m.IsKeywordEnabled(keyword));
}
// get properties
//material.SetProp(prop);
foreach (var kv in prop.Properties)
{
switch (kv.ShaderPropertyType)
{
case ShaderPropertyType.Color:
{
// No color conversion. Because color property is serialized to raw float array.
var value = m.GetColor(kv.Key).ToFloat4(ColorSpace.Linear, ColorSpace.Linear);
material.vectorProperties.Add(kv.Key, value);
}
break;
case ShaderPropertyType.Range:
case ShaderPropertyType.Float:
{
var value = m.GetFloat(kv.Key);
material.floatProperties.Add(kv.Key, value);
}
break;
case ShaderPropertyType.TexEnv:
{
var texture = m.GetTexture(kv.Key);
if (texture != null)
{
var value = -1;
var isNormalMap = kv.Key == "_BumpMap";
if (isNormalMap)
{
value = textureExporter.RegisterExportingAsNormal(texture);
}
else
{
var needsAlpha = kv.Key == "_MainTex";
value = textureExporter.RegisterExportingAsSRgb(texture, needsAlpha);
}
if (value == -1)
{
Debug.LogFormat("not found {0}", texture.name);
}
else
{
material.textureProperties.Add(kv.Key, value);
}
}
// offset & scaling
var offset = m.GetTextureOffset(kv.Key);
var scaling = m.GetTextureScale(kv.Key);
material.vectorProperties.Add(kv.Key,
new float[] { offset.x, offset.y, scaling.x, scaling.y });
}
break;
case ShaderPropertyType.Vector:
{
var value = m.GetVector(kv.Key).ToArray();
material.vectorProperties.Add(kv.Key, value);
}
break;
default:
throw new NotImplementedException();
}
}
}
foreach (var tag in TAGS)
{
var value = m.GetTag(tag, false);
if (!String.IsNullOrEmpty(value))
{
material.tagMap.Add(tag, value);
}
}
return material;
}
#endregion
}
}

View File

@@ -28,8 +28,7 @@ namespace VRM
srcMaterial.mainTextureOffset = offset;
srcMaterial.mainTextureScale = scale;
var materialExporter = new VRMMaterialExporter();
var vrmMaterial = VRMMaterialExporter.CreateFromMaterial(srcMaterial, textureExporter);
var vrmMaterial = VrmExtensionMaterialPropertyExporter.ExportMaterial(srcMaterial, textureExporter);
Assert.AreEqual(vrmMaterial.vectorProperties["_MainTex"], new float[] { 0.3f, 0.2f, 0.5f, 0.6f });
var materialImporter = new VRMMaterialDescriptorGenerator(new glTF_VRM_extensions

View File

@@ -10,7 +10,7 @@ namespace VRM.Samples
static UniGLTF.glTFMaterial ExportLoaded(string resourceName)
{
var material = Resources.Load<Material>(resourceName);
var exporter = new VRMMaterialExporter();
var exporter = new BuiltinRPVrmMaterialExporter();
var textureExporter = new TextureExporter(new EditorTextureSerializer());
var exported = exporter.ExportMaterial(material, textureExporter, new GltfExportSettings());

View File

@@ -4,7 +4,7 @@ using VRMShaders;
namespace UniVRM10
{
public class Vrm10MaterialExporter : MaterialExporter
public class BuiltinRPVrm10MaterialExporter : BuiltinRPGltfMaterialExporter
{
public override glTFMaterial ExportMaterial(Material m, ITextureExporter textureExporter, GltfExportSettings settings)
{

View File

@@ -158,7 +158,7 @@ namespace UniVRM10
static IEnumerable<glTFMaterial> ExportMaterials(Model model, ITextureExporter textureExporter, GltfExportSettings settings)
{
var materialExporter = new Vrm10MaterialExporter();
var materialExporter = new BuiltinRPVrm10MaterialExporter();
foreach (Material material in model.Materials)
{
yield return materialExporter.ExportMaterial(material, textureExporter, settings);