Export Texture with specified Color Space.

This commit is contained in:
Masataka SUMI 2021-05-13 20:57:35 +09:00
parent 72a922ded9
commit b8f2d37ccf
9 changed files with 108 additions and 37 deletions

View File

@ -21,9 +21,9 @@ namespace UniGLTF
/// <param name="bufferIndex"></param>
/// <param name="texture"></param>
/// <returns>gltf texture index</returns>
public static int PushGltfTexture(this glTF gltf, int bufferIndex, Texture2D texture, Func<Texture2D, (byte[] bytes, string mime)> getTextureBytes)
public static int PushGltfTexture(this glTF gltf, int bufferIndex, Texture2D texture, ColorSpace textureColorSpace, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes)
{
var bytesWithMime = getTextureBytes(texture);
var bytesWithMime = getTextureBytes(texture, textureColorSpace);
// add view
var view = gltf.buffers[bufferIndex].Append(bytesWithMime.bytes, glBufferTarget.NONE);

View File

@ -214,12 +214,21 @@ namespace UniGLTF
return false;
}
public virtual void ExportExtensions(Func<Texture2D, (byte[], string)> getTextureBytes)
public virtual void ExportExtensions(GetBytesWithMimeFromTexture2D getTextureBytes)
{
// do nothing
}
public virtual void Export(MeshExportSettings meshExportSettings, Func<Texture, bool> useAsset, Func<Texture2D, (byte[], string)> getTextureBytes)
/// <summary>
/// Texture2D から実際のバイト列を取得するデリゲート。
///
/// textureColorSpace は Texture2D をコピーする際に用いる。
/// Texture2D 単体では、色空間を知ることができないため。
/// 一般には、その Texture2D がアサインされる glTF のプロパティの仕様が定める色空間と一致する。
/// </summary>
public delegate (byte[] bytes, string mime) GetBytesWithMimeFromTexture2D(Texture2D texture, ColorSpace textureColorSpace);
public virtual void Export(MeshExportSettings meshExportSettings, Func<Texture, bool> useAsset, GetBytesWithMimeFromTexture2D getTextureBytes)
{
var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]);
var bufferIndex = glTF.AddBuffer(bytesBuffer);
@ -362,8 +371,8 @@ namespace UniGLTF
// Extension で Texture が増える場合があるので最後に呼ぶ
for (int i = 0; i < TextureManager.Exported.Count; ++i)
{
var unityTexture = TextureManager.Exported[i];
glTF.PushGltfTexture(bufferIndex, unityTexture, getTextureBytes);
var (unityTexture, colorSpace) = TextureManager.Exported[i];
glTF.PushGltfTexture(bufferIndex, unityTexture, colorSpace, getTextureBytes);
}
}
#endregion

View File

@ -26,7 +26,7 @@ namespace UniGLTF
var materialExporter = new MaterialExporter();
materialExporter.ExportMaterial(material, textureManager);
var convTex0 = textureManager.Exported[0];
var (convTex0, colorSpace) = textureManager.Exported[0];
var sampler = TextureSamplerUtil.Export(convTex0);
Assert.AreEqual(glWrap.CLAMP_TO_EDGE, sampler.wrapS);

View File

