赞
踩
这里我们会聊聊基础纹理和透明效果的知识。
我们用纹理干什么呢?使用它来控制模型的外观,使用的是纹理映射技术来让纹理与模型表面贴住,在逐纹素地控制模型颜色。
在建模时,美术人员会将纹理映射坐标存储在每一个顶点上,它定义了该顶点在纹理中对应的2D坐标。通常这些坐标使用一个二位变量(u,v)来表示,u为横向坐标,v是纵向坐标。这也是我们所称的UV坐标。
我们通常将UV坐标的范围归一化到[0,1]范围内,但在纹理采样时使用的纹理坐标不一定在[0,1]内,因为在纹理的平铺模式下,[0,1]之外的纹理坐标与[0,1]之内的纹理坐标的纹理采样是不同的。
但似乎你还是不知道纹理是什么?纹理坐标又代表什么?
纹理可以理解为一个物体身上颜色以及物体表面的光滑和粗糙情况。
而纹理坐标呢?
它实际上是一个二维数组,它的元素是一些颜色值。单个颜色值被称为纹理元素或纹素,每一个纹理像素在纹理中都有一个唯一的地址,也就是我们之前所说UV坐标。
当我们将一个纹理应用于一个图元时,它的纹理像素地址必须要映射到对象坐标中,然后再平移到屏幕坐标系或像素位置上去。
在这个例子中,我们使用Blinn-Phong光照模型进行计算:
①在高光反射的Properties中多增加一项纹理属性:
Properties{
_Color ("Color Tint", Color) = (1,1,1,1)
_MainTex ("Main Tex", 2D) = "white" {}
_Specular("Specular", Color) = (1,1,1,1)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
我们声明了一个名为_MainTex的纹理,使用一个字符串和一个花括号作为它的初值。
②在CG代码块中声明与上述属性相匹配的变量:
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
float _Gloss;
对第三个变量_MainTex_ST,这是为纹理类型的属性声明的。
_MainTex_ST.xy存储的是纹理的缩放值;
_MainTex_ST.zw存储的是纹理的偏移值。
这两个值都可以在材质面板的纹理属性中调节。
③定义顶点着色器的输入和输出结构体:
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;
};
在a2v中使用TEXCOORD0语义声明变量texcoord,使Unity将模型的第一组纹理坐标存储到该变量中。
在v2f中添加用于存储纹理坐标变量的uv,以便在片元着色器中使用该坐标进行纹理采样。
有的朋友可能会说,为什么worldMormal和worldPos要声明成纹理坐标?因为这整个shader都是为纹理采样服务的,我们将纹理坐标映射到顶点上去后,依然是要对顶点的值进行反射处理。
④定义顶点着色器:
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
先使用缩放属性_MainTex_ST.xy将纹理坐标映射到顶点坐标上去,再使用偏移属性 _MainTex_ST.zw对结果进行偏移。这样之后纹理就能显示在屏幕坐标系中了。
⑤最后在片元着色器中计算光照反射模型,这里使用的就不再是顶点的原始坐标,而是纹理映射后的坐标。
fixed4 frag(v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 aldebo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; //自然光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * aldebo; //漫反射 fixed3 diffuse = _LightColor0.rgb * aldebo * max(0, dot(worldNormal, worldLightDir)); //高光反射 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 halfDir = normalize(worldLightDir + viewDir); fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); return fixed4(ambient + diffuse + specular, 1.0); }
最主要的区别就是使用tex2D函数对纹理进行采样:第一个参数是需要被采样的纹理,第二个参数是存储返回计算得到的纹素值的变量。
材质最终反射出的颜色就是采样的结果与颜色属性_Color的乘积。在计算环境光时,就不仅仅像之前的只是一个UNITY_LIGHTMODEL_AMBIENT.xyz了,也要乘上材质反射出的颜色。
高光反射的计算依然没有变化,只是每个值的内容变化了而已。
当然最后别忘了Fallback。
这样写完之后你是不是会感慨:原来纹理映射是如此的简单。但实际上,在渲染流水线中,纹理映射的实现比我们所想象的要复杂得多。接下俩就会聊聊关于纹理属性的知识。
其实也主要是了Wrap Mode和Filter Mode的内容,它决定了当纹理坐标超过[0,1]范围后将会如何被平铺。
(1)在笔者使用的Unity2018.4.27中Wrap Mode一共有五种类型:
①Repeat重复模式
在这种模式下,如果纹理坐标超过了1,那么它的整数部分就会被舍弃而直接使用小数部分进行采样,结果就是纹理会不断重复;
②Clamp边缘拉伸模式
在这种模式下,如果纹理坐标超过了1,就会截取到1,如果小于0,就会截取到0;
③Mirror镜像模式
超出的部分将以镜像的方式展开
④Mirror Once一次镜像
字面理解起来是镜像一次,但不知道为什么它的效果跟Camp的效果是一样的;
⑤Per-axis平铺
分别对U、V方向超出部分分开做平铺处理。
(2)Filter Mode属性,它决定了当纹理由于变换而产生拉伸时将会采用哪种滤波器。它有三种模式:Point、Bilinear、Trilinear,它们得到的图片的滤波效果一
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。