将矩阵与 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。取 lookAt
的 inverse
应该可以解决这个问题。
不过,我会退后一步,看看(哈哈)大局。我注意到你的方法:
- 这是非常低效的——你用不同的模型矩阵单独调用 为每个法线。
- 您甚至不需要整个模型矩阵。三角形是二维形状,因此您只需要两个基向量。
我会在一个数组中生成法线的所有顶点,然后使用 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());
我正在尝试可视化三角形的法线。 我创建了一个三角形用作法线的视觉表示,但我无法将其与法线对齐。
我试过使用 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。取 lookAt
的 inverse
应该可以解决这个问题。
不过,我会退后一步,看看(哈哈)大局。我注意到你的方法:
- 这是非常低效的——你用不同的模型矩阵单独调用 为每个法线。
- 您甚至不需要整个模型矩阵。三角形是二维形状,因此您只需要两个基向量。
我会在一个数组中生成法线的所有顶点,然后使用 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());