读一读

//Unity Shader
Properties {
	_MainTex ("Base (RGB)", 2D) = "white" {}
	_SelfColor("自发光", Color) = (0.7,0.7,0.7,1)
	_guangzedu("光泽度", Range(0.01,100) ) = 70
	_Detail ("细节纹理", 2D) = "gray" {}
}

//C#
Render.material.SetColor("_SelfColor", new Color(0.549f, 0.549f, 0.549f));


获取到相应使用这个Shader的材质,然后调用Set...方法设置相应的属性,名字对应上就OK了。


gpu.jpg


顶点着色器是GPU的第一个阶段,主要完成坐标变换和逐顶点关照。顶点坐标由模型空间变换到齐次剪裁空间。

裁剪是在剪裁空间下去掉那些摄像机视野看不到图元。一个图元和摄像机视野的关系有三种:完全在视野内、部分在视野内、完全在视野外。(不可编程的)

屏幕映射的任务是把每个图元的x和y坐标转换到屏幕坐标系,z坐标这一步不处理。

三角形设置就是计算得到三角形边界的表达方式,三角形遍历检查在三角形网格覆盖的像素生成一个片元

片元着色器就是可以控制那些片元的颜色的输出。为了在片元着色器中进行纹理采样,我们通常会在顶点着色器阶段输出每个顶点对应的纹理坐标,然后经过光栅化阶段对三角网格的3个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标了。

逐片元操作其实就是合并,就是检查片元是否可见(测试),然后是覆盖到还是混合到颜色缓冲区去。


渲染流程分为:应用阶段、几何阶段、光栅化阶段。

应用阶段是CPU上运行的,准备场景的数据(光源、相机、模型等),粗粒剔除不可见的物体,设置渲染状态(材质、Shader等)。这一阶段最重要的输出就是渲染所需要的几何信息-即渲染图元,传递给下一个阶段(几何阶段)。

几何阶段是GPU上运行的,主要是逐顶点的把它们变换到屏幕空间去,再交给光栅器处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕的二维顶点坐标、每个顶点对应的深度值、着色等信息,传递给下一个阶段。

光栅化阶段是GPU上运行的,主要会根据上一阶段传过来的屏幕顶点信息进行插值处理(纹理和颜色),然后再进行逐像素处理。


法线贴图需要记录的是一个点的方向,而对应的图片的记录颜色RGBA,完全可以够用来记录法线方向。已知到RGB的取值范围是(0-255),而记录法线方向的范围是(-1,1)。所以需要做一个映射,假设RGB取值除以255那么范围为(0,1),此时做一个(0,1)到(-1,1)的映射即可。很简单的乘2-1法,和半兰伯特技巧般的。这也是为什么法线贴图老是蓝蓝的,因为一般来说正常的一致的法线是(0,0,1)(不凹凸),对应的颜色为(0.5,0.5,1),大概就是(127,127,255)那样吧。

法线贴图存储的信息是切线空间的,也就是以模型顶点为中心,x轴为切线,y轴为副切线,z轴为法线,也就是说为啥(0,0,1)对应的是正常的法线了。


编写Shader的时候,用到方向的都写成3维的向量,不要用4维的,一是不需要(不用平移,不需要受w分量的影响),二是会出错(减少出错的几率)


Shader "chicai/7"{
	Properties{
		_Specular("Specular",Color)=(1,1,1,1)
		_Glass("Glass",Range(8,20))=8
		_MainTex("MainTex",2D)="white"{}
		_NormalTex("NormalTex",2D)="white"{}
		_NormalIndexd("AoTuQiangDu",float)=1
	}

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

			CGPROGRAM
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Glass;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _NormalTex;
			float4 _NormalTex_ST;
			float _NormalIndexd;

			#include "Lighting.cginc"
			#pragma vertex vert
			#pragma fragment frag
			struct a2v{
				float4 vertex:POSITION;
				float3 normal:NORMAL;
				float4 tangent:TANGENT;
				float4 texcoord:TEXCOORD0;
			};

			struct v2f{
				float4 position:SV_POSITION;
				fixed3 nor:COLOR0;
				fixed3 lightDir:COLOR1;
				fixed3 viewDir:COLOR2;
				float4 uv:TEXCOORD0;
			};

			v2f vert(a2v v){
				v2f f;
				f.position = UnityObjectToClipPos(v.vertex);
				f.nor = normalize(mul(unity_ObjectToWorld,v.normal));

				TANGENT_SPACE_ROTATION;
				f.lightDir = normalize(mul(rotation,ObjSpaceLightDir(v.vertex))).xyz;
				f.viewDir = normalize(mul(rotation,ObjSpaceViewDir(v.vertex))).xyz;
				f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				f.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw;
				return f;
			}

			fixed4 frag(v2f f):SV_Target{

				fixed3 tangentNormal = UnpackNormal(tex2D(_NormalTex,f.uv.wz));
				tangentNormal.xy = tangentNormal.xy * _NormalIndexd;
				tangentNormal = normalize(tangentNormal);

				fixed3 texCol = tex2D(_MainTex,f.uv);//计算改uv上贴图的颜色,当物体本身颜色——Diffuse
				fixed3 diffuseCol = _LightColor0.rgb * texCol  * (dot(tangentNormal,f.lightDir)/2+0.5);

				fixed3 aveDir = normalize(f.lightDir+f.viewDir);
				fixed3 specularCol = _LightColor0.rgb * _Specular * pow(max(dot(aveDir,tangentNormal),0),_Glass);

				fixed3 allColor = diffuseCol + specularCol;

				return fixed4(allColor,1);
			}

			ENDCG
		}
	}
}



