翻译9 Unity Shader GUI Extension一篇就够
自定義Shader GUI面板拓展
混合金屬與非金屬效果
非均勻平滑
表面自發光
GUI拓展有兩篇文章,主要方便給策劃和美術使用。
1 自定義界面
自定義Shader界面功能,與自定義GUI面板類似,區別在于重實現函數不同。
1.1 myLightingShader vs standard
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);
}
}
第一個重實現函數就是:OnGUI
1.2 創建文本
創建文本與GUI拓展創建文本類似,統一使用GUILayout
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
DoMain();
}
void DoMain()
{
GUILayout.Label("Main Map");
}
1.2 label 演示
GUILayout.Label有多個重載函數,可以為文字指定各種顏色、字體、格式等效果。
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);
}
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
); }
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);
}
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");
}
1.7 property show
//必須包圍使用, 先縮進后恢復,不然會影響后面的顯示 EditorGUI.indentLevel += 2; MaterialEditor.ShaderProperty(metallic, MakeLabelGUIContent(metallic, tooltip)); EditorGUI.indentLevel -= 2;
1.8 縮進
1.7 顯示第二細節紋理
同理,不貼代碼了。
1.8 secondary map show
2 金屬紋理
如何混合金屬與非金屬紋理,兩個不同光澤的紋理如何混合?
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;
}
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模式查看
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何時使用?
1、對于shader_feature最佳食用方法就是在編輯器模式,人工配置material面板屬性自動收集變體。 2、如果要在runtime使用shader_feature,就要確保所有的shader變體都被構建進應用內。當然這也是很完美的方案,但是一定要確保能夠手動收集所有變體 3、對于第二點的稍次解決方案就是使用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
}
3.1 金屬_光滑紋理
效果缺點:DXT5nm紋理壓縮法線貼圖會造成偽影。尖銳的對角邊沒有與UV軸對齊,不能正確地近似。電路中是這種壓縮最糟糕的情況。這種缺點在金屬和非常光滑的表面上變得清晰可見。
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
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);
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
);
總結
以上是生活随笔為你收集整理的翻译9 Unity Shader GUI Extension一篇就够的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年支付宝黄金基金选哪只好?三只基
- 下一篇: 两步验证杀手锏:Java 接入 Goog