mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-13 14:04:29 -05:00
Merge pull request #987 from Santarh/fastMetallicRoughnessImport
Import Metallic-Smoothness with Occlusion texture with shader implementation. (faster!)
This commit is contained in:
commit
ff9a2cc80b
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5ef7bdb14a8f23043805e41692d10787
|
||||
timeCreated: 1528269709
|
||||
licenseType: Pro
|
||||
guid: fa5d94522a5d93442a7515698d7ab799
|
||||
timeCreated: 1533558728
|
||||
licenseType: Free
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user