Unity Shader零基础入门4:纹理贴图与法线贴图

纹理贴图:让模型拥有色彩、花纹等。

法线贴图:为减少资源消耗,模型本身的细节是比较少的。法线贴图可以增加模型的细节,让模型的表面出现更多的细节、阴影等。法线贴图一般是在切线空间下的,以便适合不同模型。如果是在模型空间下的,则只能配合对应的模型使用。切线空间的法线贴图看起来主要是蓝色,而模型空间的看起来五颜六色。

uv坐标: 纹理坐标又称为uv坐标。u、v分别代表横轴、纵轴。贴图左下为原点,范围为0-1。比如贴图右上角为(1,1)。

贴图的平铺和偏移:

纹理贴图和法线贴图的属性:Tiling-平铺;Offset-平移;

Tiling能让贴图变为重复的多份。比如设置为x=1,y=2,则x方向不变,y方向变成重复平铺的两份,各占一半高度。如果要使用Tiling让贴图重复平铺,需要贴图文件的Wrap Mode设置为Repeat。

Offset自然是让贴图平移偏离原位置。

本文所需要的素材包下载:

链接:https://pan.baidu.com/s/1JOSWNaT9APrw25C8geYwhA
提取码:wwic

扫描二维码关注公众号,回复: 14634517 查看本文章

 下载后导入到工程中。之后用包内的预制体在场景中生成一个模型。

 然后我们新建一个材质并给模型替换上新材质。

之后创建一个Shader文件(Standard Surface Shader),并修改内容如下:

Shader "Tutorial/04Texture"{
	Properties{
		//调整纹理贴图的颜色用
		_Color("Color",Color)=(1,1,1,1)
		//纹理贴图
		_MainTex("MainTex",2D)="white"{}
		//法线贴图。这里不能用"white"而要用"bump"表示没有法线贴图时默认使用模型本身的法线
		_NormalMap("NormalMap",2D)="bump"{}
		//控制使用法线贴图的法线的比例。如果为1则完全使用法线贴图。如果为0则完全使用模型本身的法线。
		//如果在0~1则法线贴图和模型法线按比例产生影响。如果大于1则会变得比完全使用法线贴图更加夸张。
		_BumpScale("BumpScale",Range(0,10))=1
	}

	SubShader{
		pass{
			Tags{
				"Lighting"="ForwardBase"
			}
			CGPROGRAM	
			#include "Lighting.cginc"
			#pragma vertex vert 
			#pragma fragment frag 
			fixed4 _Color;
			//纹理贴图
			sampler2D _MainTex;
			//_MainTex_ST的前二维代表纹理贴图的平铺数,后二维代表纹理贴图的偏移数。
			//这个变量必须命名为纹理贴图变量名+"_ST"
			//ST表示scale 和 transition
			float4 _MainTex_ST;
			//法线贴图
			sampler2D _NormalMap;
			//法线贴图的平铺与偏移
			//与纹理贴图的情况类似,命名必须为法线贴图名+"_ST"
			float4 _NormalMap_ST;
			float _BumpScale;

			struct a2v{
				//模型空间下的顶点坐标
				float4 vertex:POSITION;
				//模型的法线
				//之后要用切线空间,切线空间是通过模型的法线和切线确定的。所以要获得法线和切线然后传输到片元函数。
				float3 normal:normal;
				//模型的切线
				//这里的方向不再用float3而用float4,因为需要第4维用来确定切线空间中坐标轴的方向
				float4 tangent:TANGENT;
				//通过语义:TEXCOORD 获得了uv坐标
				float2 texcoord:TEXCOORD;
			};
			struct v2f{
				//剪切空间下的顶点坐标
				float4 svPos:SV_POSITION;
				//存储切线空间下平行光的方向
				//用TEXCOORD、COLOR解释时,并不代表只能存储纹理坐标、颜色。这里存了方向。
				float3 lightDir:TEXCOORD0;
				//世界空间下的顶点坐标
				float4 worldVertex:TEXCOORD1;
				//存放uv坐标用,以便传递到片元函数中。xy存储纹理贴图坐标,zw存储法线贴图坐标。xyzw分别代表第1、2、3、4维。
				float4 uv:TEXCOORD3;
			};
			v2f vert(a2v v){
				v2f f;
				f.svPos=mul(UNITY_MATRIX_MVP,v.vertex);
				//v.texcoord即uv坐标在这里表示现在需要使用纹理/法线贴图的哪个位置
				//对现在需要使用纹理贴图的哪个位置的坐标进行缩放和平移
				//*_MainTex_ST.xy表示乘上平铺数,+_MainTex_ST.zw则表示加上偏移
				f.uv.xy=v.texcoord*_MainTex_ST.xy+_MainTex_ST.zw;
				//对法线贴图也进行类似操作
				f.uv.zw=v.texcoord*_NormalMap_ST.xy+_NormalMap_ST.zw;
				//调用一个宏得到一个矩阵rotation用来把模型空间下的方向转换到切线空间下。
				//用这个宏必须配合v、f、normal、tangent这几个固定的变量名才能正常使用。
				//如果没有用这几个变量名或用来表示其它内容就可能报错。
				TANGENT_SPACE_ROTATION;
				//rotation就是之前得到的矩阵,用来把模型空间下的方向转换到切线空间的下。
				//ObjSpaceLightDir(v.vertex)表示得到该顶点的模型空间下的光源方向
				f.lightDir= mul(rotation,ObjSpaceLightDir(v.vertex));

				return f;
			}
			//因为从法线贴图取得的法线方向在切线空间下,所以把所有跟法线方向有关的运算都放在切线空间下进行。
			fixed4 frag(v2f f):SV_TARGET{
				//读取法线贴图的颜色
				//tex2D(贴图,坐标)就表示读取该贴图的相应坐标的颜色
				fixed4 normalColor=tex2D(_NormalMap,f.uv.zw);
				//使用unity自带的方法通过法线贴图的颜色得到切线空间下的法线
				fixed3 tangentNormal=UnpackNormal(normalColor);
				//切线空间下的法线的z轴实际就是模型空间下模型的法线
				//_BumpScale为使用法线贴图的比例,只需要将从法线贴图得到的切线空间下的法线的xy乘这个系数而不用管z
				//当_BumpScale为0时该向量只剩z轴自然就成了模型空间下模型的法线。
				tangentNormal.xy=tangentNormal.xy*_BumpScale;
				tangentNormal=normalize(tangentNormal);
				//顶点函数中已经将光源方向转换到了切线空间
				fixed3 lightDir=normalize(f.lightDir);
				//获得纹理贴图上对应的颜色
				fixed4 texcolor= tex2D(_MainTex,f.uv.xy)*_Color;
				//将纹理贴图的颜色融入到漫反射颜色中。
				//同时法线也影响着该点的明暗,从而在一些区域形成阴影以增加细节。
				fixed3 diffuse=_LightColor0.rgb*texcolor.rgb*max(0,dot(tangentNormal,lightDir));
				//通过漫反射颜色和环境光叠加得到最终颜色。这里让环境光颜色也融合了纹理贴图颜色。
				fixed3 tempColor=diffuse+UNITY_LIGHTMODEL_AMBIENT.rgb*texcolor;
				
				return fixed4(tempColor,1);
			}
			ENDCG
		}
	}
	Fallback "Diffuss"
}

为模型选择新写的shader后,在工程面板中添加纹理贴图和法线贴图。先在工程面板中设置一下图片的类型,纹理贴图需要是Texture,法线贴图是Normal map。

然后给模型的这个Shader添加纹理贴图和法线贴图:

 ​​之后可以调整BumpScale,当为0时完全未使用法线贴图,模型的细节就比较少。当为1时完全使用法线贴图,模型表面的细节增加了很多。当大于1时就比较夸张了。

猜你喜欢

转载自blog.csdn.net/qq_21315789/article/details/125436934