[英]Creating a transformation matrix for OpenGL with GLM (Rotations)
I have a unit cube that I would like to transform such that it connects two points.我有一个单位立方体,我想对其进行转换以连接两点。 I am new to OpenGL and only know the most basic parts of linear algebra.我是OpenGL的新手,只知道线性代数最基础的部分。 I have attempted to mimic something similar to polar coordinates in my endeavor to connect the dots.在我努力连接点的过程中,我试图模仿类似于极坐标的东西。 My current implementation does not work when there is a change in Z and another axis.当 Z 和另一个轴发生变化时,我当前的实现不起作用。 I also tried mat = glm::lookAt(center, terminal, y_axis);
我也试过mat = glm::lookAt(center, terminal, y_axis);
, but I did not have success. ,但我没有成功。
This comes from the body of a for loop located in schedule_edge_update()
.这来自位于schedule_edge_update()
中的 for 循环的主体。
auto const initial = p1;
auto const terminal = p2;
auto const distance = glm::distance(initial, terminal);
auto const length = distance * 0.5f;
auto const center = (initial + terminal) / 2.f;
auto const rejection = terminal - initial;
auto const delta = glm::normalize(rejection);
auto mat = glm::mat4(1);
// translate
mat = glm::translate(mat, center);
// rotate
auto const phi_hyp = glm::length(glm::vec2(delta.x, delta.z));
if (phi_hyp != 0.0f) {
auto phi = acosf(delta.x / phi_hyp);
mat = glm::rotate(mat, phi, y_axis);
}
auto const theta_hyp = glm::length(glm::vec2(delta.x, delta.y));
if (theta_hyp != 0.0f) {
auto theta = acosf(delta.x / theta_hyp);
theta *= delta.x > 0 ? -1.0f : 1.0f;
mat = glm::rotate(mat, theta, z_axis);
}
// scale
edges->add_matrix(glm::scale(mat, glm::vec3(length, 0.05f, 0.01f)));
When a matrix is added to edges
it is queued to be buffered for instanced rendering.当一个矩阵被添加到edges
时,它会排队等待缓冲以进行实例化渲染。
Here are my testing points and a big cube I made.这是我的测试点和我制作的一个大立方体。
Here is an example of it not working.这是它不起作用的示例。 The initial point is labeled p1 and the terminal point p2.起点标记为 p1,终点标记为 p2。 The line that isn't connecting any points should be connecting p1 and p2.不连接任何点的线应该连接 p1 和 p2。
Here is another example, but this one has the coordinates for p1 and p2 labeled.这是另一个示例,但是这个示例标记了 p1 和 p2 的坐标。 p1 and p2 differ by a change in Y and Z. However, my code rotates the cube (after translating it) around the y axis 90 degrees. p1 和 p2 的区别在于 Y 和 Z 的变化。但是,我的代码将立方体(在平移之后)围绕 y 轴旋转 90 度。 Then is scales it.然后是缩放它。 You can tell it is rotated because it is wider on one of the axis (the y-axis before rotation).您可以看出它已旋转,因为它在其中一个轴(旋转前的 y 轴)上更宽。
// Test points
auto const A = glm::vec3(-10.0f, -10.0f, -20.0f);
auto const B = glm::vec3(+10.0f, -10.0f, -20.0f);
auto const C = glm::vec3(+10.0f, +10.0f, -20.0f);
auto const D = glm::vec3(+00.0f, +10.0f, -20.0f);
auto const E = glm::vec3(+05.0f, +05.0f, -20.0f);
auto const F = glm::vec3(+00.0f, +00.0f, -30.0f);
auto const G = glm::vec3(-10.0f, -10.0f, -30.0f);
auto const H = glm::vec3(+55.0f, -15.0f, -60.0f);
auto const I = glm::vec3(+55.0f, -05.0f, -70.0f);
get_nodes().emplace_back(A);
get_nodes().emplace_back(B);
get_nodes().emplace_back(C);
get_nodes().emplace_back(D);
get_nodes().emplace_back(E);
get_nodes().emplace_back(F);
get_nodes().emplace_back(G);
get_nodes().emplace_back(H);
get_nodes().emplace_back(I);
get_edges().emplace_back(A, B);
get_edges().emplace_back(B, C);
get_edges().emplace_back(C, D);
get_edges().emplace_back(D, E);
get_edges().emplace_back(E, F);
get_edges().emplace_back(F, G);
get_edges().emplace_back(G, H);
get_edges().emplace_back(H, I);
// Big cube
auto const C0 = glm::vec3(-5.0f, -5.0f, -5.0f);
auto const C1 = glm::vec3(-5.0f, -5.0f, +5.0f);
auto const C2 = glm::vec3(-5.0f, +5.0f, -5.0f);
auto const C3 = glm::vec3(-5.0f, +5.0f, +5.0f);
auto const C4 = glm::vec3(+5.0f, -5.0f, -5.0f);
auto const C5 = glm::vec3(+5.0f, -5.0f, +5.0f);
auto const C6 = glm::vec3(+5.0f, +5.0f, -5.0f);
auto const C7 = glm::vec3(+5.0f, +5.0f, +5.0f);
get_nodes().emplace_back(C0);
get_nodes().emplace_back(C1);
get_nodes().emplace_back(C2);
get_nodes().emplace_back(C3);
get_nodes().emplace_back(C4);
get_nodes().emplace_back(C5);
get_nodes().emplace_back(C6);
get_nodes().emplace_back(C7);
get_edges().emplace_back(C0, C1);
get_edges().emplace_back(C0, C2);
get_edges().emplace_back(C0, C4);
get_edges().emplace_back(C1, C3);
get_edges().emplace_back(C1, C5);
get_edges().emplace_back(C2, C3);
get_edges().emplace_back(C2, C6);
get_edges().emplace_back(C3, C7);
get_edges().emplace_back(C4, C5);
get_edges().emplace_back(C4, C6);
get_edges().emplace_back(C5, C7);
get_edges().emplace_back(C6, C7);
schedule_node_update();
schedule_edge_update();
auto constexpr A = vec3(-0.5f, 0.0f, 0.0f);
auto constexpr B = vec3(+0.5f, 0.0f, 0.0f);
auto const C = p1;
auto const D = p2;
auto M = mat4(1.0f);
// Translate
auto const center = 0.5 * (C + D);
M = translate(M, center);
// Rotate
auto constexpr p = B - A;
auto const q = D - C;
auto const n = cross(p, q);
if (n != vec3()) {
auto const a = angle(normalize(p), normalize(q));
M = rotate(M, a, n);
}
// Scale
auto constexpr thickness = 0.05f;
M = scale(M, vec3(0.5f * distance(C, D), thickness, thickness));
edges->add_matrix(M);
So the problem boils down to this:所以问题归结为:
I know 4 points A,B,C,D
and I want to compute transform matrix that will convert A,B
into C,D
.我知道 4 点A,B,C,D
并且我想计算将A,B
转换为C,D
的变换矩阵。
This can be done like this.这可以这样做。 Let assume we convert points like this:假设我们像这样转换点:
M * A = C
M * B = D
Where M
is out transform matrix we want to compute.其中M
是我们要计算的输出变换矩阵。 There are infinite number of possible solutions (as the line AB
can have any rotation on its own axis)有无限多种可能的解决方案(因为线AB
可以在其自己的轴上进行任何旋转)
If you dissect the M a bit its just a matter of knowing position, orientation and scale.如果你稍微剖析一下 M,它只是了解 position、方向和比例的问题。
Scale is the simplest规模是最简单的
its just the ratio of the line length after and before transformation.它只是转换前后线长的比率。
scale = |CD|/|AB|
orientation方向
its represented by unit basis vectors.它由单位基向量表示。 We can exploit the fact that the AB and CD has just single rotation (all others just produce the infinite number of solutions) so we can just rotate AB
by the angle between AB
, CD
around axis perpendicular to both AB
, CD
.我们可以利用 AB 和 CD 只有单一旋转(所有其他只产生无限数量的解决方案)这一事实,因此我们可以围绕垂直于AB
和CD
的轴将AB
旋转AB
和CD
之间的角度。 The angle we can obtain by acos of dot product between unit vectors parallel to AB
, CD
.我们可以通过平行于AB
, CD
的单位向量之间的点积 acos 获得的角度。 The only problem is that will not give us direction of rotation so we need to test the two possibilities (CW,CCW).唯一的问题是它不会给我们旋转方向,所以我们需要测试两种可能性(CW,CCW)。
so:所以:
axis = cross(BA,DC) angle = +/- acos(dot(BA,DC) / |BA|*|DC|)
translation翻译
this one is simple we just transform A
with M
without translation lets call it A'
and then just correct the resulting position so it goes to C
.这个很简单,我们只需将A
转换为M
而不进行翻译,我们将其称为A'
,然后只需更正结果 position 即可变为C
。
M_origin += C-A'
Beware that the translation should be set directly, not applying translation matrix.请注意,应直接设置翻译,而不是应用翻译矩阵。 Those usually translate in local coordinate system [LCS]
which involves converting the difference to it first.那些通常在局部坐标系[LCS]
中转换,这涉及首先将差异转换为它。 In such case use在这种情况下使用
translate(Inverse(M)*(C-A'))
or要么
translate(M*(C-A'))
depending on used notations.取决于使用的符号。
Here small C++/VCL/old GL example:这里是小的 C++/VCL/旧 GL示例:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
#include "OpenGLrep4d_double.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double arot=0.0; // just animation angle
//---------------------------------------------------------------------------
const int pnts=8;
double pnt[pnts*3]= // Vertexes for 10x10x10 cube centered at (0,0,0)
{
-5.0,-5.0,-5.0,
-5.0,+5.0,-5.0,
+5.0,+5.0,-5.0,
+5.0,-5.0,-5.0,
-5.0,-5.0,+5.0,
-5.0,+5.0,+5.0,
+5.0,+5.0,+5.0,
+5.0,-5.0,+5.0,
};
const int lins=12;
int lin[lins*2]= // lines (index of point used) no winding rule
{
0,1,1,2,2,3,3,0,
4,5,5,6,6,7,7,4,
0,4,1,5,2,6,3,7,
};
double A[3]={-5.0,-5.0,-5.0}; // cube diagonal
double B[3]={+5.0,+5.0,+5.0};
double C[3]={-4.5, 2.0, 0.0}; // wanted cube diagonal
double D[3]={+4.5, 5.0, 0.0};
double M[16]; // our transform matrix
//---------------------------------------------------------------------------
void compute_M()
{
double scale,p[3],q[3],n[3],a;
const double deg=180.0/M_PI;
const double rad=M_PI/180.0;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// scale
vector_sub(p,B,A); // p=B-A
vector_sub(q,D,C); // q=D-C
scale=vector_len(q)/vector_len(p); // =|q|/|p|
// rotation between AB and CD
vector_mul(n,p,q); // n = (p x q) ... cross product
vector_one(p,p); // p = p/|p|
vector_one(q,q); // q = q/|q|
a=acos(vector_mul(p,q)); // angle between AB and CD in [rad]
glLoadIdentity(); // unit matrix
glRotated(+a*deg,n[0],n[1],n[2]); // rotate by angle around normal to AB,CD
glScaled(scale,scale,scale); // apply scale
glGetDoublev(GL_MODELVIEW_MATRIX,M); // get the M from OpenGL
// translation
matrix_mul_vector(p,M,A); // p = M*A
vector_sub(p,C,p); // p = C-p
M[12]=p[0];
M[13]=p[1];
M[14]=p[2];
M[15]=1.0;
// verify
matrix_mul_vector(p,M,B); // p = M*B
vector_sub(p,p,D); // p = p-C
if (vector_len(p)>1e-3) // if |p| too big use other direction to rotate
{
glLoadIdentity(); // unit matrix
glRotated(-a*deg,n[0],n[1],n[2]); // rotate by angle around normal to AB,CD
glScaled(scale,scale,scale); // apply scale
glGetDoublev(GL_MODELVIEW_MATRIX,M); // get the M from OpenGL
}
glPopMatrix();
}
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
int i;
double m0[16],m1[16],m[16],x[3],y[3],z[3],t2[3][3];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0.0,0.0,-50.0);
glRotated(15.0,1.0,0.0,0.0);
glRotated(arot,0.0,1.0,0.0);
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); for (i=0;i<lins*2;i++) glVertex3dv(pnt+(lin[i]*3)); // render original cube
glColor3f(0.0,1.0,0.0); glVertex3dv(A); glVertex3dv(B); // render original diagonal AB
glColor3f(1.0,1.0,0.0); glVertex3dv(C); glVertex3dv(D); // render wanted diagonal CD
glEnd();
// render transformed cube
glMatrixMode(GL_MODELVIEW);
glMultMatrixd(M);
glBegin(GL_LINES);
glColor3f(0.0,0.0,1.0); for (i=0;i<lins*2;i++) glVertex3dv(pnt+(lin[i]*3)); // render transformed cube
glEnd();
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
compute_M();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// window repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
arot+=1.5; if (arot>=360.0) arot-=360.0;
gl_draw();
}
//---------------------------------------------------------------------------
Just ignore the VCL related stuff.忽略 VCL 相关的东西。 The GL support functions you can find in here:您可以在此处找到 GL 支持功能:
The only important stuff here is the compute_M()
along with the global variables.这里唯一重要的东西是compute_M()
和全局变量。
The vector math functions are commented (so you can translate that to GLM) if you need implementations you can find those in the linked QA above.矢量数学函数被注释(因此您可以将其转换为 GLM)如果您需要实现,您可以在上面的链接 QA 中找到它们。 It basically takes.基本上需要。 For simplicity I used GL native rotations (beware they are in degrees instead of radians).为简单起见,我使用了 GL 原生旋转(注意它们是度数而不是弧度)。
Here preview:此处预览:
red
is original cube red
是原始立方体green
is original diagonal AB
green
是原来的对角线AB
blue
is transformed cube by M
blue
是由M
变换的立方体yellow
is wanted diagonal CD
yellow
是通缉对角CD
As you can see it matches.如您所见,它匹配。
In case you need to align more than just a line you need add more info for aligning (2 lines (3 points) for example) etc. For more info see:如果您需要对齐的不仅仅是一条线,您需要添加更多对齐信息(例如 2 条线(3 点))等。有关更多信息,请参见:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.