Implements glTF PBR spec compatible shader with ShaderGraph

includes
- Editor Import
- Editor Export
- Runtime Export

TBD
- Runtime Import
This commit is contained in:
Masataka SUMI
2026-07-18 18:18:01 +09:00
parent 35aa64c811
commit b343e3aba4
42 changed files with 6216 additions and 19 deletions

View File

@@ -38,16 +38,16 @@
"expression": "",
"define": "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER"
},
{
"name": "com.unity.cloud.gltfast",
"expression": "",
"define": "UNIGLTF_DISABLE_DEFAULT_GLB_IMPORTER"
},
{
"name": "com.unity.cloud.gltfast",
"expression": "",
"define": "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER"
}
{
"name": "com.unity.cloud.gltfast",
"expression": "",
"define": "UNIGLTF_DISABLE_DEFAULT_GLB_IMPORTER"
},
{
"name": "com.unity.cloud.gltfast",
"expression": "",
"define": "UNIGLTF_DISABLE_DEFAULT_GLTF_IMPORTER"
}
],
"noEngineReferences": false
}

View File

@@ -20,7 +20,7 @@ namespace UniGLTF
{
public override void OnImportAsset(AssetImportContext ctx)
{
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline);
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline, m_pbrMaterialImportType);
}
}
}

View File

@@ -20,7 +20,7 @@ namespace UniGLTF
{
public override void OnImportAsset(AssetImportContext ctx)
{
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline);
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline, m_pbrMaterialImportType);
}
}
}

View File

@@ -21,6 +21,9 @@ namespace UniGLTF
[SerializeField]
public ImporterRenderPipelineTypes m_renderPipeline;
[SerializeField]
public PbrMaterialImportType m_pbrMaterialImportType;
/// <summary>
/// glb をパースして、UnityObject化、さらにAsset化する
/// </summary>
@@ -28,7 +31,8 @@ namespace UniGLTF
/// <param name="context"></param>
/// <param name="reverseAxis"></param>
/// <param name="renderPipeline"></param>
protected static void Import(ScriptedImporter scriptedImporter, AssetImportContext context, Axes reverseAxis, ImporterRenderPipelineTypes renderPipeline)
/// <param name="pbrMaterialImportType"></param>
protected static void Import(ScriptedImporter scriptedImporter, AssetImportContext context, Axes reverseAxis, ImporterRenderPipelineTypes renderPipeline, PbrMaterialImportType pbrMaterialImportType = PbrMaterialImportType.UnityStandard)
{
UniGLTFLogger.Log("OnImportAsset to " + scriptedImporter.assetPath);
@@ -41,7 +45,7 @@ namespace UniGLTF
.Where(x => x.Value != null)
.ToDictionary(kv => new SubAssetKey(kv.Value.GetType(), kv.Key.name), kv => kv.Value);
var materialGenerator = GetMaterialDescriptorGenerator(renderPipeline);
var materialGenerator = GetMaterialDescriptorGenerator(renderPipeline, pbrMaterialImportType);
var importerContextSettings = new ImporterContextSettings(loadAnimation: true, invertAxis: reverseAxis);
using (var data = new AutoGltfFileParser(scriptedImporter.assetPath).Parse())
@@ -68,8 +72,15 @@ namespace UniGLTF
}
}
private static IMaterialDescriptorGenerator GetMaterialDescriptorGenerator(ImporterRenderPipelineTypes renderPipeline)
private static IMaterialDescriptorGenerator GetMaterialDescriptorGenerator(ImporterRenderPipelineTypes renderPipeline, PbrMaterialImportType pbrMaterialImportType)
{
if (pbrMaterialImportType == PbrMaterialImportType.GltfCompatible)
{
// NOTE: UniVRM 最適化 PBR (ShaderGraph) は 1 つのシェーダで Built-In / URP 両対応のため
// RenderPipeline による分岐を行わない。
return new InvariantGltfMaterialDescriptorGenerator();
}
return renderPipeline switch
{
ImporterRenderPipelineTypes.Auto => MaterialDescriptorGeneratorUtility .GetValidGltfMaterialDescriptorGenerator(),

View File

@@ -14,7 +14,7 @@ namespace UniGLTF
{
public override void OnImportAsset(AssetImportContext ctx)
{
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline);
Import(this, ctx, m_reverseAxis.ToAxes(), m_renderPipeline, m_pbrMaterialImportType);
}
}
}

View File

@@ -5,6 +5,7 @@
"GUID:da3e51d19d51a544fa14d43fee843098",
"GUID:60c8346e00a8ddd4cafc5a02eceeec57",
"GUID:1cd941934d098654fa21a13f28346412",
"GUID:9fe7352e1c749b94aa9194a537b768e6",
"GUID:3d354272d3f2f4c3387dbccbaebd0f60"
],
"includePlatforms": [],

View File

@@ -12,6 +12,7 @@ namespace UniGLTF
"Unlit/Texture",
"Unlit/Transparent",
"Unlit/Transparent Cutout",
UniGltfPbrContext.ShaderName,
};
public glTFMaterial ExportMaterial(Material m, ITextureExporter textureExporter, GltfExportSettings settings)
@@ -37,6 +38,9 @@ namespace UniGLTF
case "Unlit/Transparent Cutout":
if (BuiltInGenericUnlitMaterialExporter.TryExportMaterial(m, glTFBlendMode.MASK, textureExporter, out dst)) return dst;
break;
case UniGltfPbrContext.ShaderName:
if (new InvariantRpUniGltfPbrMaterialExporter().TryExportMaterial(m, textureExporter, out dst)) return dst;
break;
}
UniGLTFLogger.Log($"Material `{m.name}` fallbacks.");

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a1a30880fa75f3749950bf8be0a89e32
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: da9bfbc669de45209e81fc260129d117
timeCreated: 1784359347

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 457a0388491c4f41b3017a75679ad870
timeCreated: 1784359510

View File

