您的位置:首页 > 移动开发 > Unity3D

Unity Shader:Unity网格(1)---顶点,三角形朝向,法线,uv,以及双面渲染三角形

2017-06-15 17:33 585 查看

1,顶点

顶点是网格最基础的组成部分,可通过mesh.vertices获取和赋值。mesh.vertices是一个Vector3的数组,每个Vector3代表了此顶点在世界空间中的位置每个Vector3为此顶点与此游戏物体的相对坐标(local position)。顶点的位置,数量没有任何限制。几个同样的顶点可以组合成若干不同形状,不同数量的三角形。

手动设置顶点:

mesh = new Mesh ();
Vector3[] v3s=new Vector3[3];
v3s [0] = new Vector3 (0, 0, 0);
v3s [1] = new Vector3 (0, 1, 0);
v3s [2] = new Vector3 (1, 0, 0);
mesh.vertices = v3s;


2,由顶点组成三角形

mesh.triangles决定了网格中的三角形的形状和朝向。triangles是一个int数组,此数组长度永远是3的倍数,每三个int代表的是由哪三个顶点并由什么顺序(朝向)来组成一个三角形,例如:

手动设置三角形:

int[] index = new int[3];
index [0] = 0;
index [1] = 1;
index [2] = 2;
mesh.triangles = index;


等号右侧的0,1,2表明网格中将会有一个以 mesh.vertices[0],mesh.vertices[1],和mesh.vertices[2]组成的三角形。此三角形的形状有此三个顶点的位置决定,而它的朝向则由此三个顶点的旋转方向决定:

例如以下三维空间中,z轴方向与视线方向相同,由a-b-c组成的三角形将会背对z轴,既背对我们的视线,



而由b-a-c组成的三角形将会正对z轴,既面对我们的视线:



b-a-c,a-c-b,c-b-a,这些顺序效果相同。任何一个三角形必须单独设定顶点组合顺序,且此顺序与其他三角形不发生关系。mesh内的顶点可以用来组合三角形,也可不用来组合三角形,此处无绝对的对应关系。

3,法线

给网格赋值顶点并组成三角形,将此网格赋值给场景中的meshfilter,meshrenderer就可以将此三角形渲染出来了,



但是法线并不一定是正确的,例如上面声明的三个顶点

v3s [0] = new Vector3 (0, 0, 0);
v3s [1] = new Vector3 (0, 1, 0);
v3s [2] = new Vector3 (1, 0, 0);


不论是由0-1-2还是2-1-0的顺序组成三角形,此三角形的法线总是指向z轴方向,在有光照的场景中会出现错误的效果,这里unity提供了一个方法:

mesh.RecalculateNormals();


此方法将会自动将每个顶点的法线调整到正确方向,既是与面的朝向一致。还提供了一个方法是:mesh.RecalculateBounds();。将会重新计算mesh.bounds,通过mesh.bounds可访问size,center等一些网格的属性。所以在将mesh赋值给meshfilter前需要先调用此两方法。

4,UV

uv是为了防止与xy混淆的另一种写法。u为x轴,v为y轴,最小值为0,最大值为1无数值限制(材质上的uv值一般是0-1,当进行采样的uv值大于1时会按取1的余数来计算)。uv值用处很多,例如材质texture的采样,也既是贴图。或是利用它的一些特性(范围0-1;同一个uv区域每个像素的uv值皆不同)进行GPU内的一些计算。

当每个顶点被赋予uv值后,在片段着色阶段,片段着色器将会根据顶点的uv值将此三角形覆盖的每个像素的uv值进行自动插值:



例如此三角形中,假设三角形ABC的边bc由10个像素块组成,ABC三个顶点的UV值如括号内所示,那么BC直线(假设与x轴完全平行)上每个像素的uv值将左起依次为(0,0.1),(0,0.2),(0,0.3)…(0,1)。

uv插值是以三角形为单位进行的,既三角形ABC内各个像素的uv值与其他周边三角形各顶点的uv值是无关的。假如出现D点,那么ACD内各像素的uv值将会由A,C,D的uv值来决定:



当D的uv值为(1,1)时进行的2D采样贴图:

Vector2[] uvs = new Vector2[4];
uvs [0] = Vector2.zero; //(0,0)
uvs [1] = Vector2.up;   //(1,0)
uvs [2] = Vector2.right;//(0,1)
uvs [3] = Vector2.one;  //(1,1)
mesh.uv = uvs;


mesh.uv作为一个Vector2数组与mesh.vertices的长度必须一致,每个uv的顺序与vertices的顺序也是一一对应,既uv[0]代表的是vertices[0]的uv值。而与三角形组成的顺序无关。

(下图)当D的uv值为(1,1)时:



(下图)当D的uv值为(0,0)时进行的2D采样贴图:



(下图)当D的世界坐标位置为(3,3,0)时进行的2D采样贴图:



顶点uv值的赋值除了必须在0-1之内以外没有任何其他限制,如有特殊需要可以都为(0,0),或没有上下左右顺序。但很多GPU的方法的实现都需要一个有秩序的uv,例如上面作为例子的2D采样贴图,之所以会出现”正确的”和“错误的”效果,是由于原图作为sampler2D缓存入GPU之后,原图的每个像素点都有一个独立的uv值(由左下角(0,0)开始插值直至右上角(1,1))。



而在进入片段着色器后,tex2D命令将会根据当前三角形内像素的uv值去寻找sampler2D上的颜色,再根据此颜色对三角形内的此像素进行渲染。而由于顶点uv值的不同而导致的三角形内uv值分布的差异,以上例子中出现了不同的效果。

附:双面网格

可以利用上面的知识实现一个双面的网格,既是利用四个顶点以顺时针方向组成两个三角形再以逆时针方向组成两个三角形,四个顶点赋uv值后每个点的正反两面的uv皆相同,进而出现镜像效果。此效果与shader调用cull off选项后的效果相同,以下为实现方法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Triangle : MonoBehaviour {

public MeshFilter meshFilter;
private Mesh mesh;

// Use this for initialization
void Start () {
Vector3[] v3s=new Vector3[4];

mesh = new Mesh ();

v3s [0] = new Vector3 (0, 0, 0); v3s [1] = new Vector3 (0, 1, 0); v3s [2] = new Vector3 (1, 0, 0);

v3s [3] = new Vector3 (1, 1, 0);

mesh.vertices = v3s;
int[] index = new int[12];
index [0] = 0;
index [1] = 1;
index [2] = 2;

index [3] = 3;
index [4] = 2;
index [5] = 1;

index [6] = 1;
index [7] = 0;
index [8] = 2;

index [9] = 3;
index [10] = 1;
index [11] = 2;

mesh.triangles = index;

// Debug.Log (mesh.uv.Length);

Vector2[] uvs = new Vector2[4];
uvs [0] = Vector2.zero;
uvs [1] = Vector2.up;
uvs [2] = Vector2.right;
uvs [3] = Vector2.one;

mesh.uv = uvs;

mesh.RecalculateBounds();
mesh.RecalculateNormals();

meshFilter.mesh = mesh;

}

// Update is called once per frame
void Update () {

}
}


————————————————————————————

维护日志:

2017-8-22:更改了标题

2017-10-11:Review,更改了一些错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity shader 网格