Export texture's alpha if only necessary.

This commit is contained in:
Masataka SUMI
2021-06-23 23:04:16 +09:00
parent 851b9432d7
commit fbd885c67c
14 changed files with 203 additions and 147 deletions

View File

@@ -38,7 +38,7 @@ namespace UniGLTF
if (m.HasProperty("_MainTex"))
{
var index = textureManager.ExportAsSRgb(m.GetTexture("_MainTex"));
var index = textureManager.ExportAsSRgb(m.GetTexture("_MainTex"), needsAlpha: true);
if (index != -1)
{
material.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo()
@@ -161,7 +161,7 @@ namespace UniGLTF
if (m.HasProperty("_EmissionMap"))
{
var index = textureExporter.ExportAsSRgb(m.GetTexture("_EmissionMap"));
var index = textureExporter.ExportAsSRgb(m.GetTexture("_EmissionMap"), needsAlpha: false);
if (index != -1)
{
material.emissiveTexture = new glTFMaterialEmissiveTextureInfo()

View File

@@ -369,9 +369,10 @@ namespace UniGLTF
ExportExtensions(textureSerializer);
// Extension で Texture が増える場合があるので最後に呼ぶ
for (int i = 0; i < TextureExporter.Exported.Count; ++i)
var exported = TextureExporter.Export();
for (var exportedTextureIdx = 0; exportedTextureIdx < exported.Count; ++exportedTextureIdx)
{
var (unityTexture, colorSpace) = TextureExporter.Exported[i];
var (unityTexture, colorSpace) = exported[exportedTextureIdx];
glTF.PushGltfTexture(bufferIndex, unityTexture, colorSpace, textureSerializer);
}
}

View File

@@ -26,7 +26,9 @@ namespace UniGLTF
var materialExporter = new MaterialExporter();
materialExporter.ExportMaterial(material, textureExporter);
var (convTex0, colorSpace) = textureExporter.Exported[0];
var exported = textureExporter.Export();
var (convTex0, colorSpace) = exported[0];
var sampler = TextureSamplerUtil.Export(convTex0);
Assert.AreEqual(glWrap.CLAMP_TO_EDGE, sampler.wrapS);
@@ -100,7 +102,7 @@ namespace UniGLTF
.ToDictionary(kv => kv.Item1, kv => kv.Item2)
;
// extractor
// extractor
var extractor = new TextureExtractor(parser, UnityPath.FromUnityPath(""), textureMap);
var m = context.TextureDescriptorGenerator.Get().GetEnumerable()
.FirstOrDefault(x => x.SubAssetKey.Name == "texture_1.standard");

View File

@@ -136,7 +136,7 @@ namespace VRM
VRM.meta.title = meta.Title;
if (meta.Thumbnail != null)
{
VRM.meta.texture = TextureExporter.ExportAsSRgb(meta.Thumbnail);
VRM.meta.texture = TextureExporter.ExportAsSRgb(meta.Thumbnail, needsAlpha: true);
}
// ussage permission

View File

@@ -182,7 +182,7 @@ namespace VRM
{
var value = kv.Key == "_BumpMap"
? textureExporter.ExportAsNormal(texture)
: textureExporter.ExportAsSRgb(texture)
: textureExporter.ExportAsSRgb(texture, needsAlpha: kv.Key == "_MainTex")
;
if (value == -1)
{

View File

@@ -42,7 +42,7 @@ namespace UniVRM10
// Lighting
dst.pbrMetallicRoughness = new glTFPbrMetallicRoughness();
dst.pbrMetallicRoughness.baseColorFactor = context.BaseColorFactorSrgb.ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
var baseColorTextureIndex = textureExporter.ExportAsSRgb(context.BaseColorTexture);
var baseColorTextureIndex = textureExporter.ExportAsSRgb(context.BaseColorTexture, needsAlpha: true);
if (baseColorTextureIndex != -1)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo
@@ -51,7 +51,7 @@ namespace UniVRM10
};
}
mtoon.ShadeColorFactor = context.ShadeColorFactorSrgb.ToFloat3(ColorSpace.sRGB, ColorSpace.Linear);
var shadeColorTextureIndex = textureExporter.ExportAsSRgb(context.ShadeColorTexture);
var shadeColorTextureIndex = textureExporter.ExportAsSRgb(context.ShadeColorTexture, needsAlpha: false);
if (shadeColorTextureIndex != -1)
{
mtoon.ShadeMultiplyTexture = new TextureInfo
@@ -69,7 +69,7 @@ namespace UniVRM10
};
}
mtoon.ShadingShiftFactor = context.ShadingShiftFactor;
var shadingShiftTextureIndex = textureExporter.ExportAsLinear(context.ShadingShiftTexture);
var shadingShiftTextureIndex = textureExporter.ExportAsLinear(context.ShadingShiftTexture, needsAlpha: false);
if (shadingShiftTextureIndex != -1)
{
mtoon.ShadingShiftTexture = new ShadingShiftTextureInfo
@@ -86,7 +86,7 @@ namespace UniVRM10
// Emission
dst.emissiveFactor = context.EmissiveFactorLinear.ToFloat3(ColorSpace.Linear, ColorSpace.Linear);
var emissiveTextureIndex = textureExporter.ExportAsSRgb(context.EmissiveTexture);
var emissiveTextureIndex = textureExporter.ExportAsSRgb(context.EmissiveTexture, needsAlpha: false);
if (emissiveTextureIndex != -1)
{
dst.emissiveTexture = new glTFMaterialEmissiveTextureInfo
@@ -96,7 +96,7 @@ namespace UniVRM10
}
// Rim Lighting
var matcapTextureIndex = textureExporter.ExportAsSRgb(context.MatcapTexture);
var matcapTextureIndex = textureExporter.ExportAsSRgb(context.MatcapTexture, needsAlpha: false);
if (matcapTextureIndex != -1)
{
mtoon.MatcapTexture = new TextureInfo
@@ -107,7 +107,7 @@ namespace UniVRM10
mtoon.ParametricRimColorFactor = context.ParametricRimColorFactorSrgb.ToFloat3(ColorSpace.sRGB, ColorSpace.Linear);
mtoon.ParametricRimFresnelPowerFactor = context.ParametricRimFresnelPowerFactor;
mtoon.ParametricRimLiftFactor = context.ParametricRimLiftFactor;
var rimMultiplyTextureIndex = textureExporter.ExportAsSRgb(context.RimMultiplyTexture);
var rimMultiplyTextureIndex = textureExporter.ExportAsSRgb(context.RimMultiplyTexture, needsAlpha: false);
if (rimMultiplyTextureIndex != -1)
{
mtoon.RimMultiplyTexture = new TextureInfo
@@ -120,7 +120,7 @@ namespace UniVRM10
// Outline
mtoon.OutlineWidthMode = ExportOutlineWidthMode(context.OutlineWidthMode);
mtoon.OutlineWidthFactor = context.OutlineWidthFactor;
var outlineWidthMultiplyTextureIndex = textureExporter.ExportAsLinear(context.OutlineWidthMultiplyTexture);
var outlineWidthMultiplyTextureIndex = textureExporter.ExportAsLinear(context.OutlineWidthMultiplyTexture, needsAlpha: false);
if (outlineWidthMultiplyTextureIndex != -1)
{
mtoon.OutlineWidthMultiplyTexture = new TextureInfo
@@ -132,7 +132,7 @@ namespace UniVRM10
mtoon.OutlineLightingMixFactor = context.OutlineLightingMixFactor;
// UV Animation
var uvAnimationMaskTextureIndex = textureExporter.ExportAsLinear(context.UvAnimationMaskTexture);
var uvAnimationMaskTextureIndex = textureExporter.ExportAsLinear(context.UvAnimationMaskTexture, needsAlpha: false);
if (uvAnimationMaskTextureIndex != -1)
{
mtoon.UvAnimationMaskTexture = new TextureInfo

View File

@@ -185,9 +185,10 @@ namespace UniVRM10
var (vrm, vrmSpringBone, thumbnailTextureIndex) = ExportVrm(root, model, converter, vrmMeta);
// Extension で Texture が増える場合があるので最後に呼ぶ
for (int i = 0; i < m_textureExporter.Exported.Count; ++i)
var exportedTextures = m_textureExporter.Export();
for (var exportedTextureIdx = 0; exportedTextureIdx < exportedTextures.Count; ++exportedTextureIdx)
{
var (unityTexture, texColorSpace) = m_textureExporter.Exported[i];
var (unityTexture, texColorSpace) = exportedTextures[exportedTextureIdx];
Storage.Gltf.PushGltfTexture(0, unityTexture, texColorSpace, m_textureSerializer);
}
@@ -649,7 +650,7 @@ namespace UniVRM10
int? thumbnailTextureIndex = default;
if (meta.Thumbnail != null)
{
thumbnailTextureIndex = m_textureExporter.ExportAsSRgb(meta.Thumbnail);
thumbnailTextureIndex = m_textureExporter.ExportAsSRgb(meta.Thumbnail, needsAlpha: true);
}
return thumbnailTextureIndex;
}

View File

@@ -10,20 +10,15 @@ namespace VRMShaders
/// </summary>
public interface ITextureExporter
{
/// <summary>
/// Export する Texture2D のリスト。これが gltf.textures になる
/// </summary>
IReadOnlyList<(Texture2D, ColorSpace)> Exported { get; }
/// <summary>
/// 指定の Texture を、 sRGB 色空間の値を持つ Texture に出力するように指示する。
/// </summary>
int ExportAsSRgb(Texture src);
int ExportAsSRgb(Texture src, bool needsAlpha);
/// <summary>
/// 指定の Texture を、 Linear の値を持つ Texture に出力するように指示する。
/// </summary>
int ExportAsLinear(Texture src);
int ExportAsLinear(Texture src, bool needsAlpha);
/// <summary>
/// Unity Standard Shader の Metallic, Roughness, Occlusion 情報を、 glTF 仕様に準拠した 1 枚の合成テクスチャとして出力するように指示する。

View File

@@ -6,9 +6,12 @@ namespace VRMShaders
{
public static class TextureConverter
{
public static readonly TextureFormat WithAlphaFormat = TextureFormat.ARGB32;
public static readonly TextureFormat WithoutAlphaFormat = TextureFormat.RGB24;
public static Texture2D CreateEmptyTextureWithSettings(Texture src, ColorSpace dstColorSpace, bool dstNeedsAlpha)
{
var texFormat = dstNeedsAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24;
var texFormat = dstNeedsAlpha ? WithAlphaFormat : WithoutAlphaFormat;
var dst = new Texture2D(src.width, src.height, texFormat, src.HasMipMap(), dstColorSpace == ColorSpace.Linear);
dst.name = src.name;
dst.anisoLevel = src.anisoLevel;

View File

@@ -0,0 +1,39 @@
using System;
using UnityEngine;
namespace VRMShaders
{
internal readonly struct TextureExportKey : IEquatable<TextureExportKey>
{
public readonly Texture Src;
public readonly TextureExportTypes TextureType;
public TextureExportKey(Texture src, TextureExportTypes type)
{
if (src == null)
{
throw new ArgumentNullException();
}
Src = src;
TextureType = type;
}
public bool Equals(TextureExportKey other)
{
return Equals(Src, other.Src) && TextureType == other.TextureType;
}
public override bool Equals(object obj)
{
return obj is TextureExportKey other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return ((Src != null ? Src.GetHashCode() : 0) * 397) ^ (int) TextureType;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3698d17d1e624396a74390f338fda684
timeCreated: 1624456050

View File

@@ -0,0 +1,14 @@
namespace VRMShaders
{
internal enum TextureExportTypes
{
// sRGB テクスチャとして出力
Srgb,
// Linear テクスチャとして出力
Linear,
// Unity Standard様式 から glTF PBR様式への変換
OcclusionMetallicRoughness,
// Assetを使うときはそのバイト列を無変換で、それ以外は DXT5nm 形式からのデコードを行う
Normal,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf56f43baf1e4b47becb7d0ba42a72dc
timeCreated: 1624456063

View File

@@ -11,10 +11,9 @@ namespace VRMShaders
public sealed class TextureExporter : IDisposable, ITextureExporter
{
private readonly ITextureSerializer m_textureSerializer;
private readonly Dictionary<ExportKey, int> m_exportMap = new Dictionary<ExportKey, int>();
private readonly List<(Texture2D, ColorSpace)> m_exported = new List<(Texture2D, ColorSpace)>();
public IReadOnlyList<(Texture2D, ColorSpace)> Exported => m_exported;
private readonly List<(TextureExportKey key, bool needsAlpha, Func<Texture2D> creator)> _exportingList =
new List<(TextureExportKey key, bool needsAlpha, Func<Texture2D> creator)>();
public TextureExporter(ITextureSerializer textureSerializer)
{
@@ -26,130 +25,110 @@ namespace VRMShaders
// TODO: export 用にコピー・変換したテクスチャーをここで解放したい
}
enum ExportTypes
/// <summary>
/// 実際にテクスチャを変換する
/// </summary>
public List<(Texture2D, ColorSpace)> Export()
{
// sRGB テクスチャとして出力
Srgb,
// Linear テクスチャとして出力
Linear,
// Unity Standard様式 から glTF PBR様式への変換
OcclusionMetallicRoughness,
// Assetを使うときはそのバイト列を無変換で、それ以外は DXT5nm 形式からのデコードを行う
Normal,
var exported = new List<(Texture2D, ColorSpace)>();
for (var idx = 0; idx < _exportingList.Count; ++idx)
{
var (key, needsAlpha, creator) = _exportingList[idx];
var colorSpace = key.TextureType == TextureExportTypes.Srgb ? ColorSpace.sRGB : ColorSpace.Linear;
var texture = creator();
exported.Add((creator(), colorSpace));
}
return exported;
}
readonly struct ExportKey
public int ExportAsSRgb(Texture src, bool needsAlpha)
{
public readonly Texture Src;
public readonly ExportTypes TextureType;
return ExportSimple(src, needsAlpha, isLinear: false);
}
public ExportKey(Texture src, ExportTypes type)
public int ExportAsLinear(Texture src, bool needsAlpha)
{
return ExportSimple(src, needsAlpha, isLinear: true);
}
private int ExportSimple(Texture src, bool needsAlpha, bool isLinear)
{
if (src == null)
{
if (src == null)
return -1;
}
var exportType = isLinear ? TextureExportTypes.Linear : TextureExportTypes.Srgb;
var colorSpace = isLinear ? ColorSpace.Linear : ColorSpace.sRGB;
var key = new TextureExportKey(src, exportType);
var existsIdx = _exportingList.FindIndex(x => key.Equals(x.key));
if (existsIdx != -1)
{
// already marked as exporting
var cached = _exportingList[existsIdx];
if (needsAlpha && !cached.needsAlpha)
{
throw new ArgumentNullException();
// アルファチャンネルを必要とする使用用途が表れたため、アルファチャンネル付きで出力するように上書きする
_exportingList[existsIdx] = (cached.key, true, () => ConvertTextureSimple(src, true, colorSpace));
return existsIdx;
}
else
{
// Return cached
return existsIdx;
}
Src = src;
TextureType = type;
}
}
public int ExportAsSRgb(Texture src)
{
if (src == null)
{
return -1;
}
// cache
if (m_exportMap.TryGetValue(new ExportKey(src, ExportTypes.Srgb), out var index))
{
return index;
}
// get Texture2D
index = m_exported.Count;
var texture2D = src as Texture2D;
if (m_textureSerializer.CanExportAsEditorAssetFile(texture2D, ColorSpace.sRGB))
{
// do nothing
}
else
{
texture2D = TextureConverter.CopyTexture(src, ColorSpace.sRGB, true, null);
// Add
_exportingList.Add((key, needsAlpha, () => ConvertTextureSimple(src, needsAlpha, colorSpace)));
return _exportingList.Count - 1;
}
m_exported.Add((texture2D, ColorSpace.sRGB));
m_exportMap.Add(new ExportKey(src, ExportTypes.Srgb), index);
return index;
}
public int ExportAsLinear(Texture src)
{
if (src == null)
{
return -1;
}
var exportKey = new ExportKey(src, ExportTypes.Linear);
// search cache
if (m_exportMap.TryGetValue(exportKey, out var index))
{
return index;
}
index = m_exported.Count;
var texture2d = src as Texture2D;
if (m_textureSerializer.CanExportAsEditorAssetFile(texture2d, ColorSpace.Linear))
{
// do nothing
}
else
{
texture2d = TextureConverter.CopyTexture(src, ColorSpace.Linear, false, null);
}
m_exported.Add((texture2d, ColorSpace.Linear));
m_exportMap.Add(exportKey, index);
return index;
}
public int ExportAsCombinedGltfPbrParameterTextureFromUnityStandardTextures(Texture metallicSmoothTexture, float smoothness, Texture occlusionTexture)
{
if (metallicSmoothTexture == null && occlusionTexture == null)
if (metallicSmoothTexture != null)
{
// metallicSmoothness is available
var key = new TextureExportKey(metallicSmoothTexture, TextureExportTypes.OcclusionMetallicRoughness);
var existsIdx = _exportingList.FindIndex(x => key.Equals(x.key));
if (existsIdx != -1)
{
// Return cached
return existsIdx;
}
else
{
// Add
_exportingList.Add((key, false, () => OcclusionMetallicRoughnessConverter.Export(metallicSmoothTexture, smoothness, occlusionTexture)));
return _exportingList.Count - 1;
}
}
else if (occlusionTexture != null)
{
// TODO 厳密なチェックをしていない
// occlusion is available
var key = new TextureExportKey(occlusionTexture, TextureExportTypes.OcclusionMetallicRoughness);
var existsIdx = _exportingList.FindIndex(x => key.Equals(x.key));
if (existsIdx != -1)
{
// Return cached
return existsIdx;
}
else
{
// Add
_exportingList.Add((key, false, () => OcclusionMetallicRoughnessConverter.Export(metallicSmoothTexture, smoothness, occlusionTexture)));
return _exportingList.Count - 1;
}
}
else
{
return -1;
}
// cache
// TODO 厳密なチェックをしていない
if (metallicSmoothTexture != null && m_exportMap.TryGetValue(new ExportKey(metallicSmoothTexture, ExportTypes.OcclusionMetallicRoughness), out var index))
{
return index;
}
if (occlusionTexture != null && m_exportMap.TryGetValue(new ExportKey(occlusionTexture, ExportTypes.OcclusionMetallicRoughness), out index))
{
return index;
}
//
// Unity と glTF で互換性が無いので必ず変換が必用
//
index = m_exported.Count;
var texture2D = OcclusionMetallicRoughnessConverter.Export(metallicSmoothTexture, smoothness, occlusionTexture);
m_exported.Add((texture2D, ColorSpace.Linear));
if (metallicSmoothTexture != null)
{
m_exportMap.Add(new ExportKey(metallicSmoothTexture, ExportTypes.OcclusionMetallicRoughness), index);
}
if (occlusionTexture != null && occlusionTexture != metallicSmoothTexture)
{
m_exportMap.Add(new ExportKey(occlusionTexture, ExportTypes.OcclusionMetallicRoughness), index);
}
return index;
}
public int ExportAsNormal(Texture src)
@@ -159,21 +138,37 @@ namespace VRMShaders
return -1;
}
// cache
if (m_exportMap.TryGetValue(new ExportKey(src, ExportTypes.Normal), out var index))
var key = new TextureExportKey(src, TextureExportTypes.Normal);
var existsIdx = _exportingList.FindIndex(x => key.Equals(x.key));
if (existsIdx != -1)
{
return index;
// Return cached;
return existsIdx;
}
else
{
// Add
// NormalMap Property のテクスチャは必ず NormalMap として解釈してコピーする。
// Texture Asset の設定に依らず、Standard Shader で得られる見た目と同じ結果を得るため。
_exportingList.Add((key, false, () => NormalConverter.Export(src)));
return _exportingList.Count - 1;
}
}
index = m_exported.Count;
// NormalMap Property のテクスチャは必ず NormalMap として解釈してコピーする。
// Texture Asset の設定に依らず、Standard Shader で得られる見た目と同じ結果を得るため。
var texture2D = NormalConverter.Export(src);
m_exported.Add((texture2D, ColorSpace.Linear));
m_exportMap.Add(new ExportKey(src, ExportTypes.Normal), index);
return index;
private Texture2D ConvertTextureSimple(Texture src, bool needsAlpha, ColorSpace exportColorSpace)
{
// get Texture2D
var texture2D = src as Texture2D;
if (m_textureSerializer.CanExportAsEditorAssetFile(texture2D, exportColorSpace))
{
// do nothing
}
else
{
texture2D = TextureConverter.CopyTexture(src, exportColorSpace, needsAlpha, null);
}
return texture2D;
}
}
}