资料参照:《Unity Shader入门精要》— 冯乐乐 第7章 基础纹理
【技术美术百人计划】图形 1.3 纹理的秘密
庄懂的技术美术入门课(美术向)-直播录屏-第9课
Unity Shader 入门到改行4——最简纹理采样
1.纹理是什么
1、宏观上是一张2D图片,一个像素上有 RGB 值
2、不局限于存储图片,可以存储高度、法线等信息
纹理优化方式和原理:
CPU优化:纹理图集、纹理数组,减少DrawCall
GPU优化:纹理压缩,降低带宽
纹理设置-Wrap Mode
决定UV值在[0,1]以外的表现
Repeat:重复 (序列帧图片一般用这个)
Mirror:重复并镜像
Clamp:超出范围的纹理值使用其临近的边缘值
Border:为超出范围的纹理值使用一个定值
纹理设置-FilterModel
过滤设置,当纹理通过变化产生拉伸的时候,要使用哪种滤波来进行纹理的表现
Point :点采样模式,屏幕像素会需找最近的贴图像素点,来作为输出,这种比较生硬,但是性能好,不抗锯齿。
Bilinear :*双线性采样模式, 采用最近的4个像素来做线性插值,有平缓的过渡。可以解决贴图放大的问题,但是贴图缩小依然有锯齿。 缩小可以用mip-map来解决。
Trilinear :三线性采样模式,可以比较好的解决贴图缩小和放大。
缺点:牺牲了性能,像素处理速度变慢,贴图大小增加1/3。
优点:画面好,GPU可以根据光栅化结果导入一层MIP-MAP到显存中,减轻硬件负担,综合起来,能弥补像素处理的速度。
MipMap
将纹理按照不同等级进行缩放,查找纹理像素时根据缩放等级到对应的mipmap里进行采样。
立方体贴图 CubeMap
立方体6个面的贴图。常用在环境光照(环境球)
凹凸贴图 Displacement Mapping
法一:高度纹理:用一张图记录高度信息然后做表面位移和法线修改。
法二:法线纹理Normal:用一张图记录法线来模拟高光的效果。
位移贴图 Displacement Mapping
2. uv 坐标
什么是 uv 坐标?
uv 坐标首先是一个二维坐标,水平方向是U,垂直方向是V,而坐标用来确定一个位置,uv 就是用来确定一个纹素在某张纹理上的位置。
uv 坐标的范围是多少?
纹理的大小各异,希望能用同一个范围内的坐标来表示所有大小的纹理的 uv 坐标,所以 uv 坐标通常是归一化后的坐标,区间在[0,1]。
如何获得 uv 坐标,或者说 uv 坐标存放在哪里?
uv 坐标是存放在顶点数据中的。在模型中,每一个顶点有一个属性,表示uv坐标。在3d游戏中,3d建模人员在建模软件中完成顶点绑定纹理坐标的操作。
纹理采样是什么意思?
给定一个 uv 坐标和一张纹理,获得这张纹理在 uv 坐标处的纹素的颜色值。这个过程就是纹理采样。
uv 坐标会超过取值范围吗?
通常存储在模型顶点中的 uv 坐标都在 [0,1] 区间内。但是在进行纹理采样时,传入的 uv 却不一定,这是因为我们可以在 着色器内对 uv 进行各种计算。
在进行纹理采样时,如果接受到一个 不在 [0, 1] 区间的纹理坐标时,如何确定该返回哪个纹素,由纹理的“平铺模式Wrap Mode”确定。在 Unity 中,纹理的“平铺模式”通常由引擎开发选项进行设置。
3. shader中实现纹理采样:
2D纹理采样
//属性声明
_MainTex ("MainTex", 2D) = "white" {
}
// 输入参数
uniform sampler2D _MainTex; float4 _MainTex_ST; //纹理名_ST,可以通过这个变量获得纹理的缩放(.xy)和平移值(.zw)
// 纹理采样
float3 diffuse = tex2D(_MainTex, i.uv);
CubeMap采样
设置TextureShape为“Cube”
//属性声明
_Cubemap("环境球", Cube) = "_Skybox" {
}
// 输入参数
uniform samplerCUBE _Cubemap;
//采样Cubemap _CubemapMip为Mip值
float3 vrDirWS = reflect(-vDirWS, nDirWS);
float3 var_Cubemap = texCUBElod(_Cubemap, float4(vrDirWS, _CubemapMip)).rgb;
法线贴图Normal
法线纹理的思想是取代对三角面上的顶点法线进行插值,而是简单的通过从纹理中取样来获取法线方向。
设置TextureType为“Normal map”
//属性声明 bump
_NormTex ("RGB:法线贴图", 2D)= "bump" {
}
// 输入参数
uniform sampler2D _NormTex;
// 采样并解码nDirTS
float3 nDirTS = UnpackNormal(tex2D(_NormalMap, i.uv0)).rgb;
构造TBN矩阵,计算nDirWS
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS);
//获得切线空间转世界空间下的法线方向
float3 nDirWS = normalize(mul(nDirTS, TBN));