自定义Shader GUI面板拓展
混合金属与非金属效果
非均匀平滑
表面自发光算法
GUI拓展有两篇文章,主要方便给策划和美术使用。api
自定义Shader界面功能,与自定义GUI面板相似,区别在于重实现函数不一样。编辑器
1.1 myLightingShader vs standardide
建立一脚本继承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
建立文本与GUI拓展建立文本相似,统一使用GUILayoutspa
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { DoMain(); } void DoMain() { GUILayout.Label("Main Map"); }
1.2 label 演示3d
GUILayout.Label有多个重载函数,能够为文字指定各类颜色、字体、格式等效果。
1.3 文字效果
选中当前材质后,若材质使用的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 色调和偏移
同理,有一个点在于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 隐藏属性
这两个值主要做用于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.8 secondary map show
如何混合金属与非金属纹理,两个不一样光泽的纹理如何混合?
2.1没有金属纹理,质感稍显油腻
通常能够用灰度图标记金属色、凹凸(视差)色。金属标记为1白色,非金属向0趋近黑色。所以采样灰度图、高度图alpha与diffuse混合。接着扩展GUI-略
//...
[NoScaleOffset]_MetallicMap("MetallicMap", 2D) = "white"{}
float GetMetallic(Interpolators i) { return tex2D(_MetallicMap, i.uv.xy).r * _Metallic; }
2.2 油腻感下降
当使用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
#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
一、对于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 }
如今每帧调用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); } }
相似金属纹理,光滑纹理也是一张灰度图。金属部分很光滑,其余部分很粗糙。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 边缘伪影
第一个工做流:对于金属质感材质老是须要Metallic,同时也确定须要smoothness加强平滑感。
第二个工做流:不要金属质感而要平滑,能够把smoothness放进Albedo纹理alpha通道。这种状况适用不须要金属的不透明材质。
对于多个工做流,能提供一个下拉列表项匹配就很方便。
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;
}
Unity提供了MaterialEditor.RegisterPropertyChangeUndo函数取消
#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.3 uniform vs albedo
经过模拟材质表面光源效果,只须要在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
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
);