Unity HDRP快速制作Mask Map

在HDRP管线中,材质需要使用Mask Map贴图,这个贴图由四张子图合成。子图都是灰度图,灰度图只有一个通道,灰度图每个像素的值由8位表示,8位能表示的无符号整数范围为0~255。Mask Map是RGBA图,其有四个通道,每个通道由8位表示,共32位。每个通道的值的范围也为0~255。

因此,可以用四个灰度图构成一个RGBA图,RGBA的每个通道的值为对应的灰度图的像素值。这便是制作Mask Map贴图的原理。

在substance painter中可以直接输出Mask Map贴图;我们也可以在PS中通过合并通道的方式制作Mask Map贴图。但对程序来说,最好还是在Unity当中快速制作。

有些RGB图是灰色的,不是常见的彩色,我们也把这种图叫灰度图,其特点是同一个像素点的RGB三个通道的值是一样的。(由于算法不同,不一定完全一样)

在Unity的Inspector面板中,即使是单通道灰度图,会处理或显示为三通道的灰度图。

【操作界面】

将下面的代码直接复制粘贴后,可以在下图中找到打开操作界面的位置。注意要在unity当中将颜色空间改为线性空间。

在下方的操作界面中,可以拖进来对应的纹理,可以在Preview Output中预览当前的合成结果,点击下面的按钮会生成Mask Map贴图。

实现和注释看下文的代码。

【代码原理】

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

public class MaskMapEditor : EditorWindow//扩展编辑器要继承EditorWindow
{
    private Texture2D metallic, occlusion, detailmask, smoothness;//四个纹理字段
    private Texture2D _metallic, _occlusion, _detailmask, _smoothness;//这四个字段用于预览的实现
    private Material mat;//带有Shader的材质
    private bool changed;

    [MenuItem("HDRP/CreateMaskMap")]
    static void OpenMaskMapEditor()//实现点击菜单栏的按钮弹出窗口
    {
        MaskMapEditor maskMapEditor = GetWindow<MaskMapEditor>(false, "MaskMap Editor");//创建窗口
        maskMapEditor.titleContent.text = "MaskMap Editor";//给窗口命名
        maskMapEditor.Init();//初始化
    }

    private void Init()
    {
        if (mat == null)
        {
            mat = new Material(Shader.Find("Hidden/MaskMap"));//根据MaskMap这个Shader创建材质
            mat.hideFlags = HideFlags.HideAndDontSave;//材质不保存
        }
    }

    public void OnGUI()
    {
        GetInput();
        Preview();
        CreateMask();
    }

    private void CreateMask()
    {
        if (GUILayout.Button("Create Mask Map"))
        {
            GenPicture();
           
        }
    }

    private void GenPicture()
    {
        Texture2D output = GetTexture();
        
        string savePath = EditorUtility.SaveFilePanel("Save", Application.dataPath, "texture.png", "png");
        File.WriteAllBytes(savePath,output.EncodeToPNG());//将纹理保存为png格式,也可以是jpg、exr等格式
        AssetDatabase.Refresh();//更新,要不然在Unity当中不会看到生成的图片(在win的文件管理器中可以看到)
    }

