贴花编辑器实现细节

前言

之前写过一篇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

4. 判断点是否在立方体中

 使用立方体的六个(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);

5. 计算UV 坐标

     原来插件中使用的是sprite,根据其本身的坐标,高度,宽度;结合texture 的height,weight。计算差值信息。 这里简化了该项计算,因为没有用到sprite。我这里是使用寻找的的方法,获取顶点原来的UV。如果要对这个操作,可以改变matrix,传入shader 对UV 进行旋转或者缩放平移操作等。

贴花编辑器实现细节_第1张图片


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 通道的贴图

贴花编辑器实现细节_第2张图片

    如下图所示是随立方体移动,纹理的移动

贴花编辑器实现细节_第3张图片


你可能感兴趣的:(Computer,Graphics)