当前位置:   article > 正文

Unity-shader学习笔记(五)_shader saturate归一化 uv坐标

shader saturate归一化 uv坐标

Unity-shader学习笔记(五)

这里我们会聊聊基础纹理和透明效果的知识。

13 基础纹理

我们用纹理干什么呢?使用它来控制模型的外观,使用的是纹理映射技术来让纹理与模型表面贴住,在逐纹素地控制模型颜色。

在建模时,美术人员会将纹理映射坐标存储在每一个顶点上,它定义了该顶点在纹理中对应的2D坐标。通常这些坐标使用一个二位变量(u,v)来表示,u为横向坐标,v是纵向坐标。这也是我们所称的UV坐标。

我们通常将UV坐标的范围归一化到[0,1]范围内,但在纹理采样时使用的纹理坐标不一定在[0,1]内,因为在纹理的平铺模式下,[0,1]之外的纹理坐标与[0,1]之内的纹理坐标的纹理采样是不同的。

但似乎你还是不知道纹理是什么?纹理坐标又代表什么?

13.1 纹理与纹理坐标

纹理可以理解为一个物体身上颜色以及物体表面的光滑和粗糙情况。

而纹理坐标呢?

它实际上是一个二维数组,它的元素是一些颜色值。单个颜色值被称为纹理元素或纹素,每一个纹理像素在纹理中都有一个唯一的地址,也就是我们之前所说UV坐标。

当我们将一个纹理应用于一个图元时,它的纹理像素地址必须要映射到对象坐标中,然后再平移到屏幕坐标系或像素位置上去。

13.2 单张纹理

在这个例子中,我们使用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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们声明了一个名为_MainTex的纹理,使用一个字符串和一个花括号作为它的初值。

②在CG代码块中声明与上述属性相匹配的变量:

fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
float _Gloss;
  • 1
  • 2
  • 3
  • 4
  • 5

对第三个变量_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;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

先使用缩放属性_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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

最主要的区别就是使用tex2D函数对纹理进行采样:第一个参数是需要被采样的纹理,第二个参数是存储返回计算得到的纹素值的变量。

材质最终反射出的颜色就是采样的结果与颜色属性_Color的乘积。在计算环境光时,就不仅仅像之前的只是一个UNITY_LIGHTMODEL_AMBIENT.xyz了,也要乘上材质反射出的颜色。

高光反射的计算依然没有变化,只是每个值的内容变化了而已。

当然最后别忘了Fallback。

13.3 纹理的属性

这样写完之后你是不是会感慨:原来纹理映射是如此的简单。但实际上,在渲染流水线中,纹理映射的实现比我们所想象的要复杂得多。接下俩就会聊聊关于纹理属性的知识。

其实也主要是了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,它们得到的图片的滤波效果一

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/235882
推荐阅读
相关标签
  

闽ICP备14008679号