@@ -0,0 +1,173 @@
using UnityEngine;
using UnityEngine.Rendering;
namespace UniGLTF
{
public class InvariantRpUniGltfPbrMaterialExporter
{
public Shader Shader { get; set; }
public InvariantRpUniGltfPbrMaterialExporter(Shader shader = null)
{
Shader = shader ?? UniGltfPbrContext.GetShader();
}
public bool TryExportMaterial(Material src, ITextureExporter textureExporter, out glTFMaterial dst)
{
if (src == null || src.shader == null || src.shader != Shader)
{
dst = default;
return false;
}
dst = new glTFMaterial
{
name = src.name,
pbrMetallicRoughness = new glTFPbrMetallicRoughness(),
};
var context = new UniGltfPbrContext(src);
ExportSurfaceSettings(context, dst);
ExportBaseColor(context, dst, textureExporter);
ExportMetallicRoughness(context, dst, textureExporter);
ExportOcclusion(context, dst, textureExporter);
ExportNormal(context, dst, textureExporter);
ExportEmission(context, dst, textureExporter);
return true;
}
private static void ExportSurfaceSettings(UniGltfPbrContext context, glTFMaterial dst)
{
dst.alphaMode = (context.SurfaceType, context.IsAlphaClipEnabled) switch
{
(UniGltfPbrSurfaceType.Opaque, false) => glTFBlendMode.OPAQUE.ToString(),
(UniGltfPbrSurfaceType.Opaque, true) => glTFBlendMode.MASK.ToString(),
(UniGltfPbrSurfaceType.Transparent, _) => glTFBlendMode.BLEND.ToString(),
_ => glTFBlendMode.OPAQUE.ToString(),
};
dst.alphaCutoff = context.AlphaCutoff;
dst.doubleSided = context.CullMode != CullMode.Back; // NOTE: cull front は glTF 非対応
}
private static void ExportBaseColor(UniGltfPbrContext context, glTFMaterial dst, ITextureExporter textureExporter)
{
// _baseColorFactor は sRGB 格納なので sRGB->Linear で glTF に戻す
dst.pbrMetallicRoughness.baseColorFactor = context.BaseColorSrgb.ToFloat4(ColorSpace.sRGB, ColorSpace.Linear);
if (context.BaseColorTexture != null)
{
var needsAlpha = context.SurfaceType != UniGltfPbrSurfaceType.Opaque;
var index = textureExporter.RegisterExportingAsSRgb(context.BaseColorTexture, needsAlpha);
if (index >= 0)
{
dst.pbrMetallicRoughness.baseColorTexture = new glTFMaterialBaseColorTextureInfo
{
index = index,
texCoord = 0,
};
ExportTextureTransform(context.BaseColorTextureOffset, context.BaseColorTextureScale, dst.pbrMetallicRoughness.baseColorTexture);
}
}
}
private static void ExportMetallicRoughness(UniGltfPbrContext context, glTFMaterial dst, ITextureExporter textureExporter)
{
// 反転・1 固定なし。factor とテクスチャは独立 (glTF セマンティクス通り)
dst.pbrMetallicRoughness.metallicFactor = context.MetallicFactor;
dst.pbrMetallicRoughness.roughnessFactor = context.RoughnessFactor;
if (context.MetallicRoughnessTexture != null)
{
// 既に glTF ネイティブ ORM パッキング -> Linear passthrough (チャンネル入替なし)
var index = textureExporter.RegisterExportingAsLinear(context.MetallicRoughnessTexture, false);
if (index >= 0)
{
dst.pbrMetallicRoughness.metallicRoughnessTexture = new glTFMaterialMetallicRoughnessTextureInfo
{
index = index,
texCoord = 0,
};
ExportTextureTransform(context.MetallicRoughnessTextureOffset, context.MetallicRoughnessTextureScale, dst.pbrMetallicRoughness.metallicRoughnessTexture);
}
}
}
private static void ExportOcclusion(UniGltfPbrContext context, glTFMaterial dst, ITextureExporter textureExporter)
{
if (context.OcclusionTexture != null)
{
// metallicRoughnessTexture と同一 Texture の場合は TextureExporter が index を dedupe する
var index = textureExporter.RegisterExportingAsLinear(context.OcclusionTexture, false);
if (index >= 0)
{
dst.occlusionTexture = new glTFMaterialOcclusionTextureInfo
{
index = index,
texCoord = 0,
strength = context.OcclusionStrength,
};
ExportTextureTransform(context.OcclusionTextureOffset, context.OcclusionTextureScale, dst.occlusionTexture);
}
}
}
private static void ExportNormal(UniGltfPbrContext context, glTFMaterial dst, ITextureExporter textureExporter)
{
if (context.NormalTexture == null) return;
var index = textureExporter.RegisterExportingAsNormal(context.NormalTexture);
if (index >= 0)
{
dst.normalTexture = new glTFMaterialNormalTextureInfo
{
index = index,
texCoord = 0,
scale = context.NormalScale,
};
ExportTextureTransform(context.NormalTextureOffset, context.NormalTextureScale, dst.normalTexture);
}
}
private static void ExportEmission(UniGltfPbrContext context, glTFMaterial dst, ITextureExporter textureExporter)
{
var emissive = context.EmissiveFactorLinear; // linear
var strength = context.EmissiveStrength;
// emissiveFactor * emissiveStrength を放射輝度とし、glTF-valid (factor<=1) になるよう正規化する。
// maxComponent が 1 を超える分は KHR_materials_emissive_strength に逃がす。
var total = new Vector3(emissive.r * strength, emissive.g * strength, emissive.b * strength);
var maxComp = Mathf.Max(total.x, Mathf.Max(total.y, total.z));
if (maxComp > 1.0f)
{
dst.emissiveFactor = new[] { total.x / maxComp, total.y / maxComp, total.z / maxComp };
glTF_KHR_materials_emissive_strength.Serialize(ref dst.extensions, maxComp);
}
else
{
dst.emissiveFactor = new[] { total.x, total.y, total.z };
}
if (context.EmissiveTexture != null)
{
var index = textureExporter.RegisterExportingAsSRgb(context.EmissiveTexture, true);
if (index >= 0)
{
dst.emissiveTexture = new glTFMaterialEmissiveTextureInfo
{
index = index,
texCoord = 0,
};
ExportTextureTransform(context.EmissiveTextureOffset, context.EmissiveTextureScale, dst.emissiveTexture);
}
}
}
private static void ExportTextureTransform(Vector2 offset, Vector2 scale, glTFTextureInfo dst)
{
// NOTE: シェーダは全テクスチャに ScaleOffset を持つため、各テクスチャ固有の transform を出力する
GltfMaterialExportUtils.ExportTextureTransform(offset, scale, dst);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7347686cba894aef8e19cffcf0bd2b86
timeCreated: 1784359580

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ce9e54afa9854733b9366f41c62aac2b
timeCreated: 1784359298

View File

@@ -0,0 +1,41 @@
using UnityEngine;
namespace UniGLTF
{
/// <summary>
/// glTF 準拠 PBR ShaderGraph (UniGltfPbr) を用いる IMaterialDescriptorGenerator。
///
/// オプトイン用。ImporterContext の materialGenerator 引数に渡して使う。
/// RenderPipeline 非依存 (1 つの ShaderGraph が Built-In / URP の両 Target を持つ) なので
/// 既定の RenderPipeline 分岐 (MaterialDescriptorGeneratorUtility) は経由しない。
///
/// 分岐順は既存 generator と同じ unlit -> pbr -> default。
/// unlit は既存の BuiltInGltfUnlitMaterialImporter (UniGLTF/UniUnlit) を再利用する。
/// </summary>
public sealed class InvariantGltfMaterialDescriptorGenerator : IMaterialDescriptorGenerator
{
public InvariantRpUniGltfPbrMaterialImporter PbrMaterialImporter { get; }
public BuiltInGltfUnlitMaterialImporter UnlitMaterialImporter { get; } = new();
public InvariantGltfMaterialDescriptorGenerator(Shader pbrShader = null)
{
PbrMaterialImporter = new InvariantRpUniGltfPbrMaterialImporter(pbrShader);
}
public MaterialDescriptor Get(GltfData data, int i)
{
if (UnlitMaterialImporter.TryCreateParam(data, i, out var param)) return param;
if (PbrMaterialImporter.TryCreateParam(data, i, out param)) return param;
// NOTE: Fallback to default material
if (Symbols.VRM_DEVELOP)
{
UniGLTFLogger.Warning($"material: {i} out of range. fallback");
}
return GetGltfDefault(GltfMaterialImportUtils.ImportMaterialName(i, null));
}
public MaterialDescriptor GetGltfDefault(string materialName = null)
=> PbrMaterialImporter.CreateDefaultParam(materialName);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3fa5e67f09fd71c4c92bcd538b25ece8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3157f2650bb047adbc517f8cd35e962a
timeCreated: 1784359319

View File

@@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Rendering;
namespace UniGLTF
{
/// <summary>
/// glTF 準拠 PBR ShaderGraph (UniGltfPbr) 向けの MaterialDescriptor を生成する。
///
/// 既定の Standard / URP-Lit 経路との違い:
/// - metallicRoughnessTexture / occlusionTexture を glTF のパッキングのまま Linear で取り込む
/// (OcclusionMetallicRoughnessConverter による GPU blit + ReadPixels 変換を行わない)
/// - metallicFactor / roughnessFactor をそのまま格納する (1.0 固定や smoothness 反転をしない)
///
/// 構造は UniGLTF.UrpGltfPbrMaterialImporter を踏襲する。
/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#materials
/// </summary>
public class InvariantRpUniGltfPbrMaterialImporter
{
public Shader Shader { get; set; }
public InvariantRpUniGltfPbrMaterialImporter(Shader shader = null)
{
Shader = shader ?? UniGltfPbrContext.GetShader();
}
public bool TryCreateParam(GltfData data, int i, out MaterialDescriptor matDesc)
{
if (i < 0 || i >= data.GLTF.materials.Count)
{
matDesc = default;
return false;
}
var src = data.GLTF.materials[i];
matDesc = new MaterialDescriptor(
GltfMaterialImportUtils.ImportMaterialName(i, src),
Shader,
new[] { (MaterialDescriptor.MaterialGenerateAsyncFunc)AsyncAction }
);
return true;
Task AsyncAction(Material x, GetTextureAsyncFunc y, IAwaitCaller z) => GenerateMaterialAsync(data, src, x, y, z);
}
/// <summary>
/// glTF default material (プロパティ既定値でシェーダを割り当てるだけ)。
/// シェーダ側の既定値 (baseColor=white, metallic=1, roughness=1) が glTF default に一致する。
/// </summary>
public MaterialDescriptor CreateDefaultParam(string materialName)
{
return new MaterialDescriptor(
materialName,
Shader,
new List<MaterialDescriptor.MaterialGenerateAsyncFunc>()
);
}
public static async Task GenerateMaterialAsync(GltfData data, glTFMaterial src, Material dst, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
var context = new UniGltfPbrContext(dst);
ImportSurfaceSettings(src, context);
await ImportBaseColorAsync(data, src, context, getTextureAsync, awaitCaller);
await ImportMetallicRoughnessAsync(data, src, context, getTextureAsync, awaitCaller);
await ImportOcclusionAsync(data, src, context, getTextureAsync, awaitCaller);
await ImportNormalAsync(data, src, context, getTextureAsync, awaitCaller);
await ImportEmissionAsync(data, src, context, getTextureAsync, awaitCaller);
context.Validate();
}
public static void ImportSurfaceSettings(glTFMaterial src, UniGltfPbrContext context)
{
context.SurfaceType = src.alphaMode switch
{
"OPAQUE" => UniGltfPbrSurfaceType.Opaque,
"MASK" => UniGltfPbrSurfaceType.Opaque, // AlphaClip. Opaque queue (AlphaTest)
"BLEND" => UniGltfPbrSurfaceType.Transparent,
_ => UniGltfPbrSurfaceType.Opaque,
};
context.IsAlphaClipEnabled = src.alphaMode == "MASK";
context.AlphaCutoff = src.alphaCutoff;
context.CullMode = src.doubleSided ? CullMode.Off : CullMode.Back;
}
public static async Task ImportBaseColorAsync(GltfData data, glTFMaterial src, UniGltfPbrContext context, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
var baseColorFactor = GltfMaterialImportUtils.ImportLinearBaseColorFactor(data, src);
if (baseColorFactor.HasValue)
{
// _baseColorFactor は Default(sRGB) カラー想定なので .gamma で格納する
context.BaseColorSrgb = baseColorFactor.Value.gamma;
}
if (src is { pbrMetallicRoughness: { baseColorTexture: { index: >= 0 } } })
{
if (GltfPbrTextureImporter.TryBaseColorTexture(data, src, out _, out var desc))
{
context.BaseColorTexture = await getTextureAsync(desc, awaitCaller);
context.BaseColorTextureOffset = desc.Offset;
context.BaseColorTextureScale = desc.Scale;
}
}
}
public static async Task ImportMetallicRoughnessAsync(GltfData data, glTFMaterial src, UniGltfPbrContext context, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
if (src.pbrMetallicRoughness != null)
{
context.MetallicFactor = src.pbrMetallicRoughness.metallicFactor;
context.RoughnessFactor = src.pbrMetallicRoughness.roughnessFactor;
}
if (src is { pbrMetallicRoughness: { metallicRoughnessTexture: { index: >= 0 } } })
{
var info = src.pbrMetallicRoughness.metallicRoughnessTexture;
var (offset, scale) = GltfTextureImporter.GetTextureOffsetAndScale(info);
// NOTE: glTF の ORM パッキングをそのまま Linear で取り込む (変換なし)
if (GltfTextureImporter.TryCreateLinear(data, info.index, offset, scale, out _, out var desc))
{
context.MetallicRoughnessTexture = await getTextureAsync(desc, awaitCaller);
context.MetallicRoughnessTextureOffset = desc.Offset;
context.MetallicRoughnessTextureScale = desc.Scale;
}
}
}
public static async Task ImportOcclusionAsync(GltfData data, glTFMaterial src, UniGltfPbrContext context, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
if (src is { occlusionTexture: { index: >= 0 } })
{
context.OcclusionStrength = src.occlusionTexture.strength;
var (offset, scale) = GltfTextureImporter.GetTextureOffsetAndScale(src.occlusionTexture);
if (GltfTextureImporter.TryCreateLinear(data, src.occlusionTexture.index, offset, scale, out _, out var desc))
{
context.OcclusionTexture = await getTextureAsync(desc, awaitCaller);
context.OcclusionTextureOffset = desc.Offset;
context.OcclusionTextureScale = desc.Scale;
}
}
}
private static async Task ImportNormalAsync(GltfData data, glTFMaterial src, UniGltfPbrContext context, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
if (src.normalTexture is { index: >= 0 })
{
if (GltfPbrTextureImporter.TryNormalTexture(data, src, out _, out var desc))
{
context.NormalTexture = await getTextureAsync(desc, awaitCaller);
context.NormalScale = src.normalTexture.scale;
context.NormalTextureOffset = desc.Offset;
context.NormalTextureScale = desc.Scale;
}
}
}
/// <summary>
/// emissiveFactor と KHR_materials_emissive_strength を分離して取り込む。
/// (GltfMaterialImportUtils.ImportLinearEmissiveFactor は strength を factor に畳み込むため使わない)
/// </summary>
private static async Task ImportEmissionAsync(GltfData data, glTFMaterial src, UniGltfPbrContext context, GetTextureAsyncFunc getTextureAsync, IAwaitCaller awaitCaller)
{
if (src.emissiveFactor != null && src.emissiveFactor.Length == 3)
{
var emissive = new Vector3(src.emissiveFactor[0], src.emissiveFactor[1], src.emissiveFactor[2]);
var linear = data.MigrationFlags.IsEmissiveFactorGamma
? emissive.ToColor3(ColorSpace.sRGB, ColorSpace.Linear)
: emissive.ToColor3(ColorSpace.Linear, ColorSpace.Linear);
context.EmissiveFactorLinear = linear;
}
var strength = 1.0f;
if (glTF_KHR_materials_emissive_strength.TryGet(src.extensions, out var emissiveStrength))
{
strength = emissiveStrength.emissiveStrength;
}
context.EmissiveStrength = strength;
if (src is { emissiveTexture: { index: >= 0 } })
{
if (GltfPbrTextureImporter.TryEmissiveTexture(data, src, out _, out var desc))
{
context.EmissiveTexture = await getTextureAsync(desc, awaitCaller);
context.EmissiveTextureOffset = desc.Offset;
context.EmissiveTextureScale = desc.Scale;
}
}
var isEmissive = context.EmissiveFactorLinear.maxColorComponent > 0 || context.EmissiveTexture != null;
context.Material.globalIlluminationFlags = isEmissive
? context.Material.globalIlluminationFlags & ~MaterialGlobalIlluminationFlags.EmissiveIsBlack
: context.Material.globalIlluminationFlags | MaterialGlobalIlluminationFlags.EmissiveIsBlack;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63c674962a3b65c4c910f8656909a10f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
namespace UniGLTF
{
/// <summary>
/// glTF PBR マテリアルの import 実装の選択。
///
/// RenderPipeline や具体的なシェーダ実装 (ShaderGraph 等) に依存しない名前とする。
/// </summary>
public enum PbrMaterialImportType
{
/// <summary>
/// 既定。Unity 標準の PBR マテリアル実装を用いる。
/// (Built-In: Unity "Standard" / URP: "Universal Render Pipeline/Lit")
/// metallicRoughness/occlusion は import 時にテクスチャ変換される。
/// </summary>
UnityStandard = 0,
/// <summary>
/// glTF 仕様に最適化 PBR マテリアル実装。
/// glTF の ORM を無変換で取り込み、単一シェーダで複数 RenderPipeline をカバーする。
/// (現状の実装は glTF 準拠 PBR ShaderGraph "UniGltfPbr")
/// </summary>
GltfCompatible = 1,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9daab18aa43e3714d9d39cadd243c5b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -7,6 +7,7 @@ namespace UniGLTF
public UrpLitMaterialExporter UrpLitExporter { get; set; } = new();
public UrpUnlitMaterialExporter UrpUnlitExporter { get; set; } = new();
public UrpUniUnlitMaterialExporter UrpUniUnlitExporter { get; set; } = new();
public InvariantRpUniGltfPbrMaterialExporter InvariantRpUniGltfPbrMaterialExporter { get; set; } = new();
public UrpFallbackMaterialExporter FallbackExporter { get; set; } = new();
public glTFMaterial ExportMaterial(Material m, ITextureExporter textureExporter, GltfExportSettings settings)
@@ -14,6 +15,7 @@ namespace UniGLTF
if (UrpLitExporter.TryExportMaterial(m, textureExporter, out var dst)) return dst;
if (UrpUnlitExporter.TryExportMaterial(m, textureExporter, out dst)) return dst;
if (UrpUniUnlitExporter.TryExportMaterial(m, textureExporter, out dst)) return dst;
if (InvariantRpUniGltfPbrMaterialExporter.TryExportMaterial(m, textureExporter, out dst)) return dst;
UniGLTFLogger.Log($"Material `{m.name}` fallbacks.");
return FallbackExporter.ExportMaterial(m, textureExporter);

View File

@@ -7,7 +7,8 @@
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:1cd941934d098654fa21a13f28346412",
"GUID:d8b63aba1907145bea998dd612889d6b"
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:9fe7352e1c749b94aa9194a537b768e6"
],
"includePlatforms": [
"Editor"

View File

@@ -0,0 +1,69 @@
using NUnit.Framework;
using UnityEngine;
using UniJSON;
namespace UniGLTF
{
/// <summary>
/// glTF 準拠 PBR ShaderGraph (UniGltfPbr) の export パススルー検証。
///
/// ShaderGraph アセット (UniGltfPbr.shadergraph) が未作成の間は Assert.Ignore で
/// スキップする。作成後に有効化される。仕様は Shaders/GltfPbr/README.md を参照。
/// </summary>
public class InvariantRpUniGltfPbrMaterialTests
{
private static Material TryCreateMaterialOrIgnore()
{
var material = new Material(UniGltfPbrContext.GetShader());
if (!material.HasProperty("_metallicFactor") || !material.HasProperty("_roughnessFactor"))
{
Assert.Ignore("UniGltfPbr shader lacks glTF-native properties (_metallicFactor / _roughnessFactor). See README.md.");
}
return material;
}
[Test]
public void MetallicRoughnessFactorPassthroughTest()
{
var material = TryCreateMaterialOrIgnore();
var context = new UniGltfPbrContext(material)
{
MetallicFactor = 0.3f,
RoughnessFactor = 0.7f,
};
var textureExporter = new TextureExporter(new EditorTextureSerializer());
Assert.IsTrue(new InvariantRpUniGltfPbrMaterialExporter().TryExportMaterial(material, textureExporter, out var dst));
// 反転しない / 1.0 に固定しない (glTF セマンティクスそのまま)
Assert.AreEqual(0.3f, dst.pbrMetallicRoughness.metallicFactor, 1e-4f);
Assert.AreEqual(0.7f, dst.pbrMetallicRoughness.roughnessFactor, 1e-4f);
}
[Test]
public void EmissiveStrengthExportTest()
{
var material = TryCreateMaterialOrIgnore();
var context = new UniGltfPbrContext(material)
{
EmissiveFactorLinear = new Color(0f, 0.5f, 0f),
EmissiveStrength = 4.0f,
};
var textureExporter = new TextureExporter(new EditorTextureSerializer());
Assert.IsTrue(new InvariantRpUniGltfPbrMaterialExporter().TryExportMaterial(material, textureExporter, out var dst));
// emissiveFactor * strength = (0, 2, 0) -> 正規化して factor=(0,1,0), strength=2
Assert.AreEqual(0f, dst.emissiveFactor[0], 1e-4f);
Assert.AreEqual(1f, dst.emissiveFactor[1], 1e-4f);
Assert.AreEqual(0f, dst.emissiveFactor[2], 1e-4f);
var json = dst.ToJson();
var parsed = json.ParseAsJson();
Assert.AreEqual(
2.0f,
parsed["extensions"]["KHR_materials_emissive_strength"]["emissiveStrength"].GetSingle(),
1e-4f);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53ffd3dc3e180594e91a4009b7d66471
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5dcbf6fd16720e14a825ae89d0654d04
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5bcc8913bbc87ef4d97c6c49948be2d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,19 @@
{
"name": "UniGLTF.UniGltfPbr.Editor",
"rootNamespace": "",
"references": [
"GUID:9fe7352e1c749b94aa9194a537b768e6",
"GUID:3eae0364be2026648bf74846acb8a731"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1559e7155b5ec8240bd4abf00fdb3d4d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,239 @@
using System.Linq;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace UniGLTF
{
/// <summary>
/// glTF 準拠 PBR ShaderGraph (UniGltfPbr) 用の Custom ShaderGUI。
///
/// - 見た目は URP/Lit に近づけ、CoreEditorUtils.DrawHeaderFoldout でセクション分けする。
/// - 不透明/透明・Alpha Clip・Render Face の切替は副作用 (blend/zwrite/renderQueue/keyword) を伴うため、
/// 生プロパティ (_Surface / _Blend / _SrcBlend ...) を直接露出せず、
/// <see cref="UniGltfPbrContext"/> の SurfaceType / IsAlphaClipEnabled / CullMode + Validate() に
/// 委譲して自動適用・隠蔽する。
///
/// アタッチ: UniGltfPbr.shadergraph の各 Target の Custom Editor GUI に "UniGLTF.UniGltfPbrShaderGUI" を設定。
/// </summary>
public class UniGltfPbrShaderGUI : ShaderGUI
{
private static class Styles
{
public static readonly GUIContent SurfaceType = new GUIContent("Surface Type", "Opaque / Transparent の切替。関連する Blend / ZWrite / RenderQueue は自動設定されます。");
public static readonly GUIContent RenderFace = new GUIContent("Render Face", "描画する面。Front(片面 / doubleSided=false) または Both(doubleSided=true)。");
public static readonly GUIContent AlphaClip = new GUIContent("Alpha Clipping", "Alpha が閾値未満のピクセルを破棄します (glTF MASK)。");
public static readonly GUIContent Threshold = new GUIContent("Threshold", "Alpha Clipping の閾値 (alphaCutoff)。");
public static readonly GUIContent BaseMap = new GUIContent("Base Map", "baseColorTexture (sRGB) と baseColorFactor。");
public static readonly GUIContent Metallic = new GUIContent("Metallic", "metallicFactor。");
public static readonly GUIContent Roughness = new GUIContent("Roughness", "roughnessFactor。");
public static readonly GUIContent MetallicRoughnessMap = new GUIContent("Metallic Roughness Map", "metallicRoughnessTexture (Linear, G:Roughness B:Metallic)。");
public static readonly GUIContent OcclusionMap = new GUIContent("Occlusion Map", "occlusionTexture (Linear, R:Occlusion)。");
public static readonly GUIContent OcclusionStrength = new GUIContent("Occlusion Strength", "occlusionTexture.strength。");
public static readonly GUIContent NormalMap = new GUIContent("Normal Map", "normalTexture と normalScale。");
public static readonly GUIContent Emission = new GUIContent("Emission", "emissiveFactor (HDR) と emissiveTexture (sRGB)。");
public static readonly GUIContent EmissiveStrength = new GUIContent("Emissive Strength", "KHR_materials_emissive_strength。");
}
// Render Face popup。ラベルは「描画する面」、CullMode は「カリングする面」で逆になる点に注意:
// Front を描画 = 背面カリング = CullMode.Back (glTF: doubleSided=false)
// Both = CullMode.Off (glTF: doubleSided=true)
// 裏面のみ描画 (CullMode.Front) は glTF 非対応のため選択肢に用意しない。
private static readonly string[] RenderFaceNames = { "Front", "Both" };
private static readonly CullMode[] RenderFaceCull = { CullMode.Back, CullMode.Off };
// foldout state (editor セッション保持)
private static bool s_surfaceOptions = true;
private static bool s_surfaceInputs = true;
private static bool s_advanced = false;
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
var baseColorFactor = FindProperty("_baseColorFactor", properties, false);
var baseColorTexture = FindProperty("_baseColorTexture", properties, false);
var metallicFactor = FindProperty("_metallicFactor", properties, false);
var roughnessFactor = FindProperty("_roughnessFactor", properties, false);
var metallicRoughnessTexture = FindProperty("_metallicRoughnessTexture", properties, false);
var occlusionTexture = FindProperty("_occlusionTexture", properties, false);
var occlusionStrength = FindProperty("_occlusionStrength", properties, false);
var normalTexture = FindProperty("_normalTexture", properties, false);
var normalScale = FindProperty("_normalScale", properties, false);
var emissiveFactor = FindProperty("_emissiveFactor", properties, false);
var emissiveTexture = FindProperty("_emissiveTexture", properties, false);
var emissiveStrength = FindProperty("_emissiveStrength", properties, false);
var alphaCutoff = FindProperty("_alphaCutoff", properties, false);
s_surfaceOptions = CoreEditorUtils.DrawHeaderFoldout("Surface Options", s_surfaceOptions);
if (s_surfaceOptions)
{
EditorGUI.indentLevel++;
DrawSurfaceOptions(materialEditor, alphaCutoff);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
}
s_surfaceInputs = CoreEditorUtils.DrawHeaderFoldout("Surface Inputs", s_surfaceInputs);
if (s_surfaceInputs)
{
EditorGUI.indentLevel++;
DrawSurfaceInputs(
materialEditor,
baseColorTexture, baseColorFactor,
metallicFactor, roughnessFactor, metallicRoughnessTexture,
occlusionTexture, occlusionStrength,
normalTexture, normalScale,
emissiveTexture, emissiveFactor, emissiveStrength);
EditorGUI.indentLevel--;
EditorGUILayout.Space();
}
s_advanced = CoreEditorUtils.DrawHeaderFoldout("Advanced Options", s_advanced);
if (s_advanced)
{
EditorGUI.indentLevel++;
DrawAdvanced(materialEditor);
EditorGUI.indentLevel--;
}
}
private static void DrawSurfaceOptions(MaterialEditor materialEditor, MaterialProperty alphaCutoff)
{
var materials = materialEditor.targets.OfType<Material>().ToArray();
if (materials.Length == 0) return;
// Surface Type (Opaque / Transparent)
{
var current = new UniGltfPbrContext(materials[0]).SurfaceType;
EditorGUI.showMixedValue = materials.Any(m => new UniGltfPbrContext(m).SurfaceType != current);
EditorGUI.BeginChangeCheck();
var next = (UniGltfPbrSurfaceType)EditorGUILayout.EnumPopup(Styles.SurfaceType, current);
if (EditorGUI.EndChangeCheck())
{
materialEditor.RegisterPropertyChangeUndo("Surface Type");
foreach (var m in materials)
{
var context = new UniGltfPbrContext(m) { SurfaceType = next };
context.Validate();
}
}
EditorGUI.showMixedValue = false;
}
// Render Face (cull)
{
var currentCull = new UniGltfPbrContext(materials[0]).CullMode;
var currentIndex = Mathf.Max(0, System.Array.IndexOf(RenderFaceCull, currentCull));
EditorGUI.showMixedValue = materials.Any(m => new UniGltfPbrContext(m).CullMode != currentCull);
EditorGUI.BeginChangeCheck();
var nextIndex = EditorGUILayout.Popup(Styles.RenderFace, currentIndex, RenderFaceNames);
if (EditorGUI.EndChangeCheck())
{
materialEditor.RegisterPropertyChangeUndo("Render Face");
foreach (var m in materials)
{
var context = new UniGltfPbrContext(m) { CullMode = RenderFaceCull[nextIndex] };
context.Validate();
}
}
EditorGUI.showMixedValue = false;
}
// Alpha Clipping
var alphaClipEnabled = new UniGltfPbrContext(materials[0]).IsAlphaClipEnabled;
{
EditorGUI.showMixedValue = materials.Any(m => new UniGltfPbrContext(m).IsAlphaClipEnabled != alphaClipEnabled);
EditorGUI.BeginChangeCheck();
var next = EditorGUILayout.Toggle(Styles.AlphaClip, alphaClipEnabled);
if (EditorGUI.EndChangeCheck())
{
materialEditor.RegisterPropertyChangeUndo("Alpha Clipping");
foreach (var m in materials)
{
var context = new UniGltfPbrContext(m) { IsAlphaClipEnabled = next };
context.Validate();
}
alphaClipEnabled = next;
}
EditorGUI.showMixedValue = false;
}
// Threshold は Alpha Clipping 有効時のみ表示 (副作用のない単純プロパティ)
if (alphaClipEnabled && alphaCutoff != null)
{
EditorGUI.indentLevel++;
materialEditor.ShaderProperty(alphaCutoff, Styles.Threshold);
EditorGUI.indentLevel--;
}
}
private static void DrawSurfaceInputs(
MaterialEditor materialEditor,
MaterialProperty baseColorTexture, MaterialProperty baseColorFactor,
MaterialProperty metallicFactor, MaterialProperty roughnessFactor, MaterialProperty metallicRoughnessTexture,
MaterialProperty occlusionTexture, MaterialProperty occlusionStrength,
MaterialProperty normalTexture, MaterialProperty normalScale,
MaterialProperty emissiveTexture, MaterialProperty emissiveFactor, MaterialProperty emissiveStrength)
{
// NOTE: シェーダは全テクスチャに Tiling/Offset (ScaleOffset) を持つため、
// テクスチャ割当時に各マップの TextureScaleOffsetProperty を表示する。
if (baseColorTexture != null && baseColorFactor != null)
{
materialEditor.TexturePropertySingleLine(Styles.BaseMap, baseColorTexture, baseColorFactor);
DrawScaleOffset(materialEditor, baseColorTexture);
}
if (metallicFactor != null) materialEditor.ShaderProperty(metallicFactor, Styles.Metallic);
if (roughnessFactor != null) materialEditor.ShaderProperty(roughnessFactor, Styles.Roughness);
if (metallicRoughnessTexture != null)
{
materialEditor.TexturePropertySingleLine(Styles.MetallicRoughnessMap, metallicRoughnessTexture);
DrawScaleOffset(materialEditor, metallicRoughnessTexture);
}
if (occlusionTexture != null)
{
var occExtra = occlusionTexture.textureValue != null ? occlusionStrength : null;
materialEditor.TexturePropertySingleLine(Styles.OcclusionMap, occlusionTexture, occExtra);
DrawScaleOffset(materialEditor, occlusionTexture);
}
if (normalTexture != null)
{
var normalExtra = normalTexture.textureValue != null ? normalScale : null;
materialEditor.TexturePropertySingleLine(Styles.NormalMap, normalTexture, normalExtra);
DrawScaleOffset(materialEditor, normalTexture);
}
if (emissiveTexture != null && emissiveFactor != null)
{
materialEditor.TexturePropertyWithHDRColor(Styles.Emission, emissiveTexture, emissiveFactor, false);
if (emissiveStrength != null)
{
EditorGUI.indentLevel++;
materialEditor.ShaderProperty(emissiveStrength, Styles.EmissiveStrength);
EditorGUI.indentLevel--;
}
DrawScaleOffset(materialEditor, emissiveTexture);
}
}
/// <summary>
/// テクスチャが割り当てられている時のみ Tiling/Offset を表示する。
/// </summary>
private static void DrawScaleOffset(MaterialEditor materialEditor, MaterialProperty textureProperty)
{
if (textureProperty.textureValue == null) return;
materialEditor.TextureScaleOffsetProperty(textureProperty);
GUILayout.Space(8);
}
private static void DrawAdvanced(MaterialEditor materialEditor)
{
materialEditor.EnableInstancingField();
materialEditor.DoubleSidedGIField();
materialEditor.RenderQueueField();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 30276155ae5852f43887835dfb0c1c55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 41730641437d69a4e8988676c461c280
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
{
"name": "UniGLTF.UniGltfPbr",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9fe7352e1c749b94aa9194a537b768e6
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,372 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace UniGLTF
{
/// <summary>
/// glTF 準拠 PBR ShaderGraph (UniGltfPbr) のマテリアルプロパティを操作するクラス。
/// import (書き込み) / export (読み取り) の双方で使う。
///
/// プロパティ名は glTF 2.0 仕様ネイティブ (baseColorFactor / metallicRoughnessTexture ...)。
/// テクスチャは glTF のパッキングをそのまま (roughness 未反転, ORM 未変換) 格納し、
/// roughness->smoothness などの変換は ShaderGraph 内の per-fragment 演算で行う。
///
/// Surface(Opaque/Transparent) / Blend / Cull / AlphaClip は ShaderGraph の
/// "Allow Material Override" により露出するプロパティで制御する。
/// 1 つの .shadergraph に Built-In / URP の 2 Target を同居させるため、
/// URP 系 (_Surface ...) と Built-In 系 (_BUILTIN_* ...) の両方を HasProperty ガード付きで扱う。
///
/// 参考: UniGLTF.UrpLitContext / UniGLTF.UrpBaseShaderContext
/// </summary>
public sealed class UniGltfPbrContext
{
public const string ShaderName = "UniGLTF/UniGltfPbr";
public static Shader GetShader() => Shader.Find(ShaderName);
// --- glTF native pbr properties ---
private static readonly int BaseColorFactorProp = Shader.PropertyToID("_baseColorFactor");
private static readonly int BaseColorTextureProp = Shader.PropertyToID("_baseColorTexture");
private static readonly int MetallicFactorProp = Shader.PropertyToID("_metallicFactor");
private static readonly int RoughnessFactorProp = Shader.PropertyToID("_roughnessFactor");
private static readonly int MetallicRoughnessTextureProp = Shader.PropertyToID("_metallicRoughnessTexture");
private static readonly int OcclusionTextureProp = Shader.PropertyToID("_occlusionTexture");
private static readonly int OcclusionStrengthProp = Shader.PropertyToID("_occlusionStrength");
private static readonly int NormalTextureProp = Shader.PropertyToID("_normalTexture");
private static readonly int NormalScaleProp = Shader.PropertyToID("_normalScale");
private static readonly int EmissiveFactorProp = Shader.PropertyToID("_emissiveFactor");
private static readonly int EmissiveTextureProp = Shader.PropertyToID("_emissiveTexture");
private static readonly int EmissiveStrengthProp = Shader.PropertyToID("_emissiveStrength");
private static readonly int AlphaCutoffProp = Shader.PropertyToID("_alphaCutoff");
// --- surface state (URP ShaderGraph target / stock URP Lit compatible) ---
private static readonly int UrpSurfaceProp = Shader.PropertyToID("_Surface");
private static readonly int UrpBlendProp = Shader.PropertyToID("_Blend");
private static readonly int UrpAlphaClipProp = Shader.PropertyToID("_AlphaClip");
private static readonly int UrpCullProp = Shader.PropertyToID("_Cull");
private static readonly int UrpZWriteProp = Shader.PropertyToID("_ZWrite");
private static readonly int UrpSrcBlendProp = Shader.PropertyToID("_SrcBlend");
private static readonly int UrpDstBlendProp = Shader.PropertyToID("_DstBlend");
private static readonly int UrpCutoffProp = Shader.PropertyToID("_Cutoff");
private const string UrpSurfaceTypeTransparentKeyword = "_SURFACE_TYPE_TRANSPARENT";
private const string UrpAlphaTestOnKeyword = "_ALPHATEST_ON";
// --- surface state (Built-In ShaderGraph target) ---
private static readonly int BuiltInSurfaceProp = Shader.PropertyToID("_BUILTIN_Surface");
private static readonly int BuiltInBlendProp = Shader.PropertyToID("_BUILTIN_Blend");
private static readonly int BuiltInAlphaClipProp = Shader.PropertyToID("_BUILTIN_AlphaClip");
private static readonly int BuiltInCullProp = Shader.PropertyToID("_BUILTIN_CullMode");
private static readonly int BuiltInZWriteProp = Shader.PropertyToID("_BUILTIN_ZWrite");
private static readonly int BuiltInSrcBlendProp = Shader.PropertyToID("_BUILTIN_SrcBlend");
private static readonly int BuiltInDstBlendProp = Shader.PropertyToID("_BUILTIN_DstBlend");
private const string BuiltInSurfaceTypeTransparentKeyword = "_BUILTIN_SURFACE_TYPE_TRANSPARENT";
private const string BuiltInAlphaTestOnKeyword = "_BUILTIN_ALPHATEST_ON";
public Material Material { get; }
private readonly HashSet<string> _declaredKeywords;
public UniGltfPbrContext(Material material)
{
Material = material;
_declaredKeywords = new HashSet<string>();
if (material != null && material.shader != null)
{
foreach (var kw in material.shader.keywordSpace.keywordNames)
{
_declaredKeywords.Add(kw);
}
}
}
// ---- base color ----
/// <summary>
/// _baseColorFactor は ShaderGraph "Default" カラー (sRGB 格納) 想定。
/// glTF の linear baseColorFactor は .gamma して格納し、export 時に sRGB->Linear で戻す。
/// (UniGLTF.UrpBaseShaderContext.BaseColorSrgb と同じ規約)
/// </summary>
public Color BaseColorSrgb
{
get => Material.GetColor(BaseColorFactorProp);
set => Material.SetColor(BaseColorFactorProp, value);
}
public Texture BaseColorTexture
{
get => Material.GetTexture(BaseColorTextureProp);
set => Material.SetTexture(BaseColorTextureProp, value);
}
public Vector2 BaseColorTextureOffset
{
get => Material.GetTextureOffset(BaseColorTextureProp);
set => Material.SetTextureOffset(BaseColorTextureProp, value);
}
public Vector2 BaseColorTextureScale
{
get => Material.GetTextureScale(BaseColorTextureProp);
set => Material.SetTextureScale(BaseColorTextureProp, value);
}
// ---- metallic roughness (glTF native packing, no conversion) ----
public float MetallicFactor
{
get => Material.GetFloat(MetallicFactorProp);
set => Material.SetFloat(MetallicFactorProp, value);
}
public float RoughnessFactor
{
get => Material.GetFloat(RoughnessFactorProp);
set => Material.SetFloat(RoughnessFactorProp, value);
}
/// <summary>
/// glTF metallicRoughnessTexture をそのまま (G=roughness, B=metallic) 格納する。
/// </summary>
public Texture MetallicRoughnessTexture
{
get => Material.GetTexture(MetallicRoughnessTextureProp);
set => Material.SetTexture(MetallicRoughnessTextureProp, value);
}
public Vector2 MetallicRoughnessTextureOffset
{
get => Material.GetTextureOffset(MetallicRoughnessTextureProp);
set => Material.SetTextureOffset(MetallicRoughnessTextureProp, value);
}
public Vector2 MetallicRoughnessTextureScale
{
get => Material.GetTextureScale(MetallicRoughnessTextureProp);
set => Material.SetTextureScale(MetallicRoughnessTextureProp, value);
}
// ---- occlusion ----
public float OcclusionStrength
{
get => Material.GetFloat(OcclusionStrengthProp);
set => Material.SetFloat(OcclusionStrengthProp, value);
}
/// <summary>
/// glTF occlusionTexture をそのまま (R=occlusion) 格納する。
/// metallicRoughnessTexture と同一テクスチャ (ORM 1 枚) の場合もある。
/// </summary>
public Texture OcclusionTexture
{
get => Material.GetTexture(OcclusionTextureProp);
set => Material.SetTexture(OcclusionTextureProp, value);
}
public Vector2 OcclusionTextureOffset
{
get => Material.GetTextureOffset(OcclusionTextureProp);
set => Material.SetTextureOffset(OcclusionTextureProp, value);
}
public Vector2 OcclusionTextureScale
{
get => Material.GetTextureScale(OcclusionTextureProp);
set => Material.SetTextureScale(OcclusionTextureProp, value);
}
// ---- normal ----
public float NormalScale
{
get => Material.GetFloat(NormalScaleProp);
set => Material.SetFloat(NormalScaleProp, value);
}
public Texture NormalTexture
{
get => Material.GetTexture(NormalTextureProp);
set => Material.SetTexture(NormalTextureProp, value);
}
public Vector2 NormalTextureOffset
{
get => Material.GetTextureOffset(NormalTextureProp);
set => Material.SetTextureOffset(NormalTextureProp, value);
}
public Vector2 NormalTextureScale
{
get => Material.GetTextureScale(NormalTextureProp);
set => Material.SetTextureScale(NormalTextureProp, value);
}
// ---- emissive ----
/// <summary>
/// _emissiveFactor は HDR カラー (linear 直格納) 想定。
/// (UniGLTF.UrpLitContext.EmissionColorLinear と同じ規約)
/// </summary>
public Color EmissiveFactorLinear
{
get => Material.GetColor(EmissiveFactorProp);
set => Material.SetColor(EmissiveFactorProp, value);
}
public Texture EmissiveTexture
{
get => Material.GetTexture(EmissiveTextureProp);
set => Material.SetTexture(EmissiveTextureProp, value);
}
public Vector2 EmissiveTextureOffset
{
get => Material.GetTextureOffset(EmissiveTextureProp);
set => Material.SetTextureOffset(EmissiveTextureProp, value);
}
public Vector2 EmissiveTextureScale
{
get => Material.GetTextureScale(EmissiveTextureProp);
set => Material.SetTextureScale(EmissiveTextureProp, value);
}
/// <summary>
/// KHR_materials_emissive_strength の emissiveStrength (default 1)。
/// </summary>
public float EmissiveStrength
{
get => Material.HasProperty(EmissiveStrengthProp) ? Material.GetFloat(EmissiveStrengthProp) : 1.0f;
set => Material.SetFloat(EmissiveStrengthProp, value);
}
public float AlphaCutoff
{
get => Material.GetFloat(AlphaCutoffProp);
set => Material.SetFloat(AlphaCutoffProp, value);
}
// ---- surface / alpha / cull (material-backed; URP と Built-In の両プロパティを設定) ----
public UniGltfPbrSurfaceType SurfaceType
{
get
{
if (Material.HasProperty(UrpSurfaceProp)) return (UniGltfPbrSurfaceType)(int)Material.GetFloat(UrpSurfaceProp);
if (Material.HasProperty(BuiltInSurfaceProp)) return (UniGltfPbrSurfaceType)(int)Material.GetFloat(BuiltInSurfaceProp);
return UniGltfPbrSurfaceType.Opaque;
}
set
{
SetFloatIfExists(UrpSurfaceProp, (float)value);
SetFloatIfExists(BuiltInSurfaceProp, (float)value);
}
}
public bool IsAlphaClipEnabled
{
get
{
if (Material.HasProperty(UrpAlphaClipProp)) return Material.GetFloat(UrpAlphaClipProp) >= 0.5f;
if (Material.HasProperty(BuiltInAlphaClipProp)) return Material.GetFloat(BuiltInAlphaClipProp) >= 0.5f;
return false;
}
set
{
SetFloatIfExists(UrpAlphaClipProp, value ? 1.0f : 0.0f);
SetFloatIfExists(BuiltInAlphaClipProp, value ? 1.0f : 0.0f);
}
}
public CullMode CullMode
{
get
{
if (Material.HasProperty(UrpCullProp)) return (CullMode)(int)Material.GetFloat(UrpCullProp);
if (Material.HasProperty(BuiltInCullProp)) return (CullMode)(int)Material.GetFloat(BuiltInCullProp);
return CullMode.Back;
}
set
{
SetFloatIfExists(UrpCullProp, (float)value);
SetFloatIfExists(BuiltInCullProp, (float)value);
Material.doubleSidedGI = value != CullMode.Back;
}
}
/// <summary>
/// 現在の SurfaceType / IsAlphaClipEnabled から、派生するキーワード・ブレンド・renderQueue を反映する。
/// (surface/cull の float 自体は setter で設定済み)
/// 存在しないプロパティは HasProperty で読み飛ばす。
/// </summary>
public void Validate()
{
var isOpaque = SurfaceType == UniGltfPbrSurfaceType.Opaque;
var alphaClip = IsAlphaClipEnabled;
Material.SetOverrideTag("RenderType", (isOpaque, alphaClip) switch
{
(true, false) => "Opaque",
(true, true) => "TransparentCutout",
(false, _) => "Transparent",
});
Material.renderQueue = (isOpaque, alphaClip) switch
{
(true, false) => (int)RenderQueue.Geometry,
(true, true) => (int)RenderQueue.AlphaTest,
(false, _) => (int)RenderQueue.Transparent,
};
var srcBlend = isOpaque ? (float)BlendMode.One : (float)BlendMode.SrcAlpha;
var dstBlend = isOpaque ? (float)BlendMode.Zero : (float)BlendMode.OneMinusSrcAlpha;
var zWrite = isOpaque ? 1.0f : 0.0f;
// URP target
SetFloatIfExists(UrpBlendProp, 0.0f); // 0 == Alpha
SetFloatIfExists(UrpZWriteProp, zWrite);
SetFloatIfExists(UrpSrcBlendProp, srcBlend);
SetFloatIfExists(UrpDstBlendProp, dstBlend);
SetKeywordIfDeclared(UrpSurfaceTypeTransparentKeyword, !isOpaque);
SetKeywordIfDeclared(UrpAlphaTestOnKeyword, alphaClip);
Material.SetShaderPassEnabled("DepthOnly", isOpaque);
Material.SetShaderPassEnabled("ShadowCaster", isOpaque);
// Built-In target
SetFloatIfExists(BuiltInBlendProp, 0.0f);
SetFloatIfExists(BuiltInZWriteProp, zWrite);
SetFloatIfExists(BuiltInSrcBlendProp, srcBlend);
SetFloatIfExists(BuiltInDstBlendProp, dstBlend);
SetKeywordIfDeclared(BuiltInSurfaceTypeTransparentKeyword, !isOpaque);
SetKeywordIfDeclared(BuiltInAlphaTestOnKeyword, alphaClip);
// cutoff: URP の _Cutoff と本シェーダ独自の _alphaCutoff の両方があれば揃える
if (Material.HasProperty(UrpCutoffProp) && Material.HasProperty(AlphaCutoffProp))
{
Material.SetFloat(UrpCutoffProp, Material.GetFloat(AlphaCutoffProp));
}
}
private void SetFloatIfExists(int prop, float value)
{
if (Material.HasProperty(prop))
{
Material.SetFloat(prop, value);
}
}
private void SetKeywordIfDeclared(string keyword, bool enabled)
{
if (_declaredKeywords.Contains(keyword))
{
if (enabled)
{
Material.EnableKeyword(keyword);
}
else
{
Material.DisableKeyword(keyword);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 819191e09406ecb46b8513d6bb471e87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace UniGLTF
{
public enum UniGltfPbrSurfaceType
{
Opaque = 0,
Transparent = 1,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d009b34824a54913a3422fd5c1584ff6
timeCreated: 1784357213

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 433bd749d686e5940842f4c432afadcf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e8ba9e3a727ae804aa8aeda83529a9e3
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3}

View File

@@ -15,7 +15,8 @@
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.test-framework": "1.4.6",
"com.unity.mathematics": "1.2.6"
"com.unity.mathematics": "1.2.6",
"com.unity.shadergraph": "12.1.12"
},
"samples": [
{

View File

@@ -155,7 +155,8 @@
"com.unity.modules.imgui": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.test-framework": "1.4.6",
"com.unity.mathematics": "1.2.6"
"com.unity.mathematics": "1.2.6",
"com.unity.shadergraph": "12.1.12"
}
},
"com.vrmc.univrm": {