翻譯9 Unity Shader GUI Extension一篇就夠

自定義Shader GUI面板拓展
混合金屬與非金屬效果
非均勻平滑
表面自發光算法

GUI拓展有兩篇文章,主要方便給策劃和美術使用。api

1 自定義界面

自定義Shader界面功能,與自定義GUI面板相似,區別在於重實現函數不一樣。編輯器

image image

1.1 myLightingShader vs standardide

1.1 ShaderGUI 拓展

建立一腳本繼承UnityEditor.ShaderGUI,腳本放入Editor文件夾下函數

using UnityEngine;

namespace  GUIExtension
{
    public class MyCustomShaderGUI : UnityEditor.ShaderGUI
    {
    }
}

同時在須要修改面板界面的Shader文件內,引用該類文件。注意命名空間也要帶上字體

Shader "Custom/Custom/Shader_GUIExtension"
{
	SubShader{}
        SubShader{}
        SubShader{}
//只能放在全部SubShader最後調用
	CustomEditor "GUIExtension.MyCustomShaderGUI"
}

同時再次查看ShaderGUI有哪些虛函數可供重寫。優化

using UnityEngine;
namespace UnityEditor
{
    public abstract class ShaderGUI
    {
        protected ShaderGUI();

        // 參數:
        //   propertyName:Name of the material property.
        //   properties: OnGUI函數引用地址傳遞.
        // 返回結果: 沒找到返回null.
        protected static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties);

        // 參數:
        //   propertyName:Name of the material property.
        //   properties:The array of available properties.
        //   propertyIsMandatory:值true且沒有找到對應property就拋出異常
        // 返回結果:同上
        protected static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties, bool propertyIsMandatory);

        // 摘要:給這個材質選一個新shader時的回調
        public virtual void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader);

        // 參數:
        //   materialEditor:當前材質面板
        //   properties:當前選中的shader全部properties.
        public virtual void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties);
        public virtual void OnMaterialInteractivePreviewGUI(MaterialEditor materialEditor, Rect r, GUIStyle background);

	 //預覽
        public virtual void OnMaterialPreviewGUI(MaterialEditor materialEditor, Rect r, GUIStyle background);

	 //預覽
        public virtual void OnMaterialPreviewSettingsGUI(MaterialEditor materialEditor);
    }
}

第一個重實現函數就是:OnGUIthis

1.2 建立文本

建立文本與GUI拓展建立文本相似,統一使用GUILayoutspa

public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
      DoMain();
}
void DoMain()
{
      GUILayout.Label("Main Map");
}

image

1.2 label 演示3d

GUILayout.Label有多個重載函數,能夠爲文字指定各類顏色、字體、格式等效果。

image

1.3 文字效果

1.3 顯示Albedo紋理屬性

選中當前材質後,若材質使用的Shader調用了GUI拓展,則會自動讀取該Shader的全部屬性。經過重實現OnGUI函數後,獲取其參數地址就能讀取。

MaterialEditor MaterialEditor;
MaterialProperty[] materialProperties;
//MaterialEditor是面板實例
//該shader全部properties
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
        this.MaterialEditor = materialEditor;
        this.materialProperties = properties;
}

查找Albedo屬性,並將其顯示出來。Albedo是一個Texture紋理屬性,對應一張紋理和名稱描述,可以使用FindProperty和GUIContent容器

void AlbedoPropertyShow()
{
    MaterialProperty albedo = FindProperty("_MainTex", materialProperties, true);
    //displayName是shader內手寫好的名字
    GUIContent content = new GUIContent(albedo.displayName, albedo.textureValue, "this is a main texture");
    MaterialEditor.TexturePropertySingleLine(content, albedo);
}

image

1.4 Albedo紋理,tooltip

而後給該紋理增長色調Tint顯示

