紋理基礎知識
-
紋理的最初目的是使用一張圖片來控制模型的外觀,使用紋理映射技術,可以把一張圖“黏”在模型的表面,逐紋素的控制模型的顏色。
-
UV座標通常被歸一化到[0, 1]範圍內。
-
在 OpenGL 中,紋理空間的原點位於左下角,而在 DirectX 中,原點位於左上角。
-
samplar2D _MainTex;
float4 _MainTex_ST; // 紋理的屬性,ST爲縮放和平移的縮寫,_MainTex_ST.xy縮放值,_MainTex_ST.zw 偏移值。可以在材質面板中調節,對應的是 Tiling 和 Offset。 -
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
// 等價於,先縮放,後偏移
o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
紋理屬性
-
Texture Type 紋理類型
- Default 普通貼圖
- Normal map 法線貼圖
- Editor GUI and Legacy GUI UI貼圖
- Sprite(2D and UI) 精靈
- Cursor 鼠標指針
- Cookie 遮罩貼圖
- Lightmap 烘焙貼圖
-
Texture Shape
- 2D 默認類型,2D紋理
- Cube 立方體貼圖,用於Skyboxes或者Reflection Probes
-
sRGB(Color Texture)(標準顏色的紋理)
如果不需要標準RGB顏色的紋理,可以不勾選。 -
Alpha Source:紋理Alpha通道
- None 無,不論紋理是否有Alpha,都設置爲沒有Alpha
- Input Texture Alpha 導入紋理自帶Alpha
- From Gray Scale 透明通道的值會由每個像素的灰度值生成
-
Alpha Is Transparency
當alpha用於透明處理時,我們要勾上該選項,可以防止不透明邊緣鋸齒現象,如果Alpha有它用,可以不勾選。 -
Advanced
- Non power of 2
Unity默認導入的最大紋理尺寸爲8k,當紋理大小不是2的冪次方時,Unity會對其進行縮放,影響效率。 - Read/Write Enabled
從腳本中讀取Texture數據
1.會製作一個Texture的副本,導致Texture的內存增加
2.未壓縮和DXT壓縮紋理纔可讀取,其他壓縮的不能讀取 - Generate Mip Maps
生成mipmap,使遠離相機的物體使用較小的Texture。mipmap 啓用後內存增加33%,但是可以提高性能,優化實時3D渲染性能。 - Border mipmaps:使用較小圖像時防止顏色溢出
- MipmapFiltering:優化圖像質量的過濾方式
box:平滑改變尺寸
Kaiser:銳化算法,少用 - Fadeout MipMaps:淡出功能,在Mip更換級別的時候漸變爲灰色,可用於地圖
- Non power of 2
-
Wrap Mode
它決定了當紋理座標超過[0, 1]範圍後將如何平鋪。- repeat 表示重複,當紋理座標超過1時,整數部分被捨棄,直接使用小數部分進行採樣,紋理會不斷重複。
- clamp 紋理座標大於1時會截取到1,小於0時會截取到0。
-
Filter Mode
它決定了當紋理由於變換產生拉伸時會採用哪種濾波模式。有3種模式:Point,Bilinear,Trilinear。它們得到的圖片濾波效果依次提升,但是需要耗費的性能也依次增大。-
Point模式:最近點採樣,當紋理座標沒有剛好對應Texture上的一個採樣點時,它會選擇最近的一個採樣點作爲該座標的採樣值,當紋理沒有拉伸變形時,這樣還不錯,因爲速度是最快的,但如果拉伸變形了,會出現馬賽克現象。
-
Bilinear模式:雙線性過濾,以像素對應的紋理座標爲中心,採樣它周圍4個texel(紋素)的像素,取平均值作爲該座標採樣值。這是Unity默認的模式,過渡效果相對平滑,當然速度會比最近點採樣有一定下降。
-
Trilinear模式:三線性過濾,會對像素大小和紋素大小最接近的兩層Mipmap level分別進行雙線性過濾,再對結果進行線性插值。由於使用了兩次雙線性過濾,也就是計算2x4=8個像素的值,速度會更加下降,當然濾波效果更好。
-
-
Generate Mip Maps
紋理縮小的過程比放大更加複雜一些,此時原紋理中的多個像素將會對應一個目標像素。這樣就會出現鋸齒現象。一個最常用的方法就是多級漸遠紋理(mipmapping)技術。該技術將原紋理提前用濾波處理來得到很多更小的圖像,形成一個圖像金字塔,每一層都是對上一層圖像降採樣的結果。在實時運行時,比如物體遠離攝像機時,可以直接用較小的紋理採樣。比如一張128x128的圖,會生成64x64,32x32,16x16,8x8,4x4,2x2,1x1的圖片,當然,因爲要多存儲這些圖片,通常會多佔用33%的空間。這是典型的空間換取時間的方法。所以使用時如果因爲攝像機遠近會放大縮小的圖,比如場景資源,我們儘量用mipmap,否則取消mipmap,比如ui元素。 -
Max Size
紋理最大尺寸,在紋理屬性裏我們看最後的部分,因爲沒選所以是灰色的,它會根據不同平臺進行設置,如果我們導入的紋理大小超過了Max Size,那麼Unity會把該紋理縮放成這個最大分辨率。導入的紋理可以是非正方形的,但長寬該是2的冪。如果使用了非2的冪大小(Non Power of Two, NPOT)的紋理,這些紋理往往會佔用更多的內存空間,GPU讀取速度也會下降。所以Unity在內部會把它縮放成最近的2的冪。出於性能和空間考慮,我們應儘量使用2的冪的紋理。 -
Format
Unity內部使用哪種格式存儲該紋理。打開其下拉條,我們看到一捆格式,紋理的精度越高,佔用的內存越大,得到的效果也越好,最好的是RGBA 32 bit,如果沒有alpha通道,RGB 24 bit也是效果最好的,但帶寬太大不是好事,對於不需要使用高精度的紋理,我們要適當的用壓縮格式,這對手機平臺尤其重要。
實例:用一張紋理來代替物體的漫反射顏色
Shader "ZJT/Shader07-01"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_Specular ("Color", Color) = (1, 1, 1, 1)
_Glossness ("Glossness", Range(1, 200)) = 10
}
SubShader
{
Tags{"RenderType"="Opaque"}
LOD 200
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST; // 紋理的屬性,ST爲縮放和平移的縮寫,_MainTex_ST.xy縮放值,_MainTex_ST.zw 偏移值
fixed4 _Specular;
float _Glossness;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
// 等價於
// o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag(v2f v):SV_Target
{
fixed3 worldNormal = normalize(v.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v.worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(v.worldPos));
fixed3 albedo = tex2D(_MainTex, v.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(worldLightDir + worldViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Glossness);
return fixed4(ambient + diffuse + specular, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}