学习Shader已经有几个月的时间了,Shader作为一门GPU编程语言来说确实比较的难学。主要原因经过我的思考还是本人自己的计算机图形学和美术基础不扎实。好无奈,作为一个之前单纯手写代码的程序员真的是感觉好蛋疼。
学习期间,我也向多名Shader大神请教学习经验,得到的回应大多是让我好好去学习计算机图形学。哈哈,于是乎,本人又跑去看了一个多月的计算机图形学。接着再次回来学习Shader,但是虽然比之前看的时候要好,但是在大部分的情况之下还是两眼一抹黑。
于是乎,我又想到了一位伟人的名言:实践是得到真理的唯一途径。哈哈,大家不要打我,我不是故意说废话的(其实是有意的。。。。。来打我啊)。那么怎么样才能在基础不是很扎实的情况下学习Shader呢?我想了好久,看完CSDN Shader博客大神吃西瓜的小女孩(不要打我,实在是该大神的名字太长了。。。。。)"妈妈说女孩子要自立自强"的博客之后,我似乎找到了一点灵感。我决定从Unity的内置Shader开始入手,详细的介绍一些我看到的效果不错而且比较实用的Shader。我也希望各位大神能对我写的东西提出批评和指导,让我们这些新手一起得到提高和进步,哈哈。
暂时因为工作的原因,我准备以每周一篇的速度来进行更新,具体的看日后的安排吧,我会加油的。。。。。
这次就来先来看我最近做一个项目练手的时候用到的一个shader:
效果见下图:
这是我之前做的一个激光特效Shader,用来练手。
shader的主要代码如下:
Shader "Particles/Additive" { Properties { _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5) _MainTex ("Particle Texture", 2D) = "white" {} _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 } Category { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend SrcAlpha One ColorMask RGB Cull Off Lighting Off ZWrite Off SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_particles #pragma multi_compile_fog #include "UnityCG.cginc" sampler2D _MainTex; fixed4 _TintColor; struct appdata_t { float4 vertex : POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_FOG_COORDS(1) #ifdef SOFTPARTICLES_ON float4 projPos : TEXCOORD2; #endif }; float4 _MainTex_ST; v2f vert (appdata_t v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); #ifdef SOFTPARTICLES_ON o.projPos = ComputeScreenPos (o.vertex); COMPUTE_EYEDEPTH(o.projPos.z); #endif o.color = v.color; o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } sampler2D_float _CameraDepthTexture; float _InvFade; fixed4 frag (v2f i) : SV_Target { #ifdef SOFTPARTICLES_ON float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))); float partZ = i.projPos.z; float fade = saturate (_InvFade * (sceneZ-partZ)); i.color.a *= fade; #endif fixed4 col = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord); UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode return col; } ENDCG } } } }
这个Shader各位大神一眼就能够看出来了,我就不装这个逼说是我写的,不然估计回头得被各种啪啪啪啪啪啪啪无情打脸,哈哈哈哈哈哈。这是Unity自带的一个Shader,主要的功能有颜色混合,开启雾效,外加透明效果。不得不说Unity自带的shader确实厉害,这么多的功能能够这么简洁的就写出来确实不简单。
我就不说闲话不多说了,不然估计要被人给打死,哈哈哈哈哈。因为我也是想要写一篇巩固基础的文章,所以大家就不要嫌弃我啰嗦,那我就多讲一些了。
_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)这句话是在Shader面板中设置来进行混合的颜色,这里面设置了一个RGBA的默认值。
_MainTex ("Particle Texture", 2D) = "white" {} 这是在Shader面板中设置进行颜色混合的图片。
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 这是设定一个范围的值,用来最后让别人调节最终效果的时候用,因为有些效果还是让一些专业的人来进行调节会比较的好。
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } "Queue"="Transparent"这句话的意思是开启透明道,"IgnoreProjector"="True"
的意思是开启阴影的效果,"RenderType"="Transparent"是开启渲染的类别。
Blend SrcAlpha One 只得是用这种类型的alphaa混合模式,其实这就是一个拿你得到的颜色值和alpha进行混合的公式而已,这只是简写而已。
ColorMask RGB 颜色通道遮罩,意思是最后输出的颜色通道是RGB通道
Cull Off Lighting Off ZWrite Off 关闭剔除的效果 关闭灯光的效果 关闭深度缓存(这种操作其实会导致一些问题)
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
#ifdef SOFTPARTICLES_ON
float4 projPos : TEXCOORD2;
#endif
};
上面这些其实都是定义的一些属性值,我就不一一列举了,哈哈。
float4 _MainTex_ST; 别看这就只有这一小句,其实里面的名堂真的不少。
indeed for any texture property, Unity provides value for float4 with "_ST" suffix. The x,y contains texture scale, and z,w contains translation (offset).
_ST变量有4个值,xy是texture scale,zw是offset。
在Material中可以设置的Tiling就是xy,Offset就是zw。
通过这个可以看出,虽然Unity自带的Shader代码十分的简洁,比起那些动不动就上千行的代码看起来简单多了,但是里面其实偷偷帮我们封装了很多的东西,这也让我们学习unity 自带shader的难度加大了不少。所以,这也是我想写这篇博客的原因所在。
v2f vert (appdata_t v) 这句话的意思是定点着色器,返回的值是前面我们定义的v2f属性,说道这里我忍不住要吐槽一下。前段时间使用unity自带的surface shader,我脑残定义了一个属性,结果没有在定点着色器,也就是vert里面给他赋初值,因此shader一直在报错,我的天。还好最后,我们英明神武的大神过来帮我解决了这个问题。。。。。
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 这是一个MVP变换矩阵,意思是将定点坐标从世界坐标系,变换成为屏幕坐标系,这里面的包含了至少三个变换矩阵,代码写起来会非常的长,感谢万能的Unity帮我们封装好了。(虽然我并不确定是Unity封装的还是CG这门语言封装的,但就让我在这里稍微装个逼吧。。。。。。想起我一个朋友说的名言:人不装逼跟咸鱼有什么区别 哈哈哈哈哈哈哈哈)。
#ifdef SOFTPARTICLES_ON
o.projPos = ComputeScreenPos (o.vertex);
COMPUTE_EYEDEPTH(o.projPos.z);
#endif
o.color = v.color;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
这句代码的意思是根据z的深度场景来控制特效的缩放和旋转,这是Unity 5.0里面包含的新的shader,里面封装了很多的东西,我们看到的只是自己传入方法中的参数而已。详细可以看我们超级厉害的雨松MOMO的博客,这里面有讲解http://www.xuanyusong.com/?p=3486(无情甩锅 哈哈哈哈哈哈哈)
float _InvFade; 直接看代码_InvFade,这个就是一个参数控制透明度(配合距离屏幕远近粒子的深度和场景深度的差来呈现效果) 用一个美术大神专业一点的讲解来说就是类似unreal 的alpha bias材质节点,用于粒子片面和实体交接处柔化,避免出现面片穿插的交接线。
#ifdef SOFTPARTICLES_ON
float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float partZ = i.projPos.z;
float fade = saturate (_InvFade * (sceneZ-partZ));
i.color.a *= fade;
#endif
这句话就是展现特效呈现跟场景深度和距离屏幕远近差效果影响的一个方法。从这里大家也都看出来了吧,这个Shader其实就是一个专门用来做特效的Shader,类似于激光或者是2D游戏里面的刀光特效都是可以使用这个方法来制作,告诉大家一个小秘密了,很多Unity上面的特效插件都是使用这个Shader制作的,哈哈哈哈哈哈哈哈哈。
fixed4 col = 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord); 这句话的意思就是将之前那一系列处理得到的i.color来进行简单的颜色混合的处理,至于那个2.0f,其实也就是写这个shader的大神自己在调节颜色的时候定义的。计算机图形学其实很大的程度上来说就是一门欺骗人眼睛的学科,哈哈哈哈哈哈哈。
UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); 这句话的意思是设置雾的朝向和颜色,其实这也是Unity自带的一个封装好的雾的方法。
写到这里本篇博客也就接近尾声了,欢迎各位大神对我提出批评,让我能有提升的空间。如果大家有兴趣的话也可以来这个群里面找我: 344682050 ,我们可以一起在这里互相交流,共同提高shader编程能力。哈哈哈哈哈哈哈哈