Unity Shader入门教程(七):基础纹理之单张纹理
发表于2018-06-06
一 、基础纹理应用
纹理的最初目的就是使用一张图片来控制模型的外观。使用纹理映射技术,我们可以吧一张图“黏”在模型表面,逐纹素(texel)(名字是为了和逐像素进行区分)地控制模型的颜色。
结合之前的光照模型的单张纹理应用:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject' Shader "Custom/Edu/SingleTexture" { Properties { _MainTex("MainTex",2D)= "white"{} _Diffuse("Diffuse",Color) = (1,1,1,1) _Specular("Specular",Color) = (1,1,1,1) _Gloss("Gloss",Range(8,256)) = 40 _Color("ColorTint",Color) = (1,1,1,1) } SubShader { Pass { Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Diffuse; fixed4 _Specular; fixed4 _Color; float _Gloss; struct a2v { float4 vertex:POSITION; float3 normal:NORMAL; float4 texcoord:TEXCOORD0; }; struct v2f { float4 pos:SV_POSITION; float3 worldNormal:TEXCOORD0; float3 worldPos:TEXCOORD1; float2 uv:TEXCOORD2; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.worldPos = mul(unity_ObjectToWorld,v.vertex); o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject); o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag(v2f i):SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb; fixed3 diffuse = _LightColor0.rgb * albedo * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo; fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(viewDir + worldLightDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss); return fixed4( diffuse + ambient + specular , 1.0); } ENDCG } } FallBack "Diffuse" }
TRASFORM_TEX实在UnityCG.cginc中定义的:
/Transform 2D UV by scale/bias property #define TRANSFORM_TEX(tex,name)(tex.xy * name##_ST.xy + name##_ST.zw)
它接受两个参数
第一个参数是顶点的纹理坐标
第二个参数是纹理的名称
看一下效果:
二、纹理采样区间设置
下面为了阐释纹理的属性,编写一个更为纯粹的纹理属性应用shader
Shader "Custom/Edu/SimpleTextureProperties" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} } SubShader { Pass { Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; struct a2v { float4 vertex:POSITION; float2 texcoord:TEXCOORD0; }; struct v2f { float4 pos:SV_POSITION; float2 uv:TEXCOORD0; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag(v2f i):SV_Target { fixed3 color = tex2D(_MainTex,i.uv); return fixed4(color,1.0); } ENDCG } } FallBack "Diffuse" }
利用这个shader在场景中创建一个Quad。
此时我们对纹理使用的Wrap Mode的是Repeat——重复纹理采样。
这种模式下,如果纹理的坐标超过了1,那么整数部分将会被舍弃,而直接使用小数部分进行采用。
下面我们来看另外一种模式
当使用Clamp模式时,超过的部分将会截取到边界值,从而形成一个条形结构。
三、3种滤波模式
纹理导入面板中的下一个属性是FilterMode属性,它决定了当纹理由于变换而产生拉伸是将会使用哪种滤波模式。
Point模式使用了最近邻滤波,在放大或缩小时,它的采样像素数目通常只有一个,因此图像看起来有种像素风格的效果。
Bilinear滤波采用了线性滤波,对于每个目标像素,它会找4个邻近的像素,然后对他们进行线性插值混合后得到最终的像素,因此图像看起来像被模糊了。
Trilinear滤波几乎和Bilinear是一样的,知识Trilinear还会在多级渐远纹理(mipmapping)之间进行混合。
四、纹理的最大尺寸和纹理模式
【我只安装了安卓build的模块】
Unity允许我们为不同目标平选择不同的纹理尺寸和压缩格式。
如果导入的纹理超过了MaxTextureSize中的设置,那么Unity会把这个纹理所防伪这个最大分辨率。
导入的纹理的长宽大小应该是2的整数次幂。
如果使用了非2的整数次幂大小(Non Power of Two,NPOT)的纹理,那么这些纹理往往会占用更多的内存空间,而且GPU的读取该纹理的速度也会下降。甚至有一些平台不支持NPOY纹理。
对于一些不需要使用很高精度的纹理(例如漫反射颜色的纹理),我们应该尽量使用压缩格式!