@ -3,6 +3,7 @@ using System.Linq;
using UniGLTF;
using UniJSON;
using UnityEngine;
using ColorSpace = UniGLTF.ColorSpace;
namespace VRM
@ -14,7 +15,7 @@ namespace VRM
return new VRMMaterialExporter();
}
public static glTF Export(MeshExportSettings configuration, GameObject go, Func<Texture, bool> useAsset, Func<Texture2D, (byte[], string)> getTextureBytes)
public static glTF Export(MeshExportSettings configuration, GameObject go, Func<Texture, bool> useAsset, GetBytesWithMimeFromTexture2D getTextureBytes)
{
var gltf = new glTF();
using (var exporter = new VRMExporter(gltf))
@ -32,7 +33,7 @@ namespace VRM
gltf.extensionsUsed.Add(glTF_VRM_extensions.ExtensionName);
}
public override void ExportExtensions(Func<Texture2D, (byte[], string)> getTextureBytes)
public override void ExportExtensions(GetBytesWithMimeFromTexture2D getTextureBytes)
{
// avatar
var animator = Copy.GetComponent<Animator>();
@ -110,7 +111,7 @@ namespace VRM
VRM.meta.title = meta.Title;
if (meta.Thumbnail != null)
{
VRM.meta.texture = glTF.PushGltfTexture(glTF.buffers.Count - 1, meta.Thumbnail, getTextureBytes);
VRM.meta.texture = glTF.PushGltfTexture(glTF.buffers.Count - 1, meta.Thumbnail, ColorSpace.sRGB, getTextureBytes);
}
VRM.meta.licenseType = meta.LicenseType;

View File

@ -127,7 +127,7 @@ namespace UniVRM10
return new float[] { -v.x, v.y, v.z };
}
public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, Func<Texture2D, (byte[], string)> getTextureBytes, VRM10MetaObject metaObject = null)
public void Export(GameObject root, Model model, ModelExporter converter, ExportArgs option, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes, VRM10MetaObject metaObject = null)
{
ExportAsset(model);
@ -180,8 +180,8 @@ namespace UniVRM10
// Extension で Texture が増える場合があるので最後に呼ぶ
for (int i = 0; i < m_textureExporter.Exported.Count; ++i)
{
var unityTexture = m_textureExporter.Exported[i];
Storage.Gltf.PushGltfTexture(0, unityTexture, getTextureBytes);
var (unityTexture, texColorSpace) = m_textureExporter.Exported[i];
Storage.Gltf.PushGltfTexture(0, unityTexture, texColorSpace, getTextureBytes);
}
if (thumbnailTextureIndex.HasValue)
@ -775,7 +775,7 @@ namespace UniVRM10
/// <param name="go"></param>
/// <param name="getTextureBytes"></param>
/// <returns></returns>
public static byte[] Export(GameObject go, Func<Texture2D, (byte[], string)> getTextureBytes = null)
public static byte[] Export(GameObject go, gltfExporter.GetBytesWithMimeFromTexture2D getTextureBytes = null)
{
if (getTextureBytes == null)
{

View File

@ -2,6 +2,7 @@ using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using ColorSpace = UniGLTF.ColorSpace;
namespace VRMShaders
{
@ -107,14 +108,14 @@ namespace VRMShaders
return false;
}
public static (byte[], string) GetTextureBytesWithMime(Texture2D texture)
public static (byte[], string) GetTextureBytesWithMime(Texture2D texture, ColorSpace colorSpace)
{
if (TryGetBytesWithMime(texture, out byte[] bytes, out string mime))
{
return (bytes, mime);
}
return TextureExporter.GetTextureBytesWithMime(texture);
return TextureExporter.GetTextureBytesWithMime(texture, colorSpace);
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Linq;
using UnityEngine;
using ColorSpace = UniGLTF.ColorSpace;
namespace VRMShaders
@ -22,10 +23,27 @@ namespace VRMShaders
}
public static Texture2D CopyTexture(Texture src, TextureImportTypes textureType, Material material)
{
return CopyTexture(src, textureType.GetColorSpace(), material);
}
public static Texture2D CopyTexture(Texture src, ColorSpace colorSpace, Material material)
{
Texture2D dst = null;
RenderTextureReadWrite colorSpace = textureType.GetColorSpace();
var renderTexture = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.ARGB32, colorSpace);
RenderTextureReadWrite readWrite;
switch (colorSpace)
{
case ColorSpace.sRGB:
readWrite = RenderTextureReadWrite.sRGB;
break;
case ColorSpace.Linear:
readWrite = RenderTextureReadWrite.Linear;
break;
default:
throw new ArgumentOutOfRangeException(nameof(colorSpace), colorSpace, null);
}
var renderTexture = new RenderTexture(src.width, src.height, 0, RenderTextureFormat.ARGB32, readWrite);
if (material != null)
{
@ -36,7 +54,7 @@ namespace VRMShaders
Graphics.Blit(src, renderTexture);
}
dst = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false, colorSpace == RenderTextureReadWrite.Linear);
dst = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false, readWrite == RenderTextureReadWrite.Linear);
dst.ReadPixels(new Rect(0, 0, src.width, src.height), 0, 0);
dst.name = src.name;
dst.anisoLevel = src.anisoLevel;

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using ColorSpace = UniGLTF.ColorSpace;
namespace VRMShaders
@ -55,7 +56,7 @@ namespace VRMShaders
/// </summary>
/// <typeparam name="Texture2D"></typeparam>
/// <returns></returns>
public readonly List<Texture2D> Exported = new List<Texture2D>();
public readonly List<(Texture2D, ColorSpace)> Exported = new List<(Texture2D, ColorSpace)>();
/// <summary>
/// Texture の export index を得る
@ -101,7 +102,7 @@ namespace VRMShaders
{
texture2D = TextureConverter.CopyTexture(src, TextureImportTypes.sRGB, null);
}
Exported.Add(texture2D);
Exported.Add((texture2D, ColorSpace.sRGB));
m_exportMap.Add(new ExportKey(src, ConvertTypes.None), index);
return index;
@ -114,7 +115,33 @@ namespace VRMShaders
/// <returns></returns>
public int ExportLinear(Texture src)
{
throw new NotImplementedException();
if (src == null)
{
return -1;
}
var exportKey = new ExportKey(src, ConvertTypes.None);
// search cache
if (m_exportMap.TryGetValue(exportKey, out var index))
{
return index;
}
index = Exported.Count;
var texture2d = src as Texture2D;
if (m_useAsset(texture2d))
{
// do nothing
}
else
{
texture2d = TextureConverter.CopyTexture(src, TextureImportTypes.Linear, null);
}
Exported.Add((texture2d, ColorSpace.Linear));
m_exportMap.Add(exportKey, index);
return index;
}
/// <summary>
@ -147,7 +174,7 @@ namespace VRMShaders
index = Exported.Count;
var texture2D = OcclusionMetallicRoughnessConverter.Export(metallicSmoothTexture, smoothness, occlusionTexture);
Exported.Add(texture2D);
Exported.Add((texture2D, ColorSpace.Linear));
if (metallicSmoothTexture != null)
{
m_exportMap.Add(new ExportKey(metallicSmoothTexture, ConvertTypes.OcclusionMetallicRoughness), index);
@ -191,7 +218,7 @@ namespace VRMShaders
texture2D = NormalConverter.Export(src);
}
Exported.Add(texture2D);
Exported.Add((texture2D, ColorSpace.Linear));
m_exportMap.Add(new ExportKey(src, ConvertTypes.Normal), index);
return index;
@ -200,9 +227,7 @@ namespace VRMShaders
/// <summary>
/// 画像のバイト列を得る
/// </summary>
/// <param name="texture"></param>
/// <returns></returns>
public static (byte[] bytes, string mime) GetTextureBytesWithMime(Texture2D texture)
public static (byte[] bytes, string mime) GetTextureBytesWithMime(Texture2D texture, ColorSpace colorSpace)
{
try
{
@ -211,22 +236,38 @@ namespace VRMShaders
{
return (png, "image/png");
}
else
{
// 単純に EncodeToPNG できないため、コピーしてから EncodeToPNG する。
return CopyTextureAndGetBytesWithMime(texture, colorSpace);
}
}
catch (ArgumentException ex)
{
// fail to EncodeToPng
// System.ArgumentException: not readable, the texture memory can not be accessed from scripts. You can make the texture readable in the Texture Import Settings.
// example, ".DDS" Texture object in Editor.
Debug.LogWarning(ex);
// Read/Write が許可されていない Texture2D オブジェクトはこの関数に渡されるべきではない。
// なぜなら Texture2D の色空間は、対応する glTF プロパティ指定の色空間と一致していなければならないが
// Read/Write が許可されていない場合、その条件を守って変換することができないからである。
// したがって、この関数に渡す前に glTF プロパティ指定の色空間を加味して Copy Texture して、それを渡すべきである。
throw;
// 単純に EncodeToPNG できないため、コピーしてから EncodeToPNG する。
return CopyTextureAndGetBytesWithMime(texture, colorSpace);
}
}
private static (byte[] bytes, string mime) CopyTextureAndGetBytesWithMime(Texture2D texture, ColorSpace colorSpace)
{
var copiedTex = TextureConverter.CopyTexture(texture, colorSpace, null);
var bytes = copiedTex.EncodeToPNG();
if (Application.isPlaying)
{
UnityEngine.Object.Destroy(copiedTex);
}
else
{
UnityEngine.Object.DestroyImmediate(copiedTex);
}
throw new ArgumentException("Invalid Texture2D");
return (bytes, "image/png");
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using UnityEngine;
using ColorSpace = UniGLTF.ColorSpace;
namespace VRMShaders
{
@ -31,16 +32,16 @@ namespace VRMShaders
public static class TextureImportTypesExtensions
{
public static RenderTextureReadWrite GetColorSpace(this TextureImportTypes textureType)
public static ColorSpace GetColorSpace(this TextureImportTypes textureType)
{
switch (textureType)
{
case TextureImportTypes.sRGB:
return RenderTextureReadWrite.sRGB;
return ColorSpace.sRGB;
case TextureImportTypes.Linear:
case TextureImportTypes.StandardMap:
case TextureImportTypes.NormalMap:
return RenderTextureReadWrite.Linear;
return ColorSpace.Linear;
default:
throw new NotImplementedException();
}