Unity属性中添加法线贴图属性和凹凸强度系数

_NormalTex("NormalTex",2D)="white"{}
_NormalIndexd("AoTuQiangDu",float)=1

CG声明对应的变量和平移缩放变量

sampler2D _NormalTex;
float4 _NormalTex_ST;
float _NormalIndexd;

引进切线到顶点函数float4 tangent:TANGENT;

使用传入片元的uv参数的xy传递贴图uv坐标,zw传递法线贴图的uv坐标

f.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw;

转变视野方向和光线方向到切线空间

TANGENT_SPACE_ROTATION;//一个宏,计算出rotation
f.lightDir = normalize(mul(rotation,ObjSpaceLightDir(v.vertex))).xyz;
f.viewDir = normalize(mul(rotation,ObjSpaceViewDir(v.vertex))).xyz;

片元中得出相应的法线

fixed3 tangentNormal = UnpackNormal(tex2D(_NormalTex,f.uv.wz));//像素图片(0-255)转化为方向的(-1,1)
tangentNormal.xy = tangentNormal.xy * _NormalIndexd;
tangentNormal = normalize(tangentNormal);

Shader "chicai/6"{
	Properties{
		_Specular("Specular",Color)=(1,1,1,1)
		_Glass("Glass",Range(8,20))=8
		_MainTex("MainTex",2D)="white"{}
	}

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

			CGPROGRAM
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Glass;
			sampler2D _MainTex;
			float4 _MainTex_ST;

			#include "Lighting.cginc"
			#pragma vertex vert
			#pragma fragment frag
			struct a2v{
				float4 vertex:POSITION;
				float3 normal:NORMAL;
				float4 texcoord:TEXCOORD0;
			};

			struct v2f{
				float4 position:SV_POSITION;
				fixed3 nor:COLOR0;
				fixed3 lightDir:COLOR1;
				fixed3 viewDir:COLOR2;
				float2 uv:TEXCOORD0;
			};

			v2f vert(a2v o){
				v2f f;
				f.position = UnityObjectToClipPos(o.vertex);
				f.nor = normalize(mul(unity_ObjectToWorld,o.normal));
				f.lightDir = normalize(WorldSpaceLightDir(o.vertex));
				f.viewDir = normalize(WorldSpaceViewDir(o.vertex));
				f.uv = o.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				return f;
			}

			fixed4 frag(v2f f):SV_Target{

				fixed3 texCol = tex2D(_MainTex,f.uv);//计算该uv上贴图的颜色,当物体本身颜色——Diffuse
				fixed3 diffuseCol = _LightColor0.rgb * texCol  * (dot(f.nor,f.lightDir)/2+0.5);

				fixed3 aveDir = normalize(f.lightDir+f.viewDir);//光照方向和视野方向的平分线
				fixed3 specularCol = _LightColor0.rgb * _Specular * pow(max(dot(aveDir,f.nor),0),_Glass);

				fixed3 allColor = diffuseCol + specularCol;

				return fixed4(allColor,1);
			}

			ENDCG
		}
	}
}

首先,Unity变量:

_MainTex("MainTex",2D)="white"{}


对应CG变量:

sampler2D _MainTex;
float4 _MainTex_ST;//固定,变量名_ST,xy分量表示贴图的缩放,zw分量表示贴图的偏移


顶点函数传入uv纹理坐标  float4 texcoord:TEXCOORD0;


计算出含偏移缩放等的纹理坐标给片元

float2 uv:TEXCOORD0;
f.uv = o.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;//乘缩放,+偏移
f.uv = TRANSFORM_TEX(v.texcoord, _MainTex_ST);//内置的方法转换uv,等同于上面一行


找出对应uv坐标对应的纹理颜色充当漫反射自身颜色


// 摄像机方向(视角方向)
float3 WorldSpaceViewDir(float4 v)      // 根据模型空间中的顶点坐标 得到 (世界空间)从这个点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v) // 世界空间中的顶点坐标==》世界空间从这个点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v)        // 模型空间中的顶点坐标==》模型空间从这个点到摄像机的观察方向

// 光源方向
float3 WorldSpaceLightDir(float4 v)      // 模型空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 UnityWorldSpaceLightDir(float4 v) // 世界空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 ObjSpaceLightDir(float4 v)        // 模型空间中的顶点坐标==》模型空间中从这个点到光源的方向

// 方向转换
float3 UnityObjectToWorldNormal(float3 norm) // 把法线方向 模型空间==》世界空间
float3 UnityObjectToWorldDir(float3 dir)     // 把方向 模型空间=》世界空间
float3 UnityWorldToObjectDir(float3 dir)     // 把方向 世界空间=》模型空间


666.png