    private Texture2D GetTexture()
    {
        if (metallic != null)
        {
            mat.SetTexture("_Metallic", metallic);//给Shader的属性赋值
        }

        if (occlusion != null)
        {
            mat.SetTexture("_Occlusion", occlusion);
        }

        if (detailmask != null)
        {
            mat.SetTexture("_DetailMask", detailmask);
        }

        if (smoothness != null)
        {
            mat.SetTexture("_Smoothness", smoothness);
        }

        RenderTexture tempRT = new RenderTexture(2048, 2048, 32, RenderTextureFormat.ARGB32);//生成纹理,分辨率可以自己改为1024的,也可以自己在编辑器上做出多个可供选择的分辨率,容易实现
        tempRT.Create();
        Texture2D temp2 = new Texture2D(tempRT.width, tempRT.height, TextureFormat.ARGB32, false);
        Graphics.Blit(temp2, tempRT, mat);//将temp2纹理的值通过mat赋值到tempRT,核心的代码,可以好好看看对这个方法的解释
        RenderTexture prev = RenderTexture.active;
        RenderTexture.active = tempRT;//设置当前active的纹理

        Texture2D output = new Texture2D(tempRT.width, tempRT.height, TextureFormat.ARGB32, false);//创建一个RGBA格式的纹理
        output.ReadPixels(new Rect(0, 0, tempRT.width, tempRT.height), 0, 0);//读取当前active的纹理            
        output.Apply();//apply将改变写入
        RenderTexture.active = prev;

        return output;
    }


    private void GetInput()
    {
        EditorGUILayout.BeginHorizontal("Box");
        GUILayout.Label("Input Textures");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal("Box");
        _metallic = TextureField("metallic", metallic);
        if (_metallic != metallic)
        {
            metallic = _metallic;
            changed = true;
        }
        _occlusion = TextureField("occlusion", occlusion);
        if (_occlusion != occlusion)
        {
            occlusion = _occlusion;
            changed = true;
        }
        _detailmask = TextureField("detailmask", detailmask);
        if (_detailmask != detailmask)
        {
            detailmask = _detailmask;
            changed = true;
        }
        _smoothness = TextureField("smoothness", smoothness);
        if (_smoothness != smoothness)
        {
            smoothness = _smoothness;
            changed = true;
        }
        EditorGUILayout.EndHorizontal();

    }

    private Texture2D preview = null;
    private void Preview()
    {
        EditorGUILayout.BeginHorizontal("Box");
        GUILayout.Label("Preview Output");
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal("Box");

        Rect previewRect = new Rect(this.position.width / 2-75, 250, 150, 150);
        if (preview == null)
        {
            preview = Texture2D.blackTexture;
        }

        if (changed)
        {
            preview = GetTexture();
            changed = false;
        }
        EditorGUI.DrawPreviewTexture(previewRect, preview);
        EditorGUILayout.Space(160);
        EditorGUILayout.EndHorizontal();
    }

    private Texture2D TextureField(string name,Texture2D texture)
    {
        EditorGUILayout.BeginVertical();
        var style = new GUIStyle(GUI.skin.label);
        style.alignment = TextAnchor.MiddleCenter;
        style.fixedWidth = 150;
        GUILayout.Label(name,style);
        Texture2D result = EditorGUILayout.ObjectField(texture,typeof(Texture2D),false,GUILayout.Width(150),GUILayout.Height(150)) as Texture2D;
        EditorGUILayout.EndHorizontal();
        return result;
    }
}
Shader "Hidden/MaskMap"
{
    Properties
    {
        _MainTex("Texture",2D) = "white"{}
        _Metallic("Texture", 2D) = "black"{}
        _Occlusion("Texture", 2D) = "black"{}
        _DetailMask("Texture",2D) = "black"{}
        _Smoothness("Texture",2D) = "black"{}
    }
    SubShader
    {
        Tags { "RenderType" = "Transparent" }
        LOD 100

        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;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _Metallic;
            sampler2D _Occlusion;
            sampler2D _DetailMask;
            sampler2D _Smoothness;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                col.r = tex2D(_Metallic, i.uv).r;
                col.g = tex2D(_Occlusion, i.uv).g;
                col.b = tex2D(_DetailMask, i.uv).b;
                col.a = tex2D(_Smoothness, i.uv).g;                
                col.a = pow(col.a, 0.45);      //线性空间会对a通道做gamma校正,要自己校正         
                return col;
            }
            ENDCG
        }
    }
}

【参考】

Unity HDRP管线中快速制作Mask贴图工具_郭大钦的博客-CSDN博客_mask贴图怎么做

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值