视频讲解
Unity中对UV的理解
一、什么是UV
UV是什么?UV其实就纹理坐标轴的另外一种定义;
比如平时我们说的Transform的坐标,就是说该Transform在笛卡尔坐标系中的具体位置,描述该位置的形象表示用(x,y,z);所以平时我们说到物体的坐标的时候,潜意识就会想到(x,y,z);
为什么纹理也会有坐标呢?
比如下面这张图片:
从这张图片我们可以通过查看它的信息,知道这张图片的大小是多少,尺寸是多少,什么时候创建的,但是我们并不会去关心这张图片的坐标,因为这个时候我们并不会用上它的坐标,但是它却是存在的。
当我们把上面这张纹理放置在一个Quad上面的时候的时候,就需要用到纹理的坐标:看下面效果:
上面的是图片上Unity运行的效果,其中有5个大小相等Quad,但是它们使用同一张纹理得到的结果是不一样的;这就是因为每个Quad所获取的纹理的UV坐标范围不一样,导致Quad渲染的结果不一样;
二、实例分析
UV坐标存在的意义就是在于和物体坐标之间的一个映射关系;
比如上图中的纹理大小是512x512,我们不能用512*512这个大小去进行映射,如果我们是映射到同样大小的物体上,它们是1对1的关系,也就是每个物体的坐标点和纹理的UV坐标点是对应上的;否则映射过程上非常麻烦的;
那么什么是物体坐标呢?
看看下面的一个Quad的图片:
上图中物体Pivot和Center都在中心,在线框模式可以看到该物体有4个顶点,2个三角形;要想把一个纹理的全部或者部分完全在这个quad上面正确显示出来,就需要用到uv坐标,需要以下几个步骤:
- 找到该Quad的三角形的渲染方式,是逆时针还是顺时针;会得到正面或者反面的图片
- 找到该Quad的4个UV坐标的先后顺序,也就是确认uv[0]、uv[1]、uv[2]、uv[3]和顶点坐标vertext[0]、vertext[1]、vertext[2]、vertext[3]的对应关系,正确的对应关系才能得到正确的结果;这里你可以用UV坐标的任意合理范围的4个uv坐标点来对应顶点坐标,这样就可以得到纹理中部分或者全部渲染到quad上;
- uv坐标的取值范围都会转换到0-1之间;比如512大小图片范围就会转为:uv(0/512,0/512)-(512/512,512/512)的取值范围,根据图片大小进行转换;
- 最后重新设置对象的UV坐标即可;
UV是纹理坐标的别称,UV坐标就是xy坐标,只是为了便于在描述过程中的区别才有这个说法;所以处理uv坐标就是处理笛卡尔坐标系;可以把一个纹理看作一个笛卡尔坐标系中的第一象限,左下角就是(0,0)坐标,该坐标的第一象限的最大取值范围就是0-1之间;
纹理和对象之间的映射就是纹理坐标和物体坐标之间的映射关系,纹理坐标的正确映射可以得到物体正确的外观效果。
三、源码
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ValakiShowUvPartDemos : MonoBehaviour
{
enum ShowUVPartType
{
None=0,
LeftTop,
LeftBottom,
Center,
RightTop,
RightBottom
};
List<Vector2> uvs;
Vector2[] backUv;
Mesh mesh;
[SerializeField] ShowUVPartType showUVPartType;
[SerializeField] bool isPlayRandomUvPart;
ShowUVPartType currentUvPartType = ShowUVPartType.None;
private void Awake()
{
mesh = GetComponent<MeshFilter>().mesh;
uvs = mesh.uv.ToList();
backUv = mesh.uv;
}
float totalTime;
private void LateUpdate()
{
if (isPlayRandomUvPart)
{
totalTime += Time.deltaTime;
if(totalTime >= 0.7f)
{
totalTime = 0;
int random = Random.Range(1, 6);
showUVPartType = (ShowUVPartType)random;
}
}
if (currentUvPartType != showUVPartType)
{
currentUvPartType = showUVPartType;
switch (showUVPartType)
{
case ShowUVPartType.Center:
mesh.SetUVs(0, backUv);
break;
case ShowUVPartType.LeftBottom:
ShowLeftBottomUvPart();
break;
case ShowUVPartType.LeftTop:
ShowLeftTopUvPart();
break;
case ShowUVPartType.RightBottom:
ShowRightBottomUvPart();
break;
case ShowUVPartType.RightTop:
ShowRightTopUvPart();
break;
}
}
}
void ShowLeftTopUvPart()
{
//左-右下-左上-右上
uvs[0] = new Vector2(0, 0.75f);
uvs[1] = new Vector2(0.25f, 0.75f);
uvs[2] = new Vector2(0, 1);
uvs[3] = new Vector2(0.25f, 1);
mesh.SetUVs(0, uvs);
}
void ShowRightTopUvPart()
{
uvs[0] = new Vector2(0.75f, 0.75f);
uvs[1] = new Vector2(1, 0.75f);
uvs[2] = new Vector2(0.75f, 1);
uvs[3] = new Vector2(1, 1);
mesh.SetUVs(0, uvs);
}
void ShowLeftBottomUvPart()
{
uvs[0] = new Vector2(0, 0);
uvs[1] = new Vector2(0.25f, 0);
uvs[2] = new Vector2(0, 0.25f);
uvs[3] = new Vector2(0.25f, 0.25f);
mesh.SetUVs(0, uvs);
}
void ShowRightBottomUvPart()
{
uvs[0] = new Vector2(0.75f, 0);
uvs[1] = new Vector2(1, 0);
uvs[2] = new Vector2(0.75f, 0.25f);
uvs[3] = new Vector2(1, 0.25f);
mesh.SetUVs(0, uvs);
}
}
结语:
别问我段位多少,我只是混子而已【valaki】