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