void AlbedoPropertyShow()
{
    MaterialProperty albedo = FindProperty("_MainTex", MaterialProperties, true);
    
MaterialProperty tint = FindProperty("_Tint", MaterialProperties, true);
    //displayName是shader內手寫好的名字
    GUIContent content = new GUIContent(albedo.displayName, albedo.textureValue, "this is a main texture");
    //重載函數
    MaterialEditor.TexturePropertySingleLine(content, albedo, 
tint
);
}

image

1.5 色調和偏移

1.4 代碼合併優化-略

1.5 顯示Normal紋理屬性

同理,有一個點在於bumpScale屬性顯示,假如沒有指定紋理時就不想顯示BumbScale屬性。

 

void NormalShow()
{
     MaterialProperty normal = FindProperty("_NormalMap", MaterialProperties, true);

     //沒有紋理時不想顯示bumpscale
     MaterialProperty bumpScale = null;
     if(normal.textureValue != null) bumpScale = FindProperty("_BumpScale", MaterialProperties, true);

      MaterialEditor.TexturePropertySingleLine(MakeGUIContent(normal), normal, bumpScale);
}

image image

1.6 隱藏屬性

1.6 顯示金屬和平滑值屬性

這兩個值主要做用於MainTex紋理,應該放在其以後位置顯示。

void SpecialShaderPropertyShow(string propertyName,string tooltip = null)
{
      MaterialProperty metallic = FindProperty(propertyName, MaterialProperties, true);
      MaterialEditor.ShaderProperty(metallic, MakeLabelGUIContent(metallic, tooltip));
}

void MetallicShow()
{
      SpecialShaderPropertyShow("_Metallic");
}
void SmoothnessShow()
{
      SpecialShaderPropertyShow("_Smoothness");
}

image

1.7 property show

//必須包圍使用, 先縮進後恢復,否則會影響後面的顯示
EditorGUI.indentLevel += 2;
MaterialEditor.ShaderProperty(metallic, MakeLabelGUIContent(metallic, tooltip));
EditorGUI.indentLevel -= 2;

image

1.8 縮進

1.7 顯示第二細節紋理

同理,不貼代碼了。

image

1.8 secondary map show

2 金屬紋理

如何混合金屬與非金屬紋理,兩個不一樣光澤的紋理如何混合?

imageimage

2.1沒有金屬紋理,質感稍顯油膩

2.1 使用Metallic 紋理

通常能夠用灰度圖標記金屬色、凹凸(視差)色。金屬標記爲1白色,非金屬向0趨近黑色。所以採樣灰度圖、高度圖alpha與diffuse混合。接着擴展GUI-略

//...
[NoScaleOffset]_MetallicMap("MetallicMap", 2D) = "white"{}
 
 
float GetMetallic(Interpolators i) {
    return tex2D(_MetallicMap, i.uv.xy).r * _Metallic;
}

image image

2.2 油膩感下降

2.2-2.3 Metallic紋理存在就隱藏metallic slider - 略

2.4 定義Metallic的Shader關鍵字

當使用metallicMap時就不能再使用metallicValue滑條,如不就會致使雙倍疊加。因此能夠用Shader關鍵字來決定使用兩者之一。MaterialEditor.target是當前inspector面板material實例,增長關鍵字能夠使用Material.EnableKeyword,禁用Material.DisableKeyword。

void SetKeyword(string keyword, bool enable)
{
        if(enable)
            targetMaterial.EnableKeyword(keyword);
        else
            targetMaterial.DisableKeyword(keyword);
}

自定義命名約定:_XXX_XX_...。而#pragma multi_compile指令會自動歸入已定義的關鍵字生成shader變體。

2.5 打開Debug模式查看

image

2.3 enable keyword

2.6 使用自定義Keyword(Features)

#pragma multi_compile _ _MATALLIC_MAP

使用multi_compile指令而後分別查看變體編譯:

1 有沒有使用Matallic貼圖都會編譯出以下排列組合

8 keyword variants used in scene:

