码迷,mamicode.com
首页 > 编程语言 > 详细

【浅墨Unity3D Shader编程】之十三 单色透明Shader & 标准镜面高光Shader

时间:2016-03-13 18:07:18      阅读:342      评论:0      收藏:0      [点我收藏+]

标签:


本系列文章由@浅墨_毛星云 出品,转载请注明出处。  
文章链接: 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


【浅墨Unity3D Shader编程】之十三 单色透明Shader & 标准镜面高光Shader

标签:

原文地址:http://blog.csdn.net/poem_qianmo/article/details/50878538

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!