Browse Source

Added KHR_materials_sheen extension.

Vicente Penades 5 years ago
parent
commit
aa02be8208

+ 1 - 0
build/SharpGLTF.CodeGen/Constants.cs

@@ -45,6 +45,7 @@ namespace SharpGLTF
 
         public static string KhronosPbrClearCoatSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_materials_clearcoat", "schema", "glTF.KHR_materials_clearcoat.schema.json");
         public static string KhronosPbrTransmissionSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_materials_transmission", "schema", "glTF.KHR_materials_transmission.schema.json");
+        public static string KhronosPbrSheenSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_materials_sheen", "schema", "glTF.KHR_materials_sheen.schema.json");
         public static string KhronosUnlitSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_materials_unlit", "schema", "glTF.KHR_materials_unlit.schema.json");
         public static string KhronosModelLightsPunctualSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_lights_punctual", "schema", "glTF.KHR_lights_punctual.schema.json");
         public static string KhronosNodeLightsPunctualSchemaFile => System.IO.Path.Combine(KhronosSchemaDir, "KHR_lights_punctual", "schema", "node.KHR_lights_punctual.schema.json");

+ 27 - 2
build/SharpGLTF.CodeGen/Program.cs

@@ -25,10 +25,11 @@ namespace SharpGLTF
             _ProcessMainSchema();
 
             // XMP
-            _ProcessKhronosXMPExtension();            
+            _ProcessKhronosXMPExtension();
 
-            // material extensions
+            // material extensions            
             _ProcessKhronosUnlitExtension();
+            _ProcessKhronosSheenExtension();
             _ProcessKhronosClearCoatExtension();
             _ProcessKhronosTransmissionExtension();
             _ProcessKhronosSpecularGlossinessExtension();
@@ -191,6 +192,29 @@ namespace SharpGLTF
             ProcessSchema("ext.Transmission.g", ctx);
         }
 
+        private static void _ProcessKhronosSheenExtension()
+        {
+            var ctx = LoadSchemaContext(Constants.KhronosPbrSheenSchemaFile);
+            ctx.IgnoredByCodeEmitter("glTF Property");
+            ctx.IgnoredByCodeEmitter("glTF Child of Root Property");
+            ctx.IgnoredByCodeEmitter("Texture Info");
+            ctx.IgnoredByCodeEmitter("Material Normal Texture Info");
+
+            
+            ctx.FindClass("KHR_materials_sheen glTF extension")
+                .GetField("sheenColorFactor")
+                .SetDataType(typeof(System.Numerics.Vector3), true)
+                .SetDefaultValue("Vector3.Zero")
+                .SetItemsRange(0);
+
+            ctx.FindClass("KHR_materials_sheen glTF extension")
+                .GetField("sheenRoughnessFactor")
+                .SetDataType(typeof(float), true)                
+                .SetItemsRange(0);
+
+            ProcessSchema("ext.Sheen.g", ctx);
+        }
+
         private static void _ProcessKhronosLightsPunctualExtension()
         {
             // Model
@@ -311,6 +335,7 @@ namespace SharpGLTF
             newEmitter.SetRuntimeName("KHR_materials_unlit glTF extension", "MaterialUnlit");            
             newEmitter.SetRuntimeName("KHR_materials_clearcoat glTF extension", "MaterialClearCoat");
             newEmitter.SetRuntimeName("KHR_materials_transmission glTF extension", "MaterialTransmission");
+            newEmitter.SetRuntimeName("KHR_materials_sheen glTF extension", "MaterialSheen");
 
             newEmitter.SetRuntimeName("KHR_xmp glTF extension", "XMPPacketsCollection");
             newEmitter.SetRuntimeName("KHR_xmp node extension", "XMPPacketReference");

+ 72 - 0
src/SharpGLTF.Core/Schema2/Generated/ext.Sheen.g.cs

@@ -0,0 +1,72 @@
+// <auto-generated/>
+
+//------------------------------------------------------------------------------------------------
+//      This file has been programatically generated; DON´T EDIT!
+//------------------------------------------------------------------------------------------------
+
+#pragma warning disable SA1001
+#pragma warning disable SA1027
+#pragma warning disable SA1028
+#pragma warning disable SA1121
+#pragma warning disable SA1205
+#pragma warning disable SA1309
+#pragma warning disable SA1402
+#pragma warning disable SA1505
+#pragma warning disable SA1507
+#pragma warning disable SA1508
+#pragma warning disable SA1652
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Numerics;
+using System.Text.Json;
+
+namespace SharpGLTF.Schema2
+{
+	using Collections;
+
+	/// <summary>
+	/// glTF extension that defines the sheen material model.
+	/// </summary>
+	partial class MaterialSheen : ExtraProperties
+	{
+	
+		private static readonly Vector3 _sheenColorFactorDefault = Vector3.Zero;
+		private Vector3? _sheenColorFactor = _sheenColorFactorDefault;
+		
+		private TextureInfo _sheenColorTexture;
+		
+		private const Single _sheenRoughnessFactorDefault = 0;
+		private const Single _sheenRoughnessFactorMinimum = 0;
+		private const Single _sheenRoughnessFactorMaximum = 1;
+		private Single? _sheenRoughnessFactor = _sheenRoughnessFactorDefault;
+		
+		private TextureInfo _sheenRoughnessTexture;
+		
+	
+		protected override void SerializeProperties(Utf8JsonWriter writer)
+		{
+			base.SerializeProperties(writer);
+			SerializeProperty(writer, "sheenColorFactor", _sheenColorFactor, _sheenColorFactorDefault);
+			SerializePropertyObject(writer, "sheenColorTexture", _sheenColorTexture);
+			SerializeProperty(writer, "sheenRoughnessFactor", _sheenRoughnessFactor, _sheenRoughnessFactorDefault);
+			SerializePropertyObject(writer, "sheenRoughnessTexture", _sheenRoughnessTexture);
+		}
+	
+		protected override void DeserializeProperty(string jsonPropertyName, ref Utf8JsonReader reader)
+		{
+			switch (jsonPropertyName)
+			{
+				case "sheenColorFactor": _sheenColorFactor = DeserializePropertyValue<Vector3?>(ref reader); break;
+				case "sheenColorTexture": _sheenColorTexture = DeserializePropertyValue<TextureInfo>(ref reader); break;
+				case "sheenRoughnessFactor": _sheenRoughnessFactor = DeserializePropertyValue<Single?>(ref reader); break;
+				case "sheenRoughnessTexture": _sheenRoughnessTexture = DeserializePropertyValue<TextureInfo>(ref reader); break;
+				default: base.DeserializeProperty(jsonPropertyName,ref reader); break;
+			}
+		}
+	
+	}
+
+}

+ 1 - 0
src/SharpGLTF.Core/Schema2/gltf.ExtensionsFactory.cs

@@ -17,6 +17,7 @@ namespace SharpGLTF.Schema2
         static ExtensionsFactory()
         {
             RegisterExtension<Material, MaterialUnlit>("KHR_materials_unlit");
+            RegisterExtension<Material, MaterialSheen>("KHR_materials_sheen");
             RegisterExtension<Material, MaterialClearCoat>("KHR_materials_clearcoat");
             RegisterExtension<Material, MaterialTransmission>("KHR_materials_transmission");
             RegisterExtension<Material, MaterialPBRSpecularGlossiness>("KHR_materials_pbrSpecularGlossiness");

+ 1 - 0
src/SharpGLTF.Core/Schema2/gltf.Material.cs

@@ -101,6 +101,7 @@ namespace SharpGLTF.Schema2
             {
                 result.MustBeNull("ClearCoat", this.GetExtension<MaterialClearCoat>());
                 result.MustBeNull("Transmission", this.GetExtension<MaterialTransmission>());
+                result.MustBeNull("Sheen", this.GetExtension<MaterialSheen>());
             }
         }
 

+ 11 - 0
src/SharpGLTF.Core/Schema2/gltf.MaterialChannel.cs

@@ -27,6 +27,17 @@ namespace SharpGLTF.Schema2
             _ParameterSetter = value => csetter(value.X);
         }
 
+        internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Vector3 defval, Func<Vector3> cgetter, Action<Vector3> csetter)
+            : this(m, key, texInfo)
+        {
+            Guard.NotNull(cgetter, nameof(cgetter));
+            Guard.NotNull(csetter, nameof(csetter));
+
+            _ParameterDefVal = new Vector4(defval, 0);
+            _ParameterGetter = () => new Vector4(cgetter(), 0);
+            _ParameterSetter = value => csetter(new Vector3(value.X, value.Y, value.Z));
+        }
+
         internal MaterialChannel(Material m, String key, Func<Boolean, TextureInfo> texInfo, Vector4 defval, Func<Vector4> cgetter, Action<Vector4> csetter)
             : this(m, key, texInfo)
         {

+ 70 - 16
src/SharpGLTF.Core/Schema2/gltf.MaterialsFactory.cs

@@ -13,6 +13,7 @@ namespace SharpGLTF.Schema2
         internal void ClearExtensions()
         {
             this.RemoveExtensions<MaterialUnlit>();
+            this.RemoveExtensions<MaterialSheen>();
             this.RemoveExtensions<MaterialClearCoat>();
             this.RemoveExtensions<MaterialTransmission>();
             this.RemoveExtensions<MaterialPBRSpecularGlossiness>();
@@ -32,15 +33,22 @@ namespace SharpGLTF.Schema2
         /// <summary>
         /// Initializes this <see cref="Material"/> instance with PBR Metallic Roughness attributes.
         /// </summary>
-        /// <param name="useClearCoat">True to enable CleatCoat extension.</param>
-        /// <param name="useTransmission">True to enable Transmission extension.</param>
-        public void InitializePBRMetallicRoughness(bool useClearCoat = false, bool useTransmission = false)
+        /// <param name="extensionNames">
+        /// Extension names.
+        /// Current valid names are: "ClearCoat", "Transmission", "Sheen"
+        /// </param>
+        public void InitializePBRMetallicRoughness(params string[] extensionNames)
         {
             if (this._pbrMetallicRoughness == null) this._pbrMetallicRoughness = new MaterialPBRMetallicRoughness();
 
             ClearExtensions();
-            if (useClearCoat) this.SetExtension(new MaterialClearCoat(this));
-            if (useTransmission) this.SetExtension(new MaterialTransmission(this));
+
+            foreach (var extn in extensionNames)
+            {
+                if (extn == "ClearCoat") this.SetExtension(new MaterialClearCoat(this));
+                if (extn == "Transmission") this.SetExtension(new MaterialTransmission(this));
+                if (extn == "Sheen") this.SetExtension(new MaterialSheen(this));
+            }
         }
 
         /// <summary>
@@ -95,6 +103,13 @@ namespace SharpGLTF.Schema2
                 foreach (var c in channels) yield return c;
             }
 
+            var sheen = this.GetExtension<MaterialSheen>();
+            if (sheen != null)
+            {
+                var channels = sheen.GetChannels(this);
+                foreach (var c in channels) yield return c;
+            }
+
             yield return new MaterialChannel
                 (
                 this, "Normal",
@@ -242,12 +257,6 @@ namespace SharpGLTF.Schema2
             return _specularGlossinessTexture;
         }
 
-        public Vector4 Color
-        {
-            get => _diffuseFactor.AsValue(_diffuseFactorDefault);
-            set => _diffuseFactor = value.AsNullable(_diffuseFactorDefault);
-        }
-
         public static Vector4 ParameterDefault => new Vector4(_specularFactorDefault, (float)_glossinessFactorDefault);
 
         public Vector4 Parameter
@@ -274,8 +283,8 @@ namespace SharpGLTF.Schema2
                 material, "Diffuse",
                 _GetDiffuseTexture,
                 _diffuseFactorDefault,
-                () => this.Color,
-                value => this.Color = value
+                () => _diffuseFactor.AsValue(_diffuseFactorDefault),
+                value => _diffuseFactor = value.AsNullable(_diffuseFactorDefault)
                 );
 
             yield return new MaterialChannel
@@ -333,7 +342,7 @@ namespace SharpGLTF.Schema2
                 _GetClearCoatTexture,
                 (float)_clearcoatFactorDefault,
                 () => (float)this._clearcoatFactor.AsValue(_clearcoatFactorDefault),
-                value => this._clearcoatFactor = value
+                value => this._clearcoatFactor = value.AsNullable((float)_clearcoatFactorDefault)
                 );
 
             yield return new MaterialChannel
@@ -342,7 +351,7 @@ namespace SharpGLTF.Schema2
                 _GetClearCoatRoughnessTexture,
                 (float)_clearcoatRoughnessFactorDefault,
                 () => (float)this._clearcoatRoughnessFactor.AsValue(_clearcoatRoughnessFactorDefault),
-                value => this._clearcoatRoughnessFactor = value
+                value => this._clearcoatRoughnessFactor = value.AsNullable((float)_clearcoatRoughnessFactorDefault)
                 );
 
             yield return new MaterialChannel
@@ -375,7 +384,7 @@ namespace SharpGLTF.Schema2
                 _GetTransmissionTexture,
                 (float)_transmissionFactorDefault,
                 () => (float)this._transmissionFactor.AsValue(_transmissionFactorDefault),
-                value => this._transmissionFactor = value
+                value => this._transmissionFactor = value.AsNullable((float)_transmissionFactorDefault)
                 );
         }
 
@@ -385,4 +394,49 @@ namespace SharpGLTF.Schema2
             return _transmissionTexture;
         }
     }
+
+    internal sealed partial class MaterialSheen
+    {
+        #pragma warning disable CA1801 // Review unused parameters
+        internal MaterialSheen(Material material) { }
+        #pragma warning restore CA1801 // Review unused parameters
+
+        protected override IEnumerable<ExtraProperties> GetLogicalChildren()
+        {
+            return base.GetLogicalChildren().ConcatItems(_sheenColorTexture, _sheenRoughnessTexture);
+        }
+
+        public IEnumerable<MaterialChannel> GetChannels(Material material)
+        {
+            yield return new MaterialChannel
+                (
+                material, "SheenColor",
+                _GetSheenColorTexture,
+                _sheenColorFactorDefault,
+                () => _sheenColorFactor.AsValue(_sheenColorFactorDefault),
+                value => this._sheenColorFactor = value.AsNullable(_sheenColorFactorDefault)
+                );
+
+            yield return new MaterialChannel
+                (
+                material, "SheenRoughness",
+                _GetSheenRoughnessTexture,
+                _sheenRoughnessFactorDefault,
+                () => _sheenRoughnessFactor.AsValue(_sheenRoughnessFactorDefault),
+                value => this._sheenRoughnessFactor = value.AsNullable(_sheenRoughnessFactorDefault)
+                );
+        }
+
+        private TextureInfo _GetSheenColorTexture(bool create)
+        {
+            if (create && _sheenColorTexture == null) _sheenColorTexture = new TextureInfo();
+            return _sheenColorTexture;
+        }
+
+        private TextureInfo _GetSheenRoughnessTexture(bool create)
+        {
+            if (create && _sheenRoughnessTexture == null) _sheenRoughnessTexture = new TextureInfo();
+            return _sheenRoughnessTexture;
+        }
+    }
 }

+ 3 - 0
src/SharpGLTF.Toolkit/Materials/ChannelBuilder.cs

@@ -186,6 +186,9 @@ namespace SharpGLTF.Materials
 
                 case KnownChannel.Transmission: return Vector4.Zero;
 
+                case KnownChannel.SheenColor: return Vector4.Zero;
+                case KnownChannel.SheenRoughness: return Vector4.Zero;
+
                 default: throw new NotImplementedException();
             }
         }

+ 2 - 0
src/SharpGLTF.Toolkit/Materials/MaterialBuilder.cs

@@ -50,6 +50,8 @@ namespace SharpGLTF.Materials
             KnownChannel.ClearCoatNormal,
             KnownChannel.ClearCoatRoughness,
             KnownChannel.Transmission,
+            KnownChannel.SheenColor,
+            KnownChannel.SheenRoughness,
         };
 
         private static readonly KnownChannel[] _SpeGloChannels = new[]

+ 3 - 0
src/SharpGLTF.Toolkit/Materials/MaterialEnums.cs

@@ -31,5 +31,8 @@ namespace SharpGLTF.Materials
         ClearCoatRoughness,
 
         Transmission,
+
+        SheenColor,
+        SheenRoughness
     }
 }

+ 27 - 11
src/SharpGLTF.Toolkit/Schema2/MaterialExtensions.cs

@@ -6,6 +6,9 @@ using System.Text;
 
 using SharpGLTF.Materials;
 
+using ALPHAMODE = SharpGLTF.Materials.AlphaMode;
+using ALPHAMODEGLTF2 = SharpGLTF.Schema2.AlphaMode;
+
 namespace SharpGLTF.Schema2
 {
     public static partial class Toolkit
@@ -169,7 +172,7 @@ namespace SharpGLTF.Schema2
 
         #region creation API
 
-        public static Material CreateMaterial(this ModelRoot root, Materials.MaterialBuilder mb)
+        public static Material CreateMaterial(this ModelRoot root, MaterialBuilder mb)
         {
             Guard.NotNull(root, nameof(root));
             Guard.NotNull(mb, nameof(mb));
@@ -181,7 +184,7 @@ namespace SharpGLTF.Schema2
             return m;
         }
 
-        public static Materials.MaterialBuilder ToMaterialBuilder(this Material srcMaterial)
+        public static MaterialBuilder ToMaterialBuilder(this Material srcMaterial)
         {
             if (srcMaterial == null) return Materials.MaterialBuilder.CreateDefault();
             var dstMaterial = new Materials.MaterialBuilder(srcMaterial.Name);
@@ -195,30 +198,33 @@ namespace SharpGLTF.Schema2
 
         #region transfer API
 
-        public static Schema2.AlphaMode ToSchema2(this Materials.AlphaMode alpha)
+        public static ALPHAMODEGLTF2 ToSchema2(this ALPHAMODE alpha)
         {
             switch (alpha)
             {
-                case Materials.AlphaMode.BLEND: return Schema2.AlphaMode.BLEND;
-                case Materials.AlphaMode.MASK: return Schema2.AlphaMode.MASK;
-                case Materials.AlphaMode.OPAQUE: return Schema2.AlphaMode.OPAQUE;
+                case ALPHAMODE.BLEND: return ALPHAMODEGLTF2.BLEND;
+                case ALPHAMODE.MASK: return ALPHAMODEGLTF2.MASK;
+                case ALPHAMODE.OPAQUE: return ALPHAMODEGLTF2.OPAQUE;
                 default: throw new NotImplementedException(alpha.ToString());
             }
         }
 
-        public static Materials.AlphaMode ToToolkit(this Schema2.AlphaMode alpha)
+        public static ALPHAMODE ToToolkit(this ALPHAMODEGLTF2 alpha)
         {
             switch (alpha)
             {
-                case Schema2.AlphaMode.BLEND: return Materials.AlphaMode.BLEND;
-                case Schema2.AlphaMode.MASK: return Materials.AlphaMode.MASK;
-                case Schema2.AlphaMode.OPAQUE: return Materials.AlphaMode.OPAQUE;
+                case ALPHAMODEGLTF2.BLEND: return ALPHAMODE.BLEND;
+                case ALPHAMODEGLTF2.MASK: return ALPHAMODE.MASK;
+                case ALPHAMODEGLTF2.OPAQUE: return ALPHAMODE.OPAQUE;
                 default: throw new NotImplementedException(alpha.ToString());
             }
         }
 
         public static void CopyTo(this Material srcMaterial, MaterialBuilder dstMaterial)
         {
+            Guard.NotNull(srcMaterial, nameof(srcMaterial));
+            Guard.NotNull(dstMaterial, nameof(dstMaterial));
+
             _CopyDefaultTo(srcMaterial, dstMaterial);
 
             if (srcMaterial.Unlit)
@@ -259,6 +265,7 @@ namespace SharpGLTF.Schema2
             srcMaterial.CopyChannelsTo(dstMaterial, "BaseColor", "MetallicRoughness");
             srcMaterial.CopyChannelsTo(dstMaterial, "ClearCoat", "ClearCoatRoughness", "ClearCoatNormal");
             srcMaterial.CopyChannelsTo(dstMaterial, "Transmission");
+            srcMaterial.CopyChannelsTo(dstMaterial, "SheenColor", "SheenRoughness");
         }
 
         private static void _CopyDefaultTo(Material srcMaterial, MaterialBuilder dstMaterial)
@@ -342,6 +349,9 @@ namespace SharpGLTF.Schema2
 
             var hasTransmission = srcMaterial.GetChannel("Transmission") != null;
 
+            var hasSheen = srcMaterial.GetChannel("SheenColor") != null
+                || srcMaterial.GetChannel("SheenRoughness") != null;
+
             srcMaterial.CopyChannelsTo(dstMaterial, "Normal", "Occlusion", "Emissive");
 
             MaterialBuilder defMaterial = null;
@@ -355,7 +365,12 @@ namespace SharpGLTF.Schema2
 
             if (srcMaterial.ShaderStyle == "PBRMetallicRoughness")
             {
-                dstMaterial.InitializePBRMetallicRoughness(hasClearCoat, hasTransmission);
+                dstMaterial.InitializePBRMetallicRoughness
+                    (
+                    hasClearCoat ? "ClearCoat" : null,
+                    hasTransmission ? "Transmission" : null,
+                    hasSheen ? "Sheen" : null);
+
                 defMaterial = srcMaterial;
             }
 
@@ -372,6 +387,7 @@ namespace SharpGLTF.Schema2
                 defMaterial.CopyChannelsTo(dstMaterial, "BaseColor", "MetallicRoughness");
                 defMaterial.CopyChannelsTo(dstMaterial, "ClearCoat", "ClearCoatRoughness", "ClearCoatNormal");
                 defMaterial.CopyChannelsTo(dstMaterial, "Transmission");
+                defMaterial.CopyChannelsTo(dstMaterial, "SheenColor", "SheenRoughness");
             }
         }
 