<no keywords defined>
_MATALLIC_MAP
VERTEXLIGHT_ON
VERTEXLIGHT_ON _MATALLIC_MAP
SHADOWS_SCREEN
SHADOWS_SCREEN _MATALLIC_MAP
SHADOWS_SCREEN VERTEXLIGHT_ON
SHADOWS_SCREEN VERTEXLIGHT_ON _MATALLIC_MAP

但是,multi_compile指令會生成全部可能的排列組合變體,這些要花費大量時間編譯,並且有些keywords確實沒使用。對於自定義的shader keywords可使用shader_feature編譯指令優化哪些沒有使用的關鍵字,不生成變體,同時在構建時也會檢查關鍵字是否被使用。

2 shader_feature沒有使用Matallic貼圖,變體數量下降:

4 keyword variants used in scene:

<no keywords defined>
VERTEXLIGHT_ON
SHADOWS_SCREEN
SHADOWS_SCREEN VERTEXLIGHT_ON
那麼multi_compile與shader_feature什麼時候使用?
一、對於shader_feature最佳食用方法就是在編輯器模式,人工配置material面板屬性自動收集變體。
二、若是要在runtime使用shader_feature,就要確保全部的shader變體都被構建進應用內。固然這也是很完美的方案,可是必定要確保可以手動收集全部變體
三、對於第二點的稍次解決方案就是使用muti_compile指令

根據關鍵字啓用,自動獲取

float GetMetallic(Interpolators i) {
#if defined(_METALLIC_MAP)
    return tex2D(_MetallicMap, i.uv.xy).r;
#else
    return _Metallic;
#endif
}

2.7 ChangeCheck檢查

如今每幀調用OnGUI,會重複執行全部方法。理論上只有當material面板屬性被改變了,調用執行內部方法。Unity提供了EditorGUI.BeginChaneCheck和EditorGUI.EndChangeCheck方法。這兩個方法須要匹配使用,begin在要檢查以前的位置調用,end在結束時檢查:如有修改返回true

void MetallicMapShow()
{
        EditorGUI.BeginChangeCheck();
        MaterialProperty mp = MakerMapWithScaleShow("_MetallicMap", "_Metallic", true);
        if(EditorGUI.EndChangeCheck()){
            SetKeyword("_METALLIC_MAP", mp.textureValue);
        }
}

 

3 光滑紋理

相似金屬紋理,光滑紋理也是一張灰度圖。金屬部分很光滑,其餘部分很粗糙。Unity提供的standardShader是採樣了紋理的alpha通道,而事實上它要求金屬和光滑值合併在同一張金屬貼圖的不一樣通道(這也給了一個很好的工做流提示)。好處:一是不用採樣兩次;二是DXT5壓縮會分離RGB和A通道。固然這在二者都須要之下。

float GetSmoothness(Interpolators i) {
#if defined(_METALLIC_MAP)
    return tex2D(_MetallicMap, i.uv.xy).a  * _Smoothness;
#else
    return _Smoothness;
#endif
}

image

3.1 金屬_光滑紋理

效果缺點:DXT5nm紋理壓縮法線貼圖會形成僞影。尖銳的對角邊沒有與UV軸對齊,不能正確地近似。電路中是這種壓縮最糟糕的狀況。這種缺點在金屬和很是光滑的表面上變得清晰可見。

image

3.2 邊緣僞影

3.2 Albedo與smoothness結合

第一個工做流:對於金屬質感材質老是須要Metallic,同時也確定須要smoothness加強平滑感。

第二個工做流:不要金屬質感而要平滑,能夠把smoothness放進Albedo紋理alpha通道。這種狀況適用不須要金屬的不透明材質。

3.3 關鍵字選擇

對於多個工做流,能提供一個下拉列表項匹配就很方便。

enum SmoothnessSource {
    Uniform, Albedo, Metallic
}

當使用Uniform表明沒有關鍵字寫入。當Albedo表明包含光滑度的albedo紋理;當metallic表明包含光滑度的metallic紋理。Material實例提供了IsKeywordEnabled函數檢測關鍵字啓用。

