unity shader (4)--实现漫反射模型

摘自冯乐乐的《unity shader 入门精要》


首先给shader其一个名字

Shader "Custom/DiffuseVertxLevelMat"

为了得到并控制材质漫反射的颜色,在Properties语义块中声明如下
			#include "Lighting.cginc"

Properties
	{
		_Diffuse("Diffuse",Color)=(1,1,1,1)
	}

因为这是顶点/片元着色器的代码,所以要把代码写在Pass语义块中,,而非SubShader语义块,并且在第一行声明光照模式为ForwardBase:

SubShader
	{
		Pass
		{
			Tags{"LightMode"="ForwardBase"}
			

然后使用CGPROGRAM和ENDCG来包围代码块,定义顶点着色器和片元着色器代码,如下:

CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

为了使用unity内置的一些变量,然后包含:

#include "Lighting.cginc"


最后是核心的代码,

fixed4 _Diffuse;

			struct a2v
			{
				float4 vertex:POSITION;
				float4 normal:NORMAL;
			};

			struct v2f
			{
				float4 pos:SV_POSITION;
				fixed3 color:COLOR;
			};

			v2f vert(a2v v)
			{
				v2f o;

				//Transform the vertex from object space to projection space
				//变换对象空间的顶点投影空间
				o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
				
				//get ambient term
				//获得环境光照
				fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;

				//transform the normal from object space world space
				//把局部法线转换到世界法线
				fixed3 worldNormal=normalize(mul(v.normal,(float3x3)_World2Object));

				//get the light direction in world space
				//获得全局光照
				fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);

				//compute diffuse term
				//计算漫反射
				fixed3 diffuse =_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));

				o.color=ambient+diffuse;
				return o;
			}

			fixed4 frag(v2f i):SV_Target
			{
				return fixed4(i.color,1.0);
			}
			ENDCG
		}		
	}


最后补上完整的代码:



Shader "Custom/DiffuseVertexLevel" {
	Properties
	{
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}

	SubShader
	{
		Pass  //   顶点/片元着色器的代码需要写在Pass语义块中
		{
			//光照模型
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM

			//顶点着色器 的名称
			#pragma vertex vert
			//片元着色器 的名称
			#pragma fragment frag

			//使用Unity内置的一些变量,如后面的_LightColor0,所以需要
			//包含 Lighting.cginc
			#include "Lighting.cginc"

			//定义一个Properties语义块中声明的属性一样类型匹配的变量
			//通过这种方式,我们可以得到漫反射公式中需要的参数,材质漫反射属性,
			//由于颜色属性的范围在0到1之间,因此我们使用fixed精度变量来存储它
			fixed4 _Diffuse;

			//顶点着色器的输入结构体
			struct a2v
			{
				float4 vertex : POSITION;
				//用于访问顶点法线,通过NORMAL语义来告诉Unity把模型顶点法线信息存储到normal变量中
				float3 normal : NORMAL;
			};
			//顶点着色器的输出结构体(同事也说片元着色器的输入结构体)
			struct v2f
			{
				float4 pos : SV_POSITION;
				//为了接受顶点着色器计算得到的光照颜色并作用于片元着色器,
				//我们定义了一个color变量,且并不是必须使用COLOR语义,
				//有的是使用TEXCROOD0语义
				fixed3 color : COLOR;
			};

			//顶点着色器最基本的任务就是把顶点位置从模型空间转换到裁剪空间中
			// 所以使用unity内置的 UNITY_MATRIX_MVP  完成 模型*世界*投影矩阵来完成转换
			v2f vert(a2v v)
			{
				v2f o;
				
				//Transform the vertex from object space to projection space
				//变换对象空间的顶点投影空间
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				//get ambient term
				//获得环境光照
				//获取环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;


				//transform the normal from object space world space
				//把局部法线转换到世界法线
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

				//  _WorldSpaceLightPos0 获取光源方向
				//get the light direction in world space
				//获得全局光照
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);


				//compute diffuse term
				//计算漫反射
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));

				o.color = ambient + diffuse;

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(i.color, 1.0);
			}

			ENDCG
		}
	}

	FallBack "Diffuse"
}


在计算法线和光源方向之间的点积时,我们需要选择它们所在的坐标系,我们选择了世界坐标系,而由a2v得到的
顶点法线是位于模型空间下的,因此我们首先需要把法线转换到世界空间中。所以使用_World2Object把模型空间
转换到世界空间,然后通过调换它在mul函数中的位置,得到和转置矩阵相同的矩阵乘法。由于法线是一个三维矢量,
因此我们只需要截取_World2Object的前三行前三列即可



然后就是效果显示的对照图:



效果比想象中的要好很多啊!!!
最后,学习unity对于英语和码代码的速度和准确度有很高的要求,毕竟没有很好的IDE。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值