Reputation: 23
I'm trying to visualize normals of triangles. I have created a triangle to use as the visual representation of the normal but I'm having trouble aligning it to the normal.
I have tried using glm::lookAt
but the triangle ends up in some weird position and rotation after that. I am able to move the triangle in the right place with glm::translate
though.
Here is my code to create the triangle which is used for the visualization:
// xyz rgb
float vertex_data[] =
{
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,
0.25f, 0.0f, 0.025f, 0.0f, 1.0f, 1.0f,
0.25f, 0.0f, -0.025f, 0.0f, 1.0f, 1.0f,
};
unsigned int index_data[] = {0, 1, 2};
glGenVertexArrays(1, &nrmGizmoVAO);
glGenBuffers(1, &nrmGizmoVBO);
glGenBuffers(1, &nrmGizmoEBO);
glBindVertexArray(nrmGizmoVAO);
glBindBuffer(GL_ARRAY_BUFFER, nmrGizmoVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, nrmGizmoEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_data), index_data, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
and here is the code to draw the visualizations:
for(unsigned int i = 0; i < worldTriangles->size(); i++)
{
Triangle *tri = &worldTriangles->at(i);
glm::vec3 wp = tri->worldPosition;
glm::vec3 nrm = tri->normal;
nrmGizmoMatrix = glm::mat4(1.0f);
//nrmGizmoMatrix = glm::translate(nrmGizmoMatrix, wp);
nrmGizmoMatrix = glm::lookAt(wp, wp + nrm, glm::vec3(0.0f, 1.0f, 0.0f));
gizmoShader.setMatrix(projectionMatrix, viewMatrix, nrmGizmoMatrix);
glBindVertexArray(nrmGizmoVAO);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
When using only glm::translate
, the triangles appear in right positions but all point in the same direction. How can I rotate them so that they point in the direction of the normal vector?
Upvotes: 2
Views: 409
Reputation: 72479
Your code doesn't work because lookAt
is intended to be used as the view matrix, thus it returns the transform from world space to local (camera) space. In your case you want the reverse -- from local (triangle) to world space. Taking an inverse
of lookAt
should solve that.
However, I'd take a step back and look at (haha) the bigger picture. What I notice about your approach:
I'd instead generate all the vertices for the normals in a single array, and then use glDrawArrays
to draw that. For the actual calculation, observe that we have one degree of freedom when it comes to aligning the triangle along the normal. Your lookAt
code resolves that DoF rather arbitrary. A better way to resolve that is to constrain it by requiring that it faces towards the camera, thus maximizing the visible area. The calculation is straightforward:
// inputs: vertices output array, normal position, normal direction, camera position
void emit_normal(std::vector<vec3> &v, const vec3 &p, const vec3 &n, const vec3 &c) {
static const float length = 0.25f, width = 0.025f;
vec3 t = normalize(cross(n, c - p)); // tangent
v.push_back(p);
v.push_back(p + length*n + width*t);
v.push_back(p + length*n - width*t);
}
// ... in your code, generate normals through:
std::vector<vec3> normals;
for(unsigned int i = 0; i < worldTriangles->size(); i++) {
Triangle *tri = &worldTriangles->at(i);
emit_normal(normals, tri->worldPosition, tri->normal, camera_position);
}
// ... create VAO for normals ...
glDrawArrays(GL_TRIANGLES, 0, normals.size());
Note, however, that this would make the normal mesh camera-dependent -- which is desirable when rendering normals with triangles. Most CAD software draws normals with lines instead, which is much simpler and avoids many problems:
void emit_normal(std::vector<vec3> &v, const vec3 &p, const vec3 &n) {
static const float length = 0.25f;
v.push_back(p);
v.push_back(p + length*n);
}
// ... in your code, generate normals through:
std::vector<vec3> normals;
for(unsigned int i = 0; i < worldTriangles->size(); i++) {
Triangle *tri = &worldTriangles->at(i);
emit_normal(normals, tri->worldPosition, tri->normal);
}
// ... create VAO for normals ...
glDrawArrays(GL_LINES, 0, normals.size());
Upvotes: 2