底层Mesh合并研究
之前Unity模型合并调研(2)的那些代码都是基于CombineMeshes这个函数的包装,可以说没有任何"核心技术"的,接下来研究一下Mesh的底层。
加深对Mesh的理解。然后去看看MeshBaker,这个插件里面没用用到CombineMeshes是自己修改的Mesh。后面还要去看看ProBuilder。
1.创建Mesh
1.1 Plane
参考:https://blog.csdn.net/qq_22472397/article/details/81263155
照着抄了一下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlaneCreator : MonoBehaviour
{
public int width=10;
public int height=10;
public Color color=Color.green;
public GameObject result;
public const string DefaultShader="HDRP/Lit";//"Standard","HDRP/Lit"
// Start is called before the first frame update
void Start()
{
result=GenerateGO();
}
private Mesh GenerateMesh(){
Mesh mesh=new Mesh();
int x=0;
int y=0;
//创建顶点和UV
Vector3[] vertices=new Vector3[height*width];
Vector2[] uv=new Vector2[height*width];
//把uv缩放到0-1
Vector2 uvScale=new Vector2(1.0f/(width-1),1.0f/(height-1));
for(y=0;y<height;y++)
{
for(x=0;x<width;x++)
{
vertices[y*width+x]=new Vector3(x,0,y);
uv[y*width+x]=Vector2.Scale(new Vector2(x,y),uvScale);
}
}
mesh.vertices=vertices;
mesh.uv=uv;
//三角形index
int[] triangles=new int[(height-1)*(width-1)*6];
int index=0;
for(y=0;y<height-1;y++)
{
for(x=0;x<width-1;x++)
{
//每个格子2个三角形,总共6个index
triangles[index++]=(y*width)+x;
triangles[index++]=((y+1)*width)+x;
triangles[index++]=(y*width)+x+1;
triangles[index++]=((y+1)*width)+x;
triangles[index++]=((y+1)*width)+x+1;
triangles[index++]=(y*width)+x+1;
}
}
mesh.triangles=triangles;
mesh.RecalculateNormals();
return mesh;
}
private GameObject GenerateGO(){
GameObject obj=new GameObject("Mesh_Plane");
MeshFilter meshFilter=obj.AddComponent<MeshFilter>();
//创建mesh
Mesh mesh=GenerateMesh();
meshFilter.mesh=mesh;
MeshRenderer renderer=obj.AddComponent<MeshRenderer>();
//创建材质
Material mat=new Material(Shader.Find(DefaultShader));
//mat.color=Color.green;
mat.SetColor("_BaseColor",color);
renderer.material=mat;
return obj;
}
[ContextMenu("Recreate")]
private void Recreate(){
if(result)
GameObject.DestroyImmediate(result);
result=GenerateGO();
}
}
1.修改1 width,height
10*10的参数,创建出来的是0-9的Plane。4*4创建出来的是3*3,总觉得怪怪的。
具体算法部分我是不会改的,不过既然都差一,在进入算法前,设置一下就好了。这样就符合直观感受了。
private Mesh GenerateMesh(){
int h=height+1;
int w=width+1;
//..后面都是h,w了。
}
2.修改2 密度
加个密度参数,这里的Plane里面两个点之间的距离是1,实际上对于一些模型来说太大了,修改一下它的密度。
public float scale=1;//密度
Vector2 uvScale=new Vector2(1.0f/(w-1),1.0f/(h-1));
for(y=0;y<h;y++)
{
for(x=0;x<w;x++)
{
vertices[y*w+x]=new Vector3(x*scale,0,y*scale);
uv[y*w+x]=Vector2.Scale(new Vector2(x,y),uvScale);
}
}
看起来和5*5是一样的,但是点的数量增加了
3.uv
对于纯色的材质,没有设置uv也没有影响的,但是对于有贴图的材质,没有uv,则没有贴图,和用3dmax建模但是没分uv的模型状态一样。这时候从数据上讲mesh.uv的数组的数据是空的。
对于Plane平面来说uv还好算的,正方体也好算的,但是对于其他模型uv怎么弄呢,立体的三角形,椎体,球体。
对于HDRP/Lit材质来说,Base UI mapping有个Planar和Triplanar的选项,可以给没有uv的模型显示贴图。
对于Plane来说,感觉像是直接把顶点坐标给了uv用。
移动模型,贴图真的是按照世界坐标来的。
4.法线
RecalculateNormals()没使用也是可以的,看不出区别。
RecalculateNormals()官方说明(https://docs.unity3d.com/2017.2/Documentation/ScriptReference/Mesh.RecalculateNormals.html)是根据顶点和三角面计算出来的。
只能说对于Plane来说,默认就是正确的。看了下,RecalculateNormals()后,mesh.normals的数据都是(0,1,0)
本来我考虑把normals都改成(0,-1,0),应该是上面没显示,下面显示,结果不是
变成了
是那种场景中没有光照时的效果,也就是说法线反了后,不影响是否显示,影响了光照量。
学了些shader后现在能理解了,就是光照和法线的夹角问题。
官网有个例子(https://docs.unity3d.com/2017.2/Documentation/ScriptReference/Mesh-normals.html),旋转法线,对于Plane来说,绕着up旋转,怎么转都一样的, 要改一下,dir。
public class RotateNormal : MonoBehaviour
{
public float speed = 100.0f;
public Vector3 dir=Vector3.up;
public MeshFilter meshFilter=null;
public Quaternion rotation;
void Start()
{
meshFilter=GetComponent<MeshFilter>();
}
public Vector3[] normals;
// Update is called once per frame
void Update()
{
// obtain the normals from the Mesh
Mesh mesh = meshFilter.mesh;
//Vector3[] normals = mesh.normals;
normals = mesh.normals;
// edit the normals in an external array
rotation = Quaternion.AngleAxis(Time.deltaTime * speed, dir);
for (int i = 0; i < normals.Length; i++)
normals[i] = rotation * normals[i];
// assign the array of normals to the mesh
mesh.normals = normals;
}
}
2.修改Mesh