前言
之前写过一篇unity 的贴花编辑,当时是学习unity插件的代码。最近因为工作需要,做了一个可以编辑纹理局部贴花的功能实现。
大致上是根据以前的思想。但是自己用openGL 实现一遍,还是切身体会到不少深入的东西。
回顾之前的贴花编辑器的思想,在unity 中用gameObject 作为 decal。 然后通过对影响到的mesh做切割,获取顶点,再重新计算mesh信息(uv, tangent,normal).最后把这个mesh给 decal Object。 因为不同的object 可以有不同的material,进而可以对decal 的material 重新赋予纹理。即是最后的贴花效果。
这里,重点说下其中的几个移植过程中,不能调用unity API,而需自己写的一些部分功能。
1. 计算切线空间
要想模拟表面的凹凸,通过光照的明暗来表现凹凸感更好。然而在TBN空间,法线都是垂直向上的。要进行光照计算。需要把法线从TBN坐标转化到世界坐标系。所以需要切线空间的计算。计算方法根据纹理坐标和位置坐标的关系,可以得出。这里计算得到的切线空间是基于单个三角形的,而在3D管线中,处理的是基于顶点。对于任一顶点,我们使用其所在的三角形所对应的切线空间向量的平均值,作为该顶点的切线空间。
verctor3D mesh:: generateTangent(const vector3D &a, const vector3D &b, const vector3D &c,
const vector2D &ta, const vector2D &tb, cosnt vector2D &tc)
{
vector2D coord1 =tb -ta;
vector2D coord2 = tc - ta;
vector3D vertex1 = b-a;
vector3D vertex2 = c-a;
vector3D axis = vector3d(vertex1 * coord2.y() - vertex2* coord1.y());
float factor;
float f = coord1.x()* coord2.y() - coord2.x()* coord1.y();
if(f) factor = 1.0f/f;
else factor =0;
return axis * f
}
2. 点在平面的判断 以及 在平面上取点,如果三角形有点在立方体之外
因为是根据立方体来裁mesh,那么在如果三角形有部分点在立方体内部,有部分在立方体外部。则我们需要把外部的点去掉,改为在立方体平面上取点来代替。
点再平面的判断,可以计算点在平面的距离(利用平面点和法线)。根据距离为(>0,=0,<0)确定点和平面的位置关系。
根据两个点(面两侧的点)到面的距离作为因子,取两点之间在直线方向上的点
plane:: lineCast(vexctor3D &a, vector3D &b)
{
float disa = abs(getDistance(a));
float disb = abs(getDistance(b));
float para = disa/(disa + disb);
return a + para*(b-a);
}
3. 包围盒的检测碰撞
检测在面和轴的方向的投影上是否有重叠,如果有,则是包围盒有碰撞
bool OBB:: intersects(const OBB& box) const
{
float min1, max1, min2, max2;
for(int i =0; i<3; i++) // 当前包围盒的三个面方向,相当于取包围盒的三个坐标轴为分离轴病计算投影做比较
{
getInterval(*this, getFaceDirection(i), min1, max1);
getInterval(box, getFaceDirection(i),min2, max2);
if(max1
使用立方体的六个(right, left, top, bottom, front, back)面 plane, 通过依次判断点与六个plane 的关系(getSide()>0,=0,<0; 三种关系),如果都是在大于0,则点在六个面的中间,即在立方体中。另外,这里也可以使用投影,看点是否在区间的方法判断。
vextor3* triangle = new vector3;
triangle->push_back(v1);
triangle->push_back(v2);
triangle->push_back(v3);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, right);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, left);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, top);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, bottom);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, front);
if(triangle == NULL) continue;
triangle = clipPolygon(triangle, back);
if(triangle == NULL) continue;
addPolygon(triangle, normal);
原来插件中使用的是sprite,根据其本身的坐标,高度,宽度;结合texture 的height,weight。计算差值信息。 这里简化了该项计算,因为没有用到sprite。我这里是使用寻找的的方法,获取顶点原来的UV。如果要对这个操作,可以改变matrix,传入shader 对UV 进行旋转或者缩放平移操作等。
6. 保存顶点
需要注意,顶点需要判断下是否在vector中(逐点计算距离,如果在阈值内就说明是同一个点)已经保存了,如果保存了。只要返回其index即可。如果没有找到,就push_back。
int findVertex(Vector3D vertex, vextor& vertices)
{
for(int i = 0 ; i< vertices.size(); i++)
{
vector3D temp = vertices[i];
float threshold = sqrt(length(vertex, temp));
if(threshold < 0.01f){
return i;
}
}
return -1;
}
附贴图功能展示: 最右边是带alpha 通道的贴图
如下图所示是随立方体移动,纹理的移动