【发布时间】:2014-03-19 07:23:13
【问题描述】:
我正在编写的程序接收 3D 网格的顶点数据,执行一系列计算(请原谅含糊不清,稍后我将尝试更详细地解释),并输出一个二进制文件,该文件定义了边缘在网格上。然后我的程序在边缘所在的位置绘制一条彩色线。如果没有适当的顶点着色器,这看起来就像一个常规的三角网格,但是一旦应用了适当的顶点着色器,只有“锐利”的边缘(它们的法线的点积大于接近于零的值)才会绘制线条在它们上,以及图形外部的边缘。我对轮廓的实现是不正确的,因为我假设如果边缘不在边缘后面,并且没有定义锐边,它将是轮廓边缘。我还没有在其他地方找到令人满意的答案,我不想依赖于将网格重新绘制为纯色并将其渲染为比原始网格稍大的旧技巧。这种方法完全基于数学,仅依赖于网格的顶点数据。我正在编写一个使用以下顶点着色器的程序:
uniform mat4 worldMatrix;
uniform mat4 projMatrix;
uniform mat4 viewProjMatrix;
uniform vec4 eyepos;
attribute vec3 a;
attribute vec3 b;
attribute vec3 n1;
attribute vec3 n2;
attribute float w;
void main()
{
float a_vertex = dot(eyepos.xyz - a, n1);
float b_vertex = dot(eyepos.xyz - a, n2);
if (a_vertex * b_vertex > 0.0) // signs are different, edge is behind the object
{
gl_Position = vec4(2.0,2.0,2.0,1.0);
}
else // the outline of the figure
{
if(w == 0.0)
{
vec4 p = vec4(a.x, a.y, a.z, 1.0);
p = p * worldMatrix * viewProjMatrix;
gl_Position = p;
}
else
{
vec4 p = vec4(b.x, b.y, b.z, 1.0);
p = p * worldMatrix * viewProjMatrix;
gl_Position = p;
}
}
if(dot(n1, n2) <= 0.2) // there is a sharp edge
{
if(w == 0.0)
{
vec4 p = vec4(a.x, a.y, a.z, 1.0);
p = p * worldMatrix * viewProjMatrix;
gl_Position = p;
}
else
{
vec4 p = vec4(b.x, b.y, b.z, 1.0);
p = p * worldMatrix * viewProjMatrix;
gl_Position = p;
}
}
}
...从使用此程序在 C++ 中编写的二进制文件中获取信息:
#include <iostream>
#include "llgl.h"
#include <fstream>
#include <vector>
#include "SuperMesh.h"
using namespace std;
using namespace llgl;
struct Vertex
{
float x,y,z,w;
float s,t,p,q;
float nx,ny,nz,nw;
};
bool isFileAlright(string fName)
{
ifstream in(fName.c_str());
if(!in.good())
return false;
return true;
}
int main(int argc, char* argv[])
{
// INPUT FILE NAME //
string fName;
cout << "Enter the path to your spec.mesh file here: ";
cin >> fName;
while(!isFileAlright(fName))
{
cout << "Enter the path to your spec.mesh file here: ";
cin >> fName;
}
SuperMesh* Model = new SuperMesh(fName.c_str());
// END INPUT //
Model->load();
Model->draw();
string fname = Model->fname;
string FileName = fname.substr(0, fname.size() - 10); // supposed to slash the last 10 characters off of the string, removing ".spec.mesh"...
FileName = FileName + ".bin"; //... and then we make it a .bin file*/
cout << FileName << endl;
ofstream out(FileName.c_str(), ios::binary);
for (unsigned w = 0; w < Model->m.size(); w++)
{
vector<float> &vdata = Model->m[w]->vdata;
vector<char> &idata = Model->m[w]->idata;
//Create a vertex and index variable, a map for Edge Mesh, perform two loops to analyze all triangles on a mesh and write out their vertex values to a file.//
Vertex* V = (Vertex*)(&vdata[0]);
unsigned short* I16 = (unsigned short*)(&idata[0]);
unsigned char* I8 = (unsigned char*)(&idata[0]);
unsigned int* I32 = (unsigned int*)(&idata[0]);
map<set<int>, vector<vec3> > EM;
for(unsigned i = 0; i < Model->m[w]->ic; i += 3) // 3 because we're looking at triangles //
{
Mesh* foo = Model->m[w];
int i1;
int i2;
int i3;
if( Model->m[w]->ise == GL_UNSIGNED_BYTE)
{
i1 = I8[i];
i2 = I8[i + 1];
i3 = I8[i + 2];
}
else if( Model->m[w]->ise == GL_UNSIGNED_SHORT)
{
i1 = I16[i];
i2 = I16[i + 1];
i3 = I16[i + 2];
}
else
{
i1 = I32[i];
i2 = I32[i + 1];
i3 = I32[i + 2];
}
vec3 p = vec3(V[i1].x, V[i1].y, V[i1].z); // to represent the point in 3D space of each vertex on every triangle on the mesh
vec3 q = vec3(V[i2].x, V[i2].y, V[i2].z);
vec3 r = vec3(V[i3].x, V[i3].y, V[i3].z);
vec3 v1 = p - q;
vec3 v2 = r - q;
vec3 n = cross(v2,v1); //important to make sure the order is correct here, do VERTEX TWO dot VERTEX ONE//
set<int> tmp;
tmp.insert(i1); tmp.insert(i2);
EM[tmp].push_back(n);
set<int> tmp2;
tmp2.insert(i2); tmp2.insert(i3);
EM[tmp2].push_back(n);
set<int> tmp3;
tmp3.insert(i3); tmp3.insert(i1);
EM[tmp3].push_back(n);
//we have now pushed every needed point into our edge map
}
int edgeNumber = 0;
cout << "There should be 12 edges on a lousy cube." << endl;
for(map<set<int>, vector<vec3> >::iterator it = EM.begin(); it != EM.end(); ++it)
{
//Now we will take our edge map and write its data to the file!//
/* Information is written to the file in this form:
Vertex One, Vertex Two, Normal One, Normal Two, r (where r, depending on its value, determines whether one edge is on top of the other in the case
where two edges are aligned with one another)
*/
set<int>::iterator tmp = it->first.begin();
int pi = *tmp;
tmp++;
int qi = *tmp;
Vertex One = V[pi];
Vertex Two = V[qi];
vec3 norm1 = it->second[0];
vec3 norm2;
if(it->second.size() == 1)
norm2 = -1 * norm1;
else
norm2 = it->second[1];
out.write((char*) &One, 12);
out.write((char*) &Two, 12);
out.write((char*) &norm1, 12);
out.write((char*) &norm2, 12);
float r = 0;
out.write((char*) &r, 4);
out.write((char*) &One, 12);
out.write((char*) &Two, 12);
out.write((char*) &norm1, 12);
out.write((char*) &norm2, 12);
r = 1;
out.write((char*) &r, 4);
edgeNumber++;
cout << "Wrote edge #" << edgeNumber << endl;
}
}
return 0;
}
这个程序的问题是它在测试用例中没有做这两个基本的事情,我用它来绘制一个带有轮廓的简单框:
它不绘制轮廓。顶点着色器不足以确定物体边缘的位置。实现这一点的二进制文件使用上面发布的第二个 sn-p 中的代码在单独的程序中预先计算,然后将其与所属的网格资产一起保存为 .bin 文件。然而,原始顶点数据只能让我走这么远,我想找一种方法在网格外部画一条线,而不使用更传统的方法。
它不会绘制我需要的所有边缘。在我的测试用例中,缺少两条边,我终其一生都无法弄清楚原因。我想我一定是在写边缘图时做错了。
关于上述代码的几点说明:
llgl 是一个 OpenGL 包装器,我用它来简化 OpenGL 的许多元素。它在这里没有广泛使用,而是用于创建网格,在其他地方完成。
像 Mesh 和 SuperMesh(将网格集合到一个刚体中)之类的东西在我的场景中是 3D 对象。在我的测试用例中,我的场景中只有一个 Mesh,定义单个 Mesh 的 SuperMesh 本质上只是创建单个 Mesh。
第二个 sn-p 中的“draw”调用预先计算了 Mesh 的边缘图,实际上并没有绘制任何东西。有必要访问 Mesh 的顶点数据。
变量“ise”取自 SuperMesh 中的各个网格,是从原始 Blender .OBJ 文件中读取的变量。它与应该使用多少内存来存储重要的顶点数据有关。使用 Blender 的朋友和导师告诉我,分配超出这些值所需的空间通常不是一个好主意。
没有很好的评论,因为我不是唯一一个处理过这段代码的人,不幸的是,我对第二个 sn-p 如何遍历所有三角形的理解有限一个网格,不知何故错过了最后两条边。一旦我更好地理解了这段代码在正确编写时应该做什么,我计划大量评论它并在未来的应用程序中使用它。
【问题讨论】: