Merge pull request #987 from Santarh/fastMetallicRoughnessImport

Import Metallic-Smoothness with Occlusion texture with shader implementation. (faster!)
This commit is contained in:
ousttrue 2021-05-28 15:32:47 +09:00 committed by GitHub
commit ff9a2cc80b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 217 additions and 200 deletions

View File

@ -1,54 +0,0 @@
Shader "UniGLTF/NormalMapDecoder"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
col.xyz = (UnpackNormal(col) + 1) * 0.5;
col.w = 1;
return col;
}
ENDCG
}
}
}

View File

@ -0,0 +1,55 @@
Shader "Hidden/UniGLTF/NormalMapExporter"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
// Convert from compressed normal value to usual normal value.
col.xyz = (UnpackNormal(col) + 1) * 0.5;
col.w = 1;
return col;
}
ENDCG
}
}
}

View File

@ -0,0 +1,70 @@
Shader "Hidden/UniGLTF/StandardMapImporter"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_GltfMetallicFactor ("glTF Metallic Factor", Float) = 0.0
_GltfRoughnessFactor ("glTF Roughness Factor", Float) = 0.0
_GltfMetallicRoughnessTexture ("glTF Metallic Roughness Texture", 2D) = "black" {}
_GltfOcclusionTexture ("glTF Occlusion Texture", 2D) = "black" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
half _GltfMetallicFactor;
half _GltfRoughnessFactor;
sampler2D _GltfMetallicRoughnessTexture;
sampler2D _GltfOcclusionTexture;
fixed4 frag (v2f i) : SV_Target
{
half4 metallicRoughnessTex = tex2D(_GltfMetallicRoughnessTexture, i.uv);
half4 occlusionTex = tex2D(_GltfOcclusionTexture, i.uv);
half occlusion = occlusionTex.r; // R: glTF Occlusion
half roughness = metallicRoughnessTex.g * _GltfRoughnessFactor; // G: glTF Roughness
half metallic = metallicRoughnessTex.b * _GltfMetallicFactor; // B: glTF Metallic
fixed4 result;
result.r = metallic; // R: Unity Metallic
result.g = occlusion; // G: Unity Occlusion
result.b = 0; // B: Unity Heightmap (no use)
result.a = 1.0 - roughness; // A: Unity Smoothness
return result;
}
ENDCG
}
}
}

View File

@ -1,7 +1,7 @@
fileFormatVersion: 2
guid: 5ef7bdb14a8f23043805e41692d10787
timeCreated: 1528269709
licenseType: Pro
guid: fa5d94522a5d93442a7515698d7ab799
timeCreated: 1533558728
licenseType: Free
ShaderImporter:
defaultTextures: []
userData:

View File

@ -1,56 +0,0 @@
// Upgrade NOTE: upgraded instancing buffer 'Props' to new syntax.
Shader "UniGLTF/StandardVColor" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows vertex:vert
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float4 v_Color;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void vert(inout appdata_full v, out Input o){
UNITY_INITIALIZE_OUTPUT(Input, o);
o.v_Color = v.color;
}
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb * IN.v_Color.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

View File

@ -4,23 +4,23 @@ namespace VRMShaders
{
public static class NormalConverter
{
private static Material m_decoder;
private static Material Decoder
private static Material _exporter;
private static Material Exporter
{
get
{
if (m_decoder == null)
if (_exporter == null)
{
m_decoder = new Material(Shader.Find("UniGLTF/NormalMapDecoder"));
_exporter = new Material(Shader.Find("Hidden/UniGLTF/NormalMapExporter"));
}
return m_decoder;
return _exporter;
}
}
// Unity texture to GLTF data
public static Texture2D Export(Texture texture)
{
return TextureConverter.CopyTexture(texture, ColorSpace.Linear, false, Decoder);
return TextureConverter.CopyTexture(texture, ColorSpace.Linear, false, Exporter);
}
}
}

View File

@ -21,77 +21,42 @@ namespace VRMShaders
/// </summary>
public static class OcclusionMetallicRoughnessConverter
{
public static Texture2D Import(Texture2D metallicRoughnessTexture,
float metallicFactor, float roughnessFactor, Texture2D occlusionTexture)
private static Material exporter;
private static Material Exporter
{
// TODO: Replace with Shader implementation
if (metallicRoughnessTexture != null && occlusionTexture != null)
get
{
if (metallicRoughnessTexture == occlusionTexture)
if (exporter == null)
{
var copyMetallicRoughness = TextureConverter.CopyTexture(metallicRoughnessTexture, ColorSpace.Linear, true, null);
var metallicRoughnessPixels = copyMetallicRoughness.GetPixels32();
for (int i = 0; i < metallicRoughnessPixels.Length; ++i)
{
metallicRoughnessPixels[i] = ImportPixel(metallicRoughnessPixels[i], metallicFactor, roughnessFactor, metallicRoughnessPixels[i]);
}
copyMetallicRoughness.SetPixels32(metallicRoughnessPixels);
copyMetallicRoughness.Apply();
copyMetallicRoughness.name = metallicRoughnessTexture.name;
return copyMetallicRoughness;
exporter = new Material(Shader.Find("Hidden/UniGLTF/StandardMapImporter"));
}
else
{
var copyMetallicRoughness = TextureConverter.CopyTexture(metallicRoughnessTexture, ColorSpace.Linear, true, null);
var metallicRoughnessPixels = copyMetallicRoughness.GetPixels32();
var copyOcclusion = TextureConverter.CopyTexture(occlusionTexture, ColorSpace.Linear, false, null);
var occlusionPixels = copyOcclusion.GetPixels32();
if (metallicRoughnessPixels.Length != occlusionPixels.Length)
{
throw new NotImplementedException();
}
for (int i = 0; i < metallicRoughnessPixels.Length; ++i)
{
metallicRoughnessPixels[i] = ImportPixel(metallicRoughnessPixels[i], metallicFactor, roughnessFactor, occlusionPixels[i]);
}
copyMetallicRoughness.SetPixels32(metallicRoughnessPixels);
copyMetallicRoughness.Apply();
copyMetallicRoughness.name = metallicRoughnessTexture.name;
DestroyTexture(copyOcclusion);
return copyMetallicRoughness;
}
}
else if (metallicRoughnessTexture != null)
{
var copyTexture = TextureConverter.CopyTexture(metallicRoughnessTexture, ColorSpace.Linear, true, null);
copyTexture.SetPixels32(copyTexture.GetPixels32().Select(x => ImportPixel(x, metallicFactor, roughnessFactor, default)).ToArray());
copyTexture.Apply();
copyTexture.name = metallicRoughnessTexture.name;
return copyTexture;
}
else if (occlusionTexture != null)
{
var copyTexture = TextureConverter.CopyTexture(occlusionTexture, ColorSpace.Linear, true, null);
copyTexture.SetPixels32(copyTexture.GetPixels32().Select(x => ImportPixel(default, metallicFactor, roughnessFactor, x)).ToArray());
copyTexture.Apply();
copyTexture.name = occlusionTexture.name;
return copyTexture;
}
else
{
throw new ArgumentNullException("no texture");
return exporter;
}
}
public static Color32 ImportPixel(Color32 metallicRoughness, float metallicFactor, float roughnessFactor, Color32 occlusion)
public static Texture2D Import(Texture2D metallicRoughnessTexture,
float metallicFactor, float roughnessFactor, Texture2D occlusionTexture)
{
var dst = new Color32
if (metallicRoughnessTexture == null && occlusionTexture == null)
{
r = (byte)(metallicRoughness.b * metallicFactor), // Metallic
g = occlusion.r, // Occlusion
b = 0, // not used
a = (byte)(255 - metallicRoughness.g * roughnessFactor), // Roughness to Smoothness
};
throw new ArgumentNullException("no texture");
}
var src = metallicRoughnessTexture != null ? metallicRoughnessTexture : occlusionTexture;
Exporter.mainTexture = src;
Exporter.SetTexture("_GltfMetallicRoughnessTexture", metallicRoughnessTexture);
Exporter.SetTexture("_GltfOcclusionTexture", occlusionTexture);
Exporter.SetFloat("_GltfMetallicFactor", metallicFactor);
Exporter.SetFloat("_GltfRoughnessFactor", roughnessFactor);
var dst = TextureConverter.CopyTexture(src, ColorSpace.Linear, true, Exporter);
Exporter.mainTexture = null;
Exporter.SetTexture("_GltfMetallicRoughnessTexture", null);
Exporter.SetTexture("_GltfOcclusionTexture", null);
Exporter.SetFloat("_GltfMetallicFactor", 0);
Exporter.SetFloat("_GltfRoughnessFactor", 0);
return dst;
}
@ -146,7 +111,7 @@ namespace VRMShaders
}
else if (occlusionTexture)
{
var dst = TextureConverter.CreateEmptyTextureWithSettings(metallicSmoothTexture, ColorSpace.Linear, false);
var dst = TextureConverter.CreateEmptyTextureWithSettings(occlusionTexture, ColorSpace.Linear, false);
var linearOcclusion = TextureConverter.CopyTexture(occlusionTexture, ColorSpace.Linear, false, null);
dst.SetPixels32(linearOcclusion.GetPixels32().Select(x => ExportPixel(default, smoothness, x)).ToArray());
dst.Apply();

View File

@ -1,3 +1,4 @@
using System.Linq;
using NUnit.Framework;
using UnityEngine;
@ -5,6 +6,32 @@ namespace VRMShaders
{
public class MetallicRoughnessConverterTests
{
private Color32 ImportPixel(Color32 metallicRoughnessPixel, float metallicFactor, float roughnessFactor, Color32 occlusionPixel)
{
var metallicRoughnessTexture = new Texture2D(4, 4, TextureFormat.ARGB32, mipChain: false, linear: true);
metallicRoughnessTexture.SetPixels32(Enumerable.Range(0, 16).Select(_ => metallicRoughnessPixel).ToArray());
metallicRoughnessTexture.Apply();
var occlusionTexture = new Texture2D(4, 4, TextureFormat.ARGB32, mipChain: false, linear: true);
occlusionTexture.SetPixels32(Enumerable.Range(0, 16).Select(_ => occlusionPixel).ToArray());
occlusionTexture.Apply();
var converted = OcclusionMetallicRoughnessConverter.Import(
metallicRoughnessTexture,
metallicFactor,
roughnessFactor,
occlusionTexture
);
var result = converted.GetPixels32()[0];
UnityEngine.Object.DestroyImmediate(metallicRoughnessTexture);
UnityEngine.Object.DestroyImmediate(occlusionTexture);
UnityEngine.Object.DestroyImmediate(converted);
return result;
}
[Test]
public void ExportingColorTest()
{
@ -48,46 +75,56 @@ namespace VRMShaders
{
var roughnessFactor = 1.0f;
Assert.That(
OcclusionMetallicRoughnessConverter.ImportPixel(new Color32(255, 255, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : Same metallic (src.r)
// g <- 0 : (Unused)
ImportPixel(new Color32(255, 255, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : metallic (metallicRoughness.b * metallicFactor)
// g <- 0 : occlusion (occlusion.r)
// b <- 0 : (Unused)
// a <- 0 : ((1 - sqrt(src.g(as float) * roughnessFactor)))(as uint8)
// a <- 0 : smoothness (1.0 - (metallicRoughness.g * roughnessFactor))
Is.EqualTo(new Color32(255, 0, 0, 0)));
}
{
var roughnessFactor = 1.0f;
Assert.That(
OcclusionMetallicRoughnessConverter.ImportPixel(new Color32(255, 128, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : Same metallic (src.r)
// g <- 0 : (Unused)
ImportPixel(new Color32(255, 127, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : metallic (metallicRoughness.b * metallicFactor)
// g <- 0 : occlusion (occlusion.r)
// b <- 0 : (Unused)
// a <- 128 : ((1 - sqrt(src.g(as float) * roughnessFactor)))(as uint8)
Is.EqualTo(new Color32(255, 0, 0, 127))); // smoothness 0.5 * src.a 1.0
// a <- 128 : smoothness (1.0 - (metallicRoughness.g * roughnessFactor))
Is.EqualTo(new Color32(255, 0, 0, 128))); // A:smoothness = 1.0 - (0.5 * 1.0) = 0.5
}
{
var roughnessFactor = 0.5f;
Assert.That(
OcclusionMetallicRoughnessConverter.ImportPixel(new Color32(255, 255, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : Same metallic (src.r)
// g <- 0 : (Unused)
ImportPixel(new Color32(255, 127, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : metallic (metallicRoughness.b * metallicFactor)
// g <- 0 : occlusion (occlusion.r)
// b <- 0 : (Unused)
// a <- 74 : ((1 - sqrt(src.g(as float) * roughnessFactor)))(as uint8)
Is.EqualTo(new Color32(255, 0, 0, 127)));
// a <- 191 : smoothness (1.0 - (metallicRoughness.g * roughnessFactor))
Is.EqualTo(new Color32(255, 0, 0, 191))); // A:smoothness = 1.0 - (0.5 * 0.5) = 0.75
}
{
var roughnessFactor = 0.0f;
Assert.That(
OcclusionMetallicRoughnessConverter.ImportPixel(new Color32(255, 255, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : Same metallic (src.r)
// g <- 0 : (Unused)
ImportPixel(new Color32(255, 255, 255, 255), 1.0f, roughnessFactor, default),
// r <- 255 : metallic (metallicRoughness.b * metallicFactor)
// g <- 0 : occlusion (occlusion.r)
// b <- 0 : (Unused)
// a <- 255 : ((1 - sqrt(src.g(as float) * roughnessFactor)))(as uint8)
// a <- 255 : smoothness (1.0 - (metallicRoughness.g * roughnessFactor))
Is.EqualTo(new Color32(255, 0, 0, 255)));
}
{
Assert.That(
ImportPixel(new Color32(222, 200, 100, 255), 0.5f, 0.25f, new Color32(127, 0, 0, 0)),
// r <- 50 : metallic (metallicRoughness.b * metallicFactor)
// g <- 127 : occlusion (occlusion.r)
// b <- 0 : (Unused)
// a <- 205 : smoothness (1.0 - (metallicRoughness.g * roughnessFactor))
Is.EqualTo(new Color32(50, 127, 0, 205)));
}
}
[Test]