void SmoothnessShow()
{
        Switchkeyword source = Switchkeyword.UNIFORM;

        if (IsKeyEnable(keyword_smoothness_albedo))
             source = Switchkeyword.SMOOTHNESS;

        if(IsKeyEnable(keyword_smoothness_metallic))
             source = Switchkeyword.METALLIC;

        //必須包圍使用, 先縮進後恢復,不會影響後面的顯示
        EditorGUI.indentLevel += 2;
        MakeShaderSpecialPropertyShow("_Smoothness");

        EditorGUI.indentLevel += 1;
        GUIContent gc = new GUIContent("Source");
        
        EditorGUI.BeginChangeCheck();

        EditorGUI.indentLevel += 1;
         GUIContent gc = new GUIContent("Source");
         //在這裏開始檢查是否手動修改了下拉單元,而後設置對應的關鍵字
         EditorGUI.BeginChangeCheck();
         source = (Switchkeyword)EditorGUILayout.EnumPopup(gc, source);
         if (EditorGUI.EndChangeCheck())
         {
             //RecordAction("123124");//取消
             SetKeyword(keyword_smoothness_metallic, source == Switchkeyword.METALLIC);
             SetKeyword(keyword_smoothness_albedo, source == Switchkeyword.SMOOTHNESS);
         }

         EditorGUI.indentLevel -= 3;

}

3.4 支持取消-不重要略

Unity提供了MaterialEditor.RegisterPropertyChangeUndo函數取消

3.5 Smoothness變體使用

#pragma shader_feature _ _SMOOTHNESS_ALBEDO _SMOOTHNESS_METALLIC

先檢查是否使用albedo關鍵字做爲平滑,而後檢查smoothness和metallic做爲平滑(當開啓金屬確定會有平滑需求)。而後與_Smoothness疊加疊加:1不變,拉動滑條能二次調節。疊加這是一種效果算法,是經驗公式,寫的多、積累的多了,天然就能寫出本身的經驗公式。

float GetSmoothness(Interpolators i) {
    float smoothness = 1;
#if defined(_SMOOTHNESS_ALBEDO)
    smoothness = tex2D(_MainTex, i.uv.xy).a;
#elif defined(_SMOOTHNESS_METALLIC) && defined(_METALLIC_MAP)
    smoothness = tex2D(_MetallicMap, i.uv.xy).a ;
#endif
    return smoothness 
* _Smoothness
;
}

3.6 查看Smoothness_Albedo

image

image image

3.3 uniform vs albedo

4 自發光

經過模擬材質表面光源效果,只須要在basePase採樣一次便可。

[NoScaleOffset]_EmissionMap("EmissionMap", 2D) = "white"{}
_Emission("Emission", Color) = (0,0,0)//默認黑色,疊加

//base pass
#pragma shader_feature _ _EMISSION_MAP
 
 
//自發光采樣
float3 GetEmission(Interpolators i) {
#ifdef FORWARD_BASE_PASS
    #ifdef _EMISSION_MAP
        return tex2D(_EmissionMap, i.uv.zw).rgb;
    #else
        return _Emission;
    #endif
#endif
    return 0;
}
 
 
//片元函數
final.rgb += GetEmission(i);

image

4.1 emission

4.1 HDR Emission

HDR:顏色的RGB份量能夠大於1,建立bloom效果。

Unity中MaterialEditor正好提供了TexturePropertyWithHDRColor函數,須要參數一是顏色範圍;參數二是否須要alpha通道。

顏色範圍有ColorPickerHDRConfig對象聲明,亮度範圍和曝光範圍。先取StandardShader數值(0,99, 1/99, 3)

Rect r = MaterialEditor.TexturePropertyWithHDRColor
(
    MakeMapGUIContent(mapinfo, null),
    mapinfo,
    bumpScale,
    config,
    false
);

image

image

相關文章
相關標籤/搜索