创建一个.cginc后缀的cg文件,保存我们要复用的代码
文件创建好以后,在shader文件里用#include"xxx.cginc"的方法,去插入它。
有一点需要注意的,就是最好在我们自己建的。cginc文件的顶部创建一个宏出来,例:
#ifndef MY_INCLUDE
#define MY_INCLUDE
// 你的cg代码内容
#endif
稍微对c++有点了解的人应该就会知道了,这在c++里是为了防止#include命令在我们的代码中反复插入相同的一段代码,造成不必要的浪费。
#ifndef会判断当前宏是否存在,如果没存在的话用#define创建该宏,#endif是设置了会起作用的代码范围。
外加使用#include时要写相对路径,例:#include"./xxx/xxx.cginc"./就是上一层目录的意思
用#define创建宏,来在shader中进行判断
这其实是上面方法的延伸,我们可以把方法创建成这个样子,例:
fixed4 MyCompute(fixed4 left, fixed4 right)
{
fixed4 result = fixed4(0);
#ifdef ADD_COMPUTE
result = left + right;
#elif
result = left * right;
#endif
return result;
}
这段代码中我们有一步判断,如果存在宏ADD_COMPUTE,则进行加法,否则进行乘法。
我们在将这个文件#include之后,在调用这个方法时,如果想要进行加法的话,就可以在shader中直接创建这个宏就可以了,例:#defineADD_COMPUTE
好处:
- 是为一些不能传入参数的方法提供了做判断的手段,例如surfaceshader的内置光照函数,它们的结构是定死的,不允许增加参数。
- 是#ifdef这种,属于编译命令,是在编译阶段进行的判断,也就是说分支部分不会被编译到最终程序中,所以在运行的时候,就不存在任何的判断性能消耗了
还有一种特殊的写法,用CGINCLUDE……ENDCG块包围的代码
代码结构大概是这个样子的:
大家应该看出来了吧,CGINCLUDE……ENDCG块可以把cg代码写在SubShader{}的外面!
Shader "Z_TestShader/XXX"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
CGINCLUDE
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
struct appdata_t
{
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord;
return o;
}
fixed4 frag (v2f i) : COLOR
{
fixed4 col = tex2D(_MainTex , i.texcoord);
fixed4 lum = Luminance(col);
return lum;
}
ENDCG
SubShader
{
ZTest Always ZWrite Off Cull Off Blend Off
Fog { Mode off }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
}
CGINCLUDE里定义的方法,都是可以在下面创建的pass中被引用的,所以在下面的pass里直接#pragmavertexvert,#pragmafragmentfrag指定了方法,直接让这个pass只剩下3句话了。
以上就是一些在shader里进行模块化开发的小技巧和写法,大家根据自己的情况来进行使用。
记住方法只是一种工具,想要发挥它的作用,主要还是看人是怎么用的。