将矩阵与 OpenGL 中的向量对齐

Align a matrix to a vector in OpenGL

我正在尝试可视化三角形的法线。 我创建了一个三角形用作法线的视觉表示,但我无法将其与法线对齐。

我试过使用 glm::lookAt 但三角形最终出现在一些奇怪的位置和旋转之后。不过,我可以使用 glm::translate 将三角形移动到正确的位置。

这是我创建用于可视化的三角形的代码:

// 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);

下面是绘制可视化效果的代码:

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);
}

仅使用 glm::translate 时,三角形出现在正确的位置,但都指向同一方向。如何旋转它们使它们指向法向量的方向?

您的代码不起作用,因为 lookAt 旨在用作视图矩阵,因此它 returns 从世界 space 到本地(相机)space。在您的情况下,您想要相反的情况——从本地(三角形)到世界 space。取 lookAtinverse 应该可以解决这个问题。


不过,我会退后一步,看看(哈哈)大局。我注意到你的方法:

  • 这是非常低效的——你用不同的模型矩阵单独调用 为每个法线
  • 您甚至不需要整个模型矩阵。三角形是二维形状,因此您只需要两个基向量。

我会在一个数组中生成法线的所有顶点,然后使用 glDrawArrays 绘制它。对于实际计算,观察我们在沿着法线对齐三角形时有一个自由度。您的 lookAt 代码解决了 DoF 相当随意的问题。解决这个问题的更好方法是通过要求它面向相机来限制它,从而最大化可见区域。计算很简单:

// 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());

但是请注意,这会使法线网格与相机相关——这在渲染带有三角形的法线时是可取的。大多数CAD软件都是用线来绘制法线,这样更简单,也避免了很多问题:

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());