+ 0 - 1
tests/SharpGLTF.NUnit/TestFiles.cs

@@ -191,7 +191,6 @@ namespace SharpGLTF
 
             var skipAlways = new string[]
             {
-                "\\Sheen\\Cloth.gltf", // still a pull request https://github.com/KhronosGroup/glTF/pull/1688
                 "\\Tests\\AssetGenerator\\",
                 "\\Demos\\retargeting\\riggedMesh.glb",
                 "\\Demos\\retargeting\\riggedMesh-recycled.glb",

+ 42 - 0
tests/SharpGLTF.Tests/Schema2/Authoring/ExtensionsCreationTests.cs

@@ -162,6 +162,48 @@ namespace SharpGLTF.Schema2.Authoring
             scene.AttachToCurrentTest("result.gltf");            
         }
 
+        [Test(Description = "Creates a quad mesh with a complex material")]
+        public void CreateSceneWithsSheenExtension()
+        {
+            TestContext.CurrentContext.AttachShowDirLink();
+            TestContext.CurrentContext.AttachGltfValidatorLinks();
+
+            var basePath = System.IO.Path.Combine(TestFiles.RootDirectory, "glTF-Sample-Models", "2.0", "SpecGlossVsMetalRough", "glTF");
+
+            var material = new Materials.MaterialBuilder("material")
+                .WithMetallicRoughnessShader()
+                .WithChannelImage(Materials.KnownChannel.Normal, System.IO.Path.Combine(basePath, "WaterBottle_normal.png"))
+                .WithChannelImage(Materials.KnownChannel.Emissive, System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"))
+                .WithChannelImage(Materials.KnownChannel.Occlusion, System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"))
+                .WithChannelImage(Materials.KnownChannel.BaseColor, System.IO.Path.Combine(basePath, "WaterBottle_baseColor.png"))
+                .WithChannelImage(Materials.KnownChannel.MetallicRoughness, System.IO.Path.Combine(basePath, "WaterBottle_roughnessMetallic.png"))
+                .WithChannelImage(Materials.KnownChannel.SheenColor, System.IO.Path.Combine(basePath, "WaterBottle_emissive.png"))
+                .WithChannelParam(Materials.KnownChannel.SheenColor, new Vector4(1,1,1,0))
+                .WithChannelImage(Materials.KnownChannel.SheenRoughness, System.IO.Path.Combine(basePath, "WaterBottle_occlusion.png"))
+                .WithChannelParam(Materials.KnownChannel.SheenRoughness, new Vector4(0.5f, 0, 0, 0));
+
+            var mesh = new Geometry.MeshBuilder<VPOS, VTEX>("mesh1");
+            mesh.UsePrimitive(material).AddQuadrangle
+                ((new Vector3(-10, 10, 0), new Vector2(1, 0))
+                , (new Vector3(10, 10, 0), new Vector2(0, 0))
+                , (new Vector3(10, -10, 0), new Vector2(0, 1))
+                , (new Vector3(-10, -10, 0), new Vector2(1, 1))
+                );
+
+            var scene = new Scenes.SceneBuilder();
+            scene.AddRigidMesh(mesh, Matrix4x4.Identity);
+
+            var gltf2 = scene.ToGltf2();
+            var sheenColorFactor = gltf2.LogicalMaterials[0].FindChannel("SheenColor").Value.Parameter;
+            Assert.AreEqual(new Vector4(1, 1, 1, 0), sheenColorFactor);
+
+            var sheenRoughnessFactor = gltf2.LogicalMaterials[0].FindChannel("SheenRoughness").Value.Parameter;
+            Assert.AreEqual(new Vector4(0.5f, 0, 0, 0), sheenRoughnessFactor);
+
+            scene.AttachToCurrentTest("result.glb");
+            scene.AttachToCurrentTest("result.gltf");
+        }
+
         [TestCase("shannon-dxt5.dds")]
         [TestCase("shannon.webp")]
         [TestCase("FlightHelmet_baseColor_basis.ktx2")]