Unity shader学习笔记(五)纹理
发表于2018-04-28
纹理映射


纹理坐标(也称为u/v坐标,横坐标是u,纵坐标是v,长宽是1)
一般将纹理的颜色代替漫反射的颜色,这样就会看到图像,一般是片元函数处理
纹理的类型有
Normalmap 法线贴图 EditorGUI andLegacy 菜单栏图标 Sprite 2D图片和UI界面 Cubemap 立方体纹理,做环境盒子 cookie 制作阴影,使手电筒的光从圆形变成另一种形状,类似蝙蝠灯的中间阴影, lightmap 光照贴图,使用避免实时计算 Advanced 自定义
如何使用纹理呢,代码如下
Shader "Custom/10-FirstShader" {
Properties{
//_Diffuse("Diffuse Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_Color("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color) = (1,1,1,1)
_Gloss("Gloss",Range(10,200)) = 20
}
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
//fixed4 _Diffuse;
fixed4 _Specular;
half _Gloss;
sampler2D _MainTex;
fixed4 _Color;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;//获取纹理坐标点
};
struct v2f {
float4 svPos:SV_POSITION;
float3 worldNormal:TEXCOORD0;//世界空间下法线向量
float4 worldVertex:TEXCOORD1;//用于传递世界空间下顶点坐标
float4 uv:TEXCOORD2;//用于传递uv坐标
};
v2f vert(a2v v) {
v2f f;
f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormal = UnityObjectToWorldNormal(v.normal);
f.worldVertex = mul(v.vertex, unity_WorldToObject);
f.uv = v.texcoord;//将纹理坐标传递到片元函数
return f;
}
fixed4 frag(v2f f) :SV_Target{
//法线向量
fixed3 normalDir = normalize(f.worldNormal);
//灯光向量
fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));
//内置函数,可以获取到纹理上某个纹理坐标点的颜色值
//这里只用到了u,v两个值
fixed3 texColor = tex2D(_MainTex,f.uv.xy)*_Color.rgb;
//使用获取到的颜色值替换掉原先漫反射光产生的颜色值
fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(normalDir, lightDir), 0);
//相机视野向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVertex));
//获取半分线
fixed3 halfDir = normalize(lightDir + viewDir);
//获取高光
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(dot(normalDir, halfDir), 0), _Gloss);
//最后环境光和纹理做了一个融合 这样修改环境光纹理也不会受到太大影响,不会使得纹理模糊
fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;
return fixed4(tempColor, 1);
}
ENDCG
}
}
Fallback "Specular"
}

上述代码中,定义了一个纹理参数_MainTex,还有一个_Color用来与纹理叠加,能够控制纹理的颜色。
再看Inspector面板,可以看到纹理属性不仅可以选择纹理图案,还有Tiling属性和Offset属性。
Tilling 是控制纹理进行压缩,Tilling值越大,那么模型上显示的纹理数量也就越多,x控制纹理的宽度,y控制纹理的高度。
Offset属性是控制材质的偏移,通过修改Offset下的x的值来使纹理进行左右移动,通过修改Offset下的y的值来使纹理进行上下的移动。
那么如何使用这两个值呢,Tilling和Offset是纹理自带的属性,可以直接使用:
定义变量
fixed4 _MainTex_ST;
变量名称是固定的,后面加_ST,其中前两个属性代表S 理解为Scale,后两个属性代表T 理解为Transform
使用缩放变量的话,将
f.uv = v.texcoord;
替换为
f.uv = v.texcoord*_MainTex_ST.xy
使用平移变量的话,将
f.uv = v.texcoord;
f.uv = v.texcoord+_MainTex_ST.zw;映射
可以使得物体有凹凸效果。
法线贴图
使用法线贴图的颜色值修改某一点的法线,以此来实现更真实的效果。ps:法线贴图应该和贴图大小相同,一一配套对应
需要注意的是:法线每个轴取值范围是-1到1,而颜色的值范围是0到1,因此会进行变换运算
像素值 = (法线某轴的值+1)/2
这样像素值就能存下法线的值,需要使用时只需要还原一下就行
法线某轴的值 = 像素值*2-1
法线贴图一般都是采用模型顶点的切线空间的坐标来存储法线,这种纹理被称为切线空间的法线纹理,而有些贴图是将模型空间中的表面进行直接修改,然后将法线存储在一张纹理中,这种纹理被称之为模型空间的法线纹理,不过该纹理只能应用于一种模型,不如切线空间的法线纹理应用范围广。
那么如何在shader中使用法线贴图呢
代码如下:
Shader "Custom/11-FirstShader"{
Properties{
//_Diffuse("Diffuse Color",Color) = (1,1,1,1)
_Color("Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
//定义一个法线贴图变量
//bump是一个内置模型,意思是当没有指定法线贴图时,使用bump的法线贴图
//一般指定的纹理是切线空间下的法线纹理,也可以使用模型空间下的法线纹理,只要注意坐标所在空间相同即可
_NormalMap("Normal Map",2D) = "bump"{}
_BumpScale("Bump Scale",Float)=1
}
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
//fixed4 _Diffuse;
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
//引用法线贴图变量
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _BumpScale;
struct a2v {
float4 vertex:POSITION;
//切线空间的确定是通过存储到模型里面的法线和切线确定的
float3 normal:NORMAL;
float4 tangent:TANGENT;//tangent.w是用来确定切线空间中坐标轴的方向的
float4 texcoord:TEXCOORD0;
};
struct v2f {
float4 svPos:SV_POSITION;
//float3 worldNormal:TEXCOORD0;
//float4 worldVertex:TEXCOORD1;
float3 lightDir : TEXCOORD0;
float4 uv:TEXCOORD1;//xy用来存储纹理的uv坐标,zw用来存储法线贴图的纹理坐标
};
//切线空间根据顶点一直在变化,因此只能在顶点函数中获取
//因此该顶点光照的方向向量也在顶点函数中获取
v2f vert(a2v v) {
v2f f;
f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
// f.worldNormal = UnityObjectToWorldNormal(v.normal);
// f.worldVertex = mul(v.vertex, _World2Object);
f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;
TANGENT_SPACE_ROTATION;//调用这个后之后,会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下
//ObjSpaceLightDir(v.vertex)//得到模型空间下的平行光方向
f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
return f;
}
//把所有跟法线方向相关的运算都放在切线空间下
//从法线贴图里取到的法线方向都是切线空间下的
fixed4 frag(v2f f) :SV_Target{
//fixed3 normalDir = normalize(f.worldNormal);
//获取纹理坐标该点的颜色值
fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);
//将颜色值转换为法线的值
// fixed3 tangentNormal = normalize( normalColor.xyz * 2 - 1 ) ; //切线空间下的法线
//UnpackNormal 是系统自带的转换方法
fixed3 tangentNormal = UnpackNormal(normalColor);
tangentNormal.xy = tangentNormal.xy*_BumpScale;
tangentNormal = normalize(tangentNormal);
fixed3 lightDir = normalize(f.lightDir);
fixed3 texColor = tex2D(_MainTex, f.uv.xy)*_Color.rgb;
fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(tangentNormal, lightDir), 0);
fixed3 tempColor = diffuse + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;
return fixed4(tempColor, 1);
}
ENDCG
}
}
Fallback "Specular"
}
有无法线贴图对比

来自:https://blog.csdn.net/u013106366/article/details/53353751
