Code
脚本的注释可以按看,有些说明,与技巧
Main Testing script - 主要测试的脚本
// jave.lin 2019.06.24
using UnityEngine;
public enum AMBIENT_TOGGLE
{
On,
Off
}
public enum AMBIENT_COLOR_TYPE
{
LightModel,
Sky,
Equator,
Ground
}
public enum AMBIENT_APPLY_ONLY_R
{
On,
Off
}
public class TestMultiCompile : MonoBehaviour
{
public AMBIENT_TOGGLE ambientToggle = AMBIENT_TOGGLE.On;
public AMBIENT_COLOR_TYPE ambientColorType = AMBIENT_COLOR_TYPE.LightModel;
public AMBIENT_APPLY_ONLY_R ambientApplyOnlyR = AMBIENT_APPLY_ONLY_R.Off;
public string[] enabledLocalKeywords;
private MeshRenderer[] mrs;
// Start is called before the first frame update
void Start()
{
mrs = GetComponentsInChildren<MeshRenderer>();
//避免有时你在退出unity运行时,有些开关的状态还是退出钱的设置,所以这里需要初始化一下
//string[] allGlobalKeywords = { ... }; // 所有你想控制的全局关键字数组
//for (int i = 0; i < allGlobalKeywords.Length; i++)
//{
// Shader.DisableKeyword(allGlobalKeywords[i]);
//}
//然后有些需要Enable的,可以在下面这里再Enable
//对局部的关键字就不需要上面的处理,因为你可以从Material.shaderKeywords里很容易看出来
//对关键字的维护,可以参考TestMultiCompileEditor.cs的写法(目前是简单的写法)
}
// Update is called once per frame
void Update()
{
// 应用优先级会被Shader.material.Enable/DisableKeyword覆盖
if (ambientToggle == AMBIENT_TOGGLE.On)
Shader.EnableKeyword("AMBIENT_ON");
else
Shader.DisableKeyword("AMBIENT_ON");
// 下面的Enable/DisableKeyword都可以封装一下,用分组的方式,让同一组的互斥就好了
switch (ambientColorType)
{
case AMBIENT_COLOR_TYPE.LightModel:
Shader.EnableKeyword("LIGHTMODEL_AMBIENT");
Shader.DisableKeyword("SKY_COLOR");
Shader.DisableKeyword("EQUATOR_COLOR");
Shader.DisableKeyword("GROUND_COLOR");
break;
case AMBIENT_COLOR_TYPE.Sky:
Shader.DisableKeyword("LIGHTMODEL_AMBIENT");
Shader.EnableKeyword("SKY_COLOR");
Shader.DisableKeyword("EQUATOR_COLOR");
Shader.DisableKeyword("GROUND_COLOR");
break;
case AMBIENT_COLOR_TYPE.Equator:
Shader.DisableKeyword("LIGHTMODEL_AMBIENT");
Shader.DisableKeyword("SKY_COLOR");
Shader.EnableKeyword("EQUATOR_COLOR");
Shader.DisableKeyword("GROUND_COLOR");
break;
case AMBIENT_COLOR_TYPE.Ground:
Shader.DisableKeyword("LIGHTMODEL_AMBIENT");
Shader.DisableKeyword("SKY_COLOR");
Shader.DisableKeyword("EQUATOR_COLOR");
Shader.EnableKeyword("GROUND_COLOR");
break;
default:
break;
}
SetApplyOnlyR(ambientApplyOnlyR);
// material.shaderKeywrods只会返回局部的关键字,所以Shader.EnableKeyword设置的是不会在这增加的
enabledLocalKeywords = mrs[0].material.shaderKeywords;
}
private void SetApplyOnlyR(AMBIENT_APPLY_ONLY_R v)
{
for (int i = 0; i < mrs.Length; i++)
{
var mr = mrs[i];
switch (v)
{
case AMBIENT_APPLY_ONLY_R.On:
mr.sharedMaterial.EnableKeyword("AMBIENT_ONLY_R");
break;
case AMBIENT_APPLY_ONLY_R.Off:
mr.sharedMaterial.DisableKeyword("AMBIENT_ONLY_R");
break;
default:
break;
}
}
}
}
Editor - 编辑器Inspector扩展
// jave.lin 2019.06.24
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(TestMultiCompile))]
public class TestMultiCompileEditor : Editor
{
// 可以从配置中读取
private string[][] globalKeywordList =
{
new string[]{ "AMBIENT_ON", }, // group one
new string[]{ // group two
"LIGHTMODEL_AMBIENT",
"SKY_COLOR",
"EQUATOR_COLOR",
"GROUND_COLOR",
}
};
// 可以从配置中读取
private string[][] localKeywordList =
{
new string[] {"AMBIENT_ONLY_R", } // group
};
private Material m;
private bool globalKeywordFoldSummary = true;
private bool localKeywordFoldSummary = true;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (m == null)
m = AssetDatabase.LoadAssetAtPath<Material>("Assets/Materials/TestMulitCompile.mat");
int idx = 0;
const int indent = 1;
// global
globalKeywordFoldSummary = EditorGUILayout.Foldout(globalKeywordFoldSummary, "Global Keyword Summary");
if (globalKeywordFoldSummary)
{
EditorGUI.indentLevel += indent;
idx = 0;
foreach (var kws in globalKeywordList)
{
EditorGUILayout.Foldout(true, $"Global Keyword_Group{idx++}");
foreach (var kw in kws)
{
EditorGUILayout.Toggle(kw, Shader.IsKeywordEnabled(kw));
}
}
EditorGUI.indentLevel -= indent;
}
// local
localKeywordFoldSummary = EditorGUILayout.Foldout(localKeywordFoldSummary, "Local Keyword Summary");
if (localKeywordFoldSummary)
{
EditorGUI.indentLevel += indent;
idx = 0;
foreach (var kws in localKeywordList)
{
EditorGUILayout.Foldout(true, $"Local Keyword_Group{idx++}");
foreach (var kw in kws)
{
EditorGUILayout.Toggle(kw, m.IsKeywordEnabled(kw));
}
}
EditorGUI.indentLevel -= indent;
}
}
}
Shader Code
// jave.lin 2019.06.23
Shader "Test/TestMultiCompile"{
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ====global keyword start====
#pragma multi_compile __ AMBIENT_ON // 2个选项,定义multi_compile keyword 开关,_ 表示未选
#pragma multi_compile __ LIGHTMODEL_AMBIENT SKY_COLOR EQUATOR_COLOR GROUND_COLOR // 5个选项,定义颜色选用的关键字
// ====global keyword end====
// ====local keyword start====
#pragma multi_compile_local __ AMBIENT_ONLY_R // 2个选项,环境光颜色只有R通道的开关
// ====local keyword end====
#include "UnityCG.cginc"
float4 vert (float4 vertex : POSITION) : SV_POSITION { return UnityObjectToClipPos(vertex); }
fixed4 frag () : SV_Target{
fixed4 col = fixed4(1,1,1,1);
#if AMBIENT_ON // 判断开关而使用环境色调
#if AMBIENT_ONLY_R // 仅使用R通道的
#if LIGHTMODEL_AMBIENT // 光照综合处理后的系数
col.rgb *= UNITY_LIGHTMODEL_AMBIENT.r;
#elif SKY_COLOR
col.rgb *= unity_AmbientSky.r;
#elif EQUATOR_COLOR
col.rgb *= unity_AmbientEquator.r;
#elif GROUND_COLOR
col.rgb *= unity_AmbientGround.r;
#else
// nops
#endif
#else
#if LIGHTMODEL_AMBIENT // 光照综合处理后的系数
col.rgb *= UNITY_LIGHTMODEL_AMBIENT.rgb;
#elif SKY_COLOR
col.rgb *= unity_AmbientSky.rgb;
#elif EQUATOR_COLOR
col.rgb *= unity_AmbientEquator.rgb;
#elif GROUND_COLOR
col.rgb *= unity_AmbientGround.rgb;
#else
// nops
#endif
#endif
#endif
return col;
}
ENDCG
}
}
}
Shader Variants
图中variants included有20个
那么20个变体是怎么生成的呢?
我们才定义了那么几个关键字,生成就这么多了
在 compile and show code 右边有个下拉按钮,点击后有个小面板,再点击小面板中的 show 按钮。
Shader Variants Combines
点击面板中的 show 按钮后,我们在脚本中看到它是如何确定20个变体的
// Total snippets: 1
// -----------------------------------------
// Snippet #0 platforms ffffffff:
Keywords always included into build: AMBIENT_ON LIGHTMODEL_AMBIENT SKY_COLOR EQUATOR_COLOR GROUND_COLOR AMBIENT_ONLY_R // 我们定义的关键字列表,该shader中的全局,局部关键字都列在这
20 keyword variants used in scene: // 共有20个关键字
// 下面是所有可能的变体组合情况,这里就是变体的数量
// 我们就不要一个一个数了
// 它是通过每行的 multi_compile 的数量相乘的总数
// 例如:有3行multi_compile,每行分别有2,3,4个,那么总数就有2*3*4=24
// 所以我们的multi_compile行定义越多,影响的系数越大。
<no keywords defined> // 我们手写无定义的:__或是_,或是不写,Unity也会默认生出的项
AMBIENT_ONLY_R
LIGHTMODEL_AMBIENT
AMBIENT_ONLY_R LIGHTMODEL_AMBIENT
SKY_COLOR
AMBIENT_ONLY_R SKY_COLOR
EQUATOR_COLOR
AMBIENT_ONLY_R EQUATOR_COLOR
GROUND_COLOR
AMBIENT_ONLY_R GROUND_COLOR
AMBIENT_ON
AMBIENT_ON AMBIENT_ONLY_R
AMBIENT_ON LIGHTMODEL_AMBIENT
AMBIENT_ON AMBIENT_ONLY_R LIGHTMODEL_AMBIENT
AMBIENT_ON SKY_COLOR
AMBIENT_ON AMBIENT_ONLY_R SKY_COLOR
AMBIENT_ON EQUATOR_COLOR
AMBIENT_ON AMBIENT_ONLY_R EQUATOR_COLOR
AMBIENT_ON GROUND_COLOR
AMBIENT_ON AMBIENT_ONLY_R GROUND_COLOR
Runtime
Lighting - Environment Lighting
菜单:Windows->Rendering->Lighting Settings->Scene Tab
Scene Tab->Environment->Environment Lighting
将场景的环境光的Source设置为:Gradient,分别设置Sky,Equator,GroundColor颜色为:红,绿,蓝,因为我们的shader用到
Inspector Informations
在Global Keyword Summary与Local Keyword Summary都可以很方便看到关键字的的开关情况
- Ambient Toggle 是控制Shader里头使用是否使用AMBIENT_ON 变体代码。
- Ambient Color Type 对应就是环境光的颜色
- Ambient Apply Only R 是否对环境观值应用R通道,On:是,那么环境光R通道将影响整体亮度;Off:否,那么环境光RGB通道将影响整体色调
其中 Ambient Toggle、Amibent Color Type都是全局关键字,Amibent Apply Only R是局部关键字
某个shader有多少个全局、局部关键字,都可以通过在Shader Inspector->…按钮,然后弹出的面板中可以看到
如下图:
Global Keywords:
Local Keywords:
Scene View
最后开启 AmbientApplyOnlyR就花屏了,然后用Ground Color的R通道就可以设置亮度了
Project
MultiCompileTest 提取码: gdck