UnityShade入门精要(3·2DTexture)
Texture
想要完成贴图的效果,是通过纹理映射,找到顶点对应的纹理坐标,查询纹理上对应的纹素,根据纹素的颜色设置顶点的颜色
OpenGL和DirectX在二维纹理空间中的坐标系有差异,在OpenGL里,纹理空间的原点位于左下角,而在DirectX中,原点位千左上角。Unity使用的纹理空间是符合OpenGL的传统的, 也就 是说,原点位于纹理左下
在shader中,新增纹理等属性,创建在 CG 代码片中声明和上述属性类型相匹配的变量,其中_MainTex_ST可以让我们得到该纹理的缩放和平移 (偏移)值
定义输入输出struct:TEXCOORDn(当作为顶点着色器向片段着色器传递的语义时,可以传递各种不同类型的数据,简化了shader的开发复杂度)
v .texcoord. xy * MainTex ST. xy + MainTex ST.zw;
vertex shader中_MainTex_ST.xy对顶点纹理坐标进行缩放,_MainTex_ST.zw 对结果进行偏移,TRANSFORM_TEX内翌宏 来帮我们计算上述过程
fragment shader中,CG使用tex2D 函数对纹理进行采样,它的第一个参数是需要被采样的纹理,第二个参数是一个float2类型的纹理坐标,采样的颜色值作为顶点的albedo漫反射颜色值
unity texture属性
在unity editor中,我们要正确设置纹理的属性,
- texture type
- Wrap Mode 决定了当纹理坐标超过[O, l]范围后将会如何被平铺。
- Repeat,如果纹理坐标超过了 1 那么它的整数部分将会被舍弃,而直接使用小数部分进行采样,这样的结果是纹理将会不断重复;
- Clamp,如果纹理坐标大千 1, 那么将会截取到 1, 如果小于O, 那么将会截取到 0。
- FilterMode指定纹理过滤模式的枚举类型(当像素和纹理分辨率不同时(需要放大纹理时),采样哪个纹素),支持 3 种模式
- Point点过滤(最近邻(nearest neighbor)算法),选取距离该像素中心最近的纹素,呈现出块状或像素化的外观
- Bilinear双线性过滤,会考虑该像素所覆盖的纹理区域周围的 4 个纹素进行加权平均计算
- Trilinear三线性过滤,Mipmap 级别之间进行线性插值计算
- 纹理缩小的过程比放大更加复杂一些,此时原纹理中的多个纹素将会对应一个目标像素,最常使用的方法就是使用多级渐远纹理(mipmapping),
- 需要修改纹理类型,即可开启多级渐远纹理技术,需要使用一定的空间用于存储这些多级渐远纹理,这是一种典型的用空间换取时间的方法,并和会配合FilterMode使用
- 当我们在为不同平台发布游戏时,需要考虑目标平台的纹理尺寸和质凰问题。允许我们为不同目标平台选择不同的分辨率
- Max Size 如果导入的纹理大小超过了设置的值,那么会把该纹理缩放为这个最大分辨率(出于性能和空间的考虑,我们应该尽量使用 2 的幕大小的纹理。)
- Format 决定了 Unity 内部使用哪种格式来存储该纹理,纹理格式精度越高,占用的内存空间越大,但得到的效果也越好
凹凸映射(bumpmapping)
为了让贴图看起来更真实,渲染凹凸感,有两种方法可以做到(性能原因并不改变模型的网格),两者通常一起使用
高度纹理 (height map) 进行高度映射,存储黑白的高度值,颜色越浅表明该位置的表面越向 外凸起,而颜色越深表明该位置越向里凹
法线纹理 (normal map)进行法线映射 ,它存储了法线的方向,对于之前一直使用的法线纹理,是定义在模型空间中(世界空间)的,但是在实际存储时,通常会使用切线空间存储法线:
切线空间:每个顶点都有一个属千自己的切线空间,TBN -》切线,副切线,法线 -》xyz
比如模型空间的法线存储在贴图,通常是颜色各异,是因为对于世界坐标来说,法线方向都不同,但是对于切线空间的法线存储在贴图,通常呈现蓝色,因为法线相对于各自顶点的切线空间是方向趋近于z的
我们进行光照计算都需要保证在同一坐标系下运算, 由于法线纹理中存储的法线是切线空间下的方向,我们通常有两种选择:
- 一种选择是在切线空间下进行光照计算,效率更高,可以在顶点着色器中就完成对光照方向和视角方向的变换
- 另一种选择是在世界空间下进行光照计算,更通用,因为有时我们需要在世界空间下进行一些计算
切线空间下法线贴图实战
在切线空间进行光照模型的计算,我们需要知道从模型空间到切线空间的变换矩阵,这个变换矩阵的逆矩阵,即从切线空间到模型空间的变换矩阵是非常容易求得的,因为这个逆矩阵变换就等于它的转置矩阵,因此将顺序按行排列即可
首先在Properties中新增两个属性,_BumpMap凹凸贴图和_BumpScale凹凸程度
输入结构体中,新增: TANGENT语义获取切线,
在vertexshader中根据normal和tangent的cross叉乘获取副切线,使用v.tangent.w和叉积结果进行相乘,这是因为和切线与法线方向都垂直的方向有两个, 而 w 决定了我们选择其中哪一个方向。
法线分量范围在[-1, l],而像素的分量范围为[O, 1],法线纹理中存储的范围是经过映射的范围,在Shader 中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向
在fragment shader中,需要*2-1的反映射过程。由于法线都是单位矢量,因此tangentNormal.z 分量可以由 tangentNonnal.xy 计算而得,
UnpackNormal ()来得到正确的法线方向
世界空间下法线贴图实战
需要输出到fragment shader的,切线空间到世界空间的变换矩阵,但是一个插值寄存器TEXCOORDn,最多只能存储float4大小的变量,对于矩阵这样的变量,我们可以把它们按行拆成多个变量再进行存储
为了充分利用寄存器存储空间,尽量把分量存储在一个类型的变量中
这个矩阵和上次的TBN不同,是它的转置==逆矩阵,因此是从切线空间到世界空间的矩阵
对于法线需要从切线空间变换到世界空间,行*列通过dot实现,half3构造一个3维的向量
法线纹理类型
作为法线贴图的纹理需要将texture type设置为normal map类型??
normal map类型可以让Unity根据不同平台对纹理进行压缩,通过 UnpackNormal 函数来针对不同的压缩格式对法线纹理进行正确的采样
作为高度图的纹理需要将texture type设置为normal map类型,并且要勾选 Createfrom Grayscale,用于从高度图中生成法线纹理的
渐变纹理
渐变纹理实际就是一个一维纹理,在纵轴方向上颜色不变,根据对法线方向和光照方向的点 积,来构建一个纹理坐标(即采样位置)
那么根据受光情况不同,会施加不同的颜色,
遮罩纹理
遮罩可以保护某些区域,使它们免于某些修改。比如我们希望模型表面某些区域的反光强烈一些,而某些区域弱一些。
使用遮罩纹理的流程一般是:通过采样得到遮罩纹理的纹素值,然后使用某个通道值与某种表面属性(例如镜面颜色部分)进行相乘,当该通道的值为 0 时,可以保护表面不受该屈性的影响。
高光遮罩纹理:逐像素地控制模型表面的高光反射强度。
普通纹理,法线纹理切线空间,法线纹理世界空间,遮罩纹理