本系列文章由@浅墨_毛星云 出品,转载请注明出处。
文章链接: http://blog.csdn.net/poem_qianmo/article/details/50878538
作者:毛星云(浅墨) 微博:http://weibo.com/u/1723155442
本文工程使用的Unity3D版本: 5.2.1
本次更新放出的Shader为透明系列的3个Shader和标准的镜面高光Shader的两个Shader。由易到难,由入门级到应用级,难度梯度合理。
依然是先放出游戏场景的exe和运行截图。
本期用的模型为妙蛙草。
【可运行的本文配套exe游戏场景请点击这里下载】
OK,直奔主题吧。
一、单色透明Shader
在上篇文章中单色透明的基础上进行改造,加入alpha混合,构成了这篇文章的第一个Shader——单色透明Shader。具体代码如下:
//透明单色Shader Shader "浅墨Shader编程/Volume13/1.SimpleAlphaShader" { //------------------------------------【唯一的子着色器】------------------------------------ SubShader { //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 Tags{ "Queue" = "Transparent" } Pass { //不写入深度缓冲,为了不遮挡住其他物体 ZWrite Off //选取Alpha混合方式 Blend SrcAlpha SrcAlpha //Blend SrcAlpha OneMinusSrcAlpha //===========开启CG着色器语言编写模块============ CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //--------------------------------【顶点着色函数】----------------------------- // 输入:POSITION语义(坐标位置) // 输出:SV_POSITION语义(像素位置) //--------------------------------------------------------------------------------- float4 vert(float4 vertexPos : POSITION) : SV_POSITION { //坐标系变换 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 return mul(UNITY_MATRIX_MVP, vertexPos); } //--------------------------------【片段着色函数】----------------------------- // 输入:无 // 输出:COLOR语义(颜色值) //--------------------------------------------------------------------------------- float4 frag(void) : COLOR { //返回单色 return float4(0.3, 1.0, 0.1, 0.6); } //===========结束CG着色器语言编写模块=========== ENDCG } } }将其施用于材质之上的效果如下:
实景效果如下:
二、颜色可以调版单色透明Shader
老规矩,让颜色可调,来一个Properties属性块,替换掉Hard encoding硬编码的颜色。所以,代码如下:
//颜色可以调版单色透明Shader Shader "浅墨Shader编程/Volume13/2.ColorChangeAlpha" { //------------------------------------【属性值】------------------------------------ Properties { //颜色值 _ColorWithAlpha("ColorWithAlpha", Color) = (0.9, 0.1, 0.1, 0.5) } //------------------------------------【唯一的子着色器】------------------------------------ SubShader { //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 Tags{ "Queue" = "Transparent" } //--------------------------------唯一的通道------------------------------- Pass { //不写入深度缓冲,为了不遮挡住其他物体 ZWrite Off //选取Alpha混合方式 Blend SrcAlpha SrcAlpha //Blend SrcAlpha OneMinusSrcAlpha //===========开启CG着色器语言编写模块============ CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //变量声明 uniform float4 _ColorWithAlpha; //--------------------------------【顶点着色函数】----------------------------- // 输入:POSITION语义(坐标位置) // 输出:SV_POSITION语义(像素位置) //--------------------------------------------------------------------------------- float4 vert(float4 vertexPos : POSITION) : SV_POSITION { //坐标系变换 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 return mul(UNITY_MATRIX_MVP, vertexPos); } //--------------------------------【片段着色函数】----------------------------- // 输入:无 // 输出:COLOR语义(颜色值) //--------------------------------------------------------------------------------- float4 frag(void) : COLOR { //返回自定义的RGBA颜色 return _ColorWithAlpha; } //===========结束CG着色器语言编写模块=========== ENDCG } } }
将其施用于材质之上的效果如下:
这边的调色板中,,除了RGB三色,还有Alpha值可以进行调节。
实景效果如下:
三、双面双色颜色可以调版透明Shader
我们可以利用Cull语句,分别在两个Pass中Cull Front和Cull Back,以让材质的正面和反面显示出不同的颜色。代码实现如下:
//双面双色颜色可以调版透明Shader Shader "浅墨Shader编程/Volume13/3.TwoSideColorChangeAlpha" { //------------------------------------【属性值】------------------------------------ Properties { //正面颜色值 _ColorWithAlpha_Front("ColorWithAlpha_Front", Color) = (0.9, 0.1, 0.1, 0.5) //背面颜色值 _ColorWithAlpha_Back("ColorWithAlpha_Back", Color) = (0.1, 0.3, 0.9, 0.5) } //------------------------------------【唯一的子着色器】------------------------------------ SubShader { //设置Queue为透明,在所有非透明几何体绘制之后再进行绘制 Tags{ "Queue" = "Transparent" } //------------------------【通道1:渲染正面】------------------------- Pass { //剔除背面,渲染正面 Cull Back //不写入深度缓冲,为了不遮挡住其他物体 ZWrite Off //选取Alpha混合方式 Blend SrcAlpha OneMinusSrcAlpha //Blend SrcAlpha SrcAlpha //===========开启CG着色器语言编写模块============ CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //变量声明 uniform float4 _ColorWithAlpha_Front; //--------------------------------【顶点着色函数】----------------------------- // 输入:POSITION语义(坐标位置) // 输出:SV_POSITION语义(像素位置) //--------------------------------------------------------------------------------- float4 vert(float4 vertexPos : POSITION) : SV_POSITION { //坐标系变换 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 return mul(UNITY_MATRIX_MVP, vertexPos); } //--------------------------------【片段着色函数】----------------------------- // 输入:无 // 输出:COLOR语义(颜色值) //--------------------------------------------------------------------------------- float4 frag(void) : COLOR { //返回自定义的RGBA颜色 return _ColorWithAlpha_Front; } //===========结束CG着色器语言编写模块=========== ENDCG } //------------------------【通道2:渲染背面】------------------------- Pass { //剔除正面,渲染背面 Cull Front //不写入深度缓冲,为了不遮挡住其他物体 ZWrite Off //选取Alpha混合方式 Blend SrcAlpha OneMinusSrcAlpha //Blend SrcAlpha SrcAlpha //===========开启CG着色器语言编写模块============ CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //变量声明 uniform float4 _ColorWithAlpha_Back; //--------------------------------【顶点着色函数】----------------------------- // 输入:POSITION语义(坐标位置) // 输出:SV_POSITION语义(像素位置) //--------------------------------------------------------------------------------- float4 vert(float4 vertexPos : POSITION) : SV_POSITION { //坐标系变换 //输出的顶点位置(像素位置)为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 return mul(UNITY_MATRIX_MVP, vertexPos); } //--------------------------------【片段着色函数】----------------------------- // 输入:无 // 输出:COLOR语义(颜色值) //--------------------------------------------------------------------------------- float4 frag(void) : COLOR { //返回自定义的RGBA颜色 return _ColorWithAlpha_Back; } //===========结束CG着色器语言编写模块=========== ENDCG } } }
将其施用于材质之上的效果如下:
可以看到,有两个可供调节的颜色选项,分别表示材质正面和反面的透明颜色,而材质最终表现出来的透明颜色,是这两种颜色的混合。
在实景表现中,从物体外部和内部,可以看到其显示出了不同的颜色。
从物体外部看:
从物体内部看:
四、镜面反射(Specular)Shader
上篇文章中讲到了Diffuse光照(漫反射光照)与实现它的Shader,经常与其相提并论的是Specular光照(镜面反射光照,或称高光)。这里,接着我们来放出镜面反射光照的Shader。其具体的原理翻开任何一本图形学的书都可以找到,这边就不多讲,直接放出详细注释的实现代码:
//镜面反射Shader(specular shader ) Shader "浅墨Shader编程/Volume13/4.Specular" { //------------------------------------【属性值】------------------------------------ Properties { //主颜色 _Color("Main Color", Color) = (1, 1, 1, 1) //镜面反射颜色 _SpecColor("Specular Color", Color) = (1, 1, 1, 1) //镜面反射光泽度 _SpecShininess("Specular Shininess", Range(1.0, 100.0)) = 10.0 } //------------------------------------【唯一的子着色器】------------------------------------ SubShader { //渲染类型设置:不透明 Tags{ "RenderType" = "Opaque" } //--------------------------------唯一的通道------------------------------- Pass { //光照模型ForwardBase Tags{ "LightMode" = "ForwardBase" } //===========开启CG着色器语言编写模块=========== CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //顶点着色器输入结构 struct appdata { float4 vertex : POSITION;//顶点位置 float3 normal : NORMAL;//法线向量坐标 }; //顶点着色器输出结构 struct v2f { float4 pos : SV_POSITION;//像素位置 float3 normal : NORMAL;//法线向量坐标 float4 posWorld : TEXCOORD0;//在世界空间中的坐标位置 }; //变量的声明 float4 _LightColor0; float4 _Color; float4 _SpecColor; float _SpecShininess; //--------------------------------【顶点着色函数】----------------------------- // 输入:顶点输入结构体 // 输出:顶点输出结构体 //--------------------------------------------------------------------------------- //顶点着色函数 v2f vert(appdata IN) { //【1】声明一个输出结构对象 v2f OUT; //【2】填充此输出结构 //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 OUT.pos = mul(UNITY_MATRIX_MVP, IN.vertex); //获得顶点在世界空间中的位置坐标 OUT.posWorld = mul(_Object2World, IN.vertex); //获取顶点在世界空间中的法线向量坐标 OUT.normal = mul(float4(IN.normal, 0.0), _World2Object).xyz; //【3】返回此输出结构对象 return OUT; } //--------------------------------【片段着色函数】----------------------------- // 输入:顶点输出结构体 // 输出:float4型的像素颜色值 //--------------------------------------------------------------------------------- fixed4 frag(v2f IN) : COLOR { //【1】先准备好需要的参数 //获取法线的方向 float3 normalDirection = normalize(IN.normal); //获取入射光线的方向 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); //获取视角方向 float3 viewDirection = normalize(_WorldSpaceCameraPos - IN.posWorld.xyz); //【2】计算出漫反射颜色值 Diffuse=LightColor * MainColor * max(0,dot(N,L)) float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection)); //【3】计算镜面反射颜色值 float3 specular; //若是法线方向和入射光方向大于180度,镜面反射值为0 if (dot(normalDirection, lightDirection) < 0.0) { specular = float3(0.0, 0.0, 0.0); } //否则,根据公式进行计算 Specular =LightColor * SpecColor *pow(max(0,dot(R,V)),Shiness),R=reflect(-L,N) else { float3 reflectDirection = reflect(-lightDirection, normalDirection); specular = _LightColor0.rgb * _SpecColor.rgb * pow(max(0.0, dot(reflectDirection, viewDirection)), _SpecShininess); } //【4】合并漫反射、镜面反射、环境光的颜色值 float4 diffuseSpecularAmbient = float4(diffuse, 1.0) + float4(specular, 1.0) + UNITY_LIGHTMODEL_AMBIENT; //【5】将漫反射-镜面反射-环境光的颜色值返回 return diffuseSpecularAmbient; } //===========结束CG着色器语言编写模块=========== ENDCG } } }将其施用于材质之上的效果如下:
其中的Specular Shininess滑条用于调节高光的衰减级数。
OK,我们将颜色调节一下,看看新的效果:
五、带纹理载入的specular shader
当然要支持纹理载入,不然实用性太低。Properties中加入纹理属性,其他的地方进行相应的变化,便可以得到支持纹理载入的specular shader:
//支持纹理载入的specular shader Shader "浅墨Shader编程/Volume13/5.Specular with Shader" { //------------------------------------【属性值】------------------------------------ Properties { //主纹理 _MainTex("Texture", 2D) = "white" {} //主颜色 _Color("Main Color", Color) = (1, 1, 1, 1) //镜面反射颜色 _SpecColor("Specular Color", Color) = (1, 1, 1, 1) //镜面反射光泽度 _SpecShininess("Specular Shininess", Range(1.0, 100.0)) = 10.0 } //------------------------------------【唯一的子着色器】------------------------------------ SubShader { //渲染类型设置:不透明 Tags{ "RenderType" = "Opaque" } //--------------------------------唯一的通道------------------------------- Pass { //光照模型ForwardBase Tags{ "LightMode" = "ForwardBase" } //===========开启CG着色器语言编写模块=========== CGPROGRAM //编译指令:告知编译器顶点和片段着色函数的名称 #pragma vertex vert #pragma fragment frag //顶点着色器输入结构 struct appdata { float4 vertex : POSITION;//顶点位置 float3 normal : NORMAL;//法线向量坐标 float2 texcoord : TEXCOORD0;//一级纹理坐标 }; //顶点着色器输出结构 struct v2f { float4 pos : SV_POSITION;//像素位置 float3 normal : NORMAL;//法线向量坐标 float2 texcoord : TEXCOORD0;//一级纹理坐标 float4 posWorld : TEXCOORD1;//在世界空间中的坐标位置 }; //变量的声明 float4 _LightColor0; float4 _Color; sampler2D _MainTex; float4 _SpecColor; float _SpecShininess; //--------------------------------【顶点着色函数】----------------------------- // 输入:顶点输入结构体 // 输出:顶点输出结构体 //--------------------------------------------------------------------------------- //顶点着色函数 v2f vert(appdata IN) { //【1】声明一个输出结构对象 v2f OUT; //【2】填充此输出结构 //输出的顶点位置为模型视图投影矩阵乘以顶点位置,也就是将三维空间中的坐标投影到了二维窗口 OUT.pos = mul(UNITY_MATRIX_MVP, IN.vertex); //获得顶点在世界空间中的位置坐标 OUT.posWorld = mul(_Object2World, IN.vertex); //获取顶点在世界空间中的法线向量坐标 OUT.normal = mul(float4(IN.normal, 0.0), _World2Object).xyz; //输出的纹理坐标也就是输入的纹理坐标 OUT.texcoord = IN.texcoord; //【3】返回此输出结构对象 return OUT; } //--------------------------------【片段着色函数】----------------------------- // 输入:顶点输出结构体 // 输出:float4型的像素颜色值 //--------------------------------------------------------------------------------- fixed4 frag(v2f IN) : COLOR { //【1】先准备好需要的参数 //获取纹理颜色 float4 texColor = tex2D(_MainTex, IN.texcoord); //获取法线的方向 float3 normalDirection = normalize(IN.normal); //获取入射光线的方向 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz); //获取视角方向 float3 viewDirection = normalize(_WorldSpaceCameraPos - IN.posWorld.xyz); //【2】计算出漫反射颜色值 Diffuse=LightColor * MainColor * max(0,dot(N,L)) float3 diffuse = _LightColor0.rgb * _Color.rgb * max(0.0, dot(normalDirection, lightDirection)); //【3】计算镜面反射颜色值 float3 specular; //若是法线方向和入射光方向大于180度,镜面反射值为0 if (dot(normalDirection, lightDirection) < 0.0) { specular = float3(0.0, 0.0, 0.0); } //否则,根据公式进行计算 Specular =LightColor * SpecColor *pow(max(0,dot(R,V)),Shiness),R=reflect(-L,N) else { float3 reflectDirection = reflect(-lightDirection, normalDirection); specular = _LightColor0.rgb * _SpecColor.rgb * pow(max(0.0, dot(reflectDirection, viewDirection)), _SpecShininess); } //【4】合并漫反射、镜面反射、环境光的颜色值 float4 diffuseSpecularAmbient = float4(diffuse, 1.0) + float4(specular, 1.0) + UNITY_LIGHTMODEL_AMBIENT; //【5】将漫反射-镜面反射-环境光的颜色值乘以纹理颜色值之后返回 return diffuseSpecularAmbient * texColor; } //===========结束CG着色器语言编写模块=========== ENDCG } } }
将其施用于材质之上的效果如下:
将此Shader施于妙蛙草的模型之上,得到的便是如端游《剑灵》一般油腻腻的画风感觉:
本文Shader的全家福:
最后,依然是放出加入特效后的场景截图。
OK,本篇的内容大致如此,下次更新见。
附1: 本博文相关资源下载链接清单
【百度云】博文游戏场景exe下载
【百度云】博文示例场景资源和源码工程下载 ( PS:工程所用Unity版本为5.2.1)
【Github】本文全部Shader源码
附2:Reference
[1] http://docs.unity3d.com/Manual/SL-Reference.html
[2] https://en.wikibooks.org/wiki/Cg_Programming