juz*_*ode 6 c++ opengl glsl matrix glm-math
OpenGL 数学库 (GLM) 使用以下算法来计算平移矩阵:
//taken from source code
template<typename T, qualifier Q>
GLM_FUNC_QUALIFIER mat<4, 4, T, Q> translate(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v)
{
mat<4, 4, T, Q> Result(m);
Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
return Result;
}
Run Code Online (Sandbox Code Playgroud)
(这里向量v是 3 维向量,矩阵 m 是 4X4 矩阵,因为我们使用齐次坐标,向量v也是 4 维)。
以下来自线性代数理论:
让m具有条目:
现在,假设矩阵m给出了一些线性变换,并且也是一个变换矩阵,如果我没记错的话,我们想分别在 X、Y 和 Z 维度上添加 X、Y 和 Z 的平移,我们这样做的方法是形成一个复合矩阵:
这给出了类似的东西:
现在,我不明白 Translate 的 GLM 函数的作用,因为它的作用如下:
添加平移变换的矩阵,即 m 变为:
现在,这两个矩阵不相等,因此它们会导致不同的变换,所以我很困惑哪个矩阵进行实际转换,哪个矩阵是正确的,或者算法背后是否隐藏着任何其他想法?
注意:在阅读答案之前,请注意在矩阵的列主表示中,您可以使用以下方式访问矩阵的条目:matrix[column-index][row-index]。
我用来执行转换的源代码:
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <string.h>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
// Window Dimensions
const GLint WIDTH=800, HEIGHT=600;
GLuint VAO, VBO, shader;
GLint uniformModel {};
GLint uniformModelRot {};
GLfloat triOffset {};
float triMaxOffset = 0.7f;
bool direction = true;
const float toRadians = 3.14159265f/180.0f;
// vertex shader
static const char* vShader =
"#version 330\n"
"layout (location = 0) in vec3 pos;\n"
"uniform mat4 model;\n"
"void main(){\n"
" gl_Position = model * vec4(0.5*pos, 1.0);\n"
"}\n";
// fragment shader
static const char* fShader = ""
"#version 330\n"
"out vec4 color;\n"
"uniform mat4 model;\n"
"void main(){\n"
" color = model *vec4(1.0, 1.0, 0.0, 1.0);\n"
"}\n";
void AddShader(GLuint theProgram, const char* ShaderCode, GLenum shaderType, std::string info){
std::cerr <<"INFO: Adding "<<info<<" Shader"<<std::endl;
GLuint theShader = glCreateShader(shaderType);
const GLchar* theCode[1];
theCode[0] = ShaderCode;
GLint codeLength[1];
codeLength[0] = strlen(ShaderCode);
glShaderSource(theShader, 1, theCode, codeLength);
glCompileShader(theShader);
GLint result =0;
GLchar eLog[1024] ={0};
glGetShaderiv(theShader, GL_COMPILE_STATUS, &result);
if(!result){
glGetShaderInfoLog(shader, sizeof(eLog), NULL, eLog);
std::cerr<<"Error compiling program"<<std::endl;
return;
}
glAttachShader(theProgram, theShader);
}
void CompileShader(){
shader = glCreateProgram();
if(!shader){
std::cerr<<"Error creating shader"<<std::endl;
return;
}
AddShader(shader, vShader, GL_VERTEX_SHADER, "vertex");
AddShader(shader, fShader, GL_FRAGMENT_SHADER, "fragment");
GLint result =0;
GLchar eLog[1024] ={0};
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &result);
if(!result){
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
std::cerr<<"Error linking program"<<std::endl;
return;
}
glValidateProgram(shader);
glGetProgramiv(shader, GL_VALIDATE_STATUS, &result);
if(!result){
glGetProgramInfoLog(shader, sizeof(eLog), NULL, eLog);
std::cerr<<"Error Validating program"<<std::endl;
return;
}
uniformModel = glGetUniformLocation(shader,"model");
}
void CreateTriangles(){
GLfloat vertices[]={
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*9,vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
int main(){
//initialize GLFW
if(!glfwInit()){
std::cerr << "GLFW initialization failed!" << std::endl;
glfwTerminate();
return 1;
}
//Setup GLFW window properties
//openGL version
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// core profile = no backward compatibility
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//allow forward compatibility
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow *mainWindow = glfwCreateWindow(WIDTH, HEIGHT, "TEST WINDOW", NULL, NULL);
if(!mainWindow){
std::cerr << "GLFW Window creation failed" << std::endl;
glfwTerminate();
return 1;
}
// get Buffer size information
int bufferWidth, bufferHeight;
glfwGetFramebufferSize(mainWindow, &bufferWidth, &bufferHeight);
// set context for GLEW to use
glfwMakeContextCurrent(mainWindow);
// allow modern extension features
if(glewInit()!=GLEW_OK){
std::cerr << "GLEW initialization failed" << std::endl;
glfwDestroyWindow(mainWindow);
glfwTerminate();
return 1;
}
// setup viewport size
glViewport(0, 0, bufferWidth, bufferHeight);
CreateTriangles();
CompileShader();
while(!glfwWindowShouldClose(mainWindow)){
// get and handle user input events
glfwPollEvents();
glClearColor(1.0f, 0.0f, 0.0f, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
if(direction){
triOffset += 0.05f;
}else{
triOffset -= 0.05f;
}
if(abs(triOffset) >= triMaxOffset){
direction = !direction;
}
glUseProgram(shader);
glm::mat4 modelMatrix(1.0f);
modelMatrix = glm::translate(modelMatrix, glm::vec3(triOffset, 0.0f, 0.0f));
glUniformMatrix4fv(uniformModel, 1, GL_FALSE,glm::value_ptr(modelMatrix));
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES,0,3);
glBindVertexArray(0);
glUseProgram(0);
// swap buffers
glfwSwapBuffers(mainWindow);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
OpenGL 数学 (GLM)基于OpenGL 着色语言 (GLSL)。实际上所做glm::translate的是设置一个平移矩阵并将输入矩阵乘以平移。它m*t按照GLSL 向量和矩阵运算的含义进行计算:
Run Code Online (Sandbox Code Playgroud)mat<4, 4, T, Q> Result(m); Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3];
(以下Result用 代替R)
注意,m[0] * v[0]将列的每个分量乘以m[0]标量v[0]。结果是向量(m[0][0]*v[0], m[0][1]*v[0], m[0][2]*v[0], m[0][3]*v[0])。
所以R[3] = m[0]*v[0] + m[1]*v[1] + m[2]*v[2] + m[3]是一样的
R[3][0] = m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0]
R[3][1] = m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1]
R[3][2] = m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2]
R[3][3] = m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3]
Run Code Online (Sandbox Code Playgroud)
glm::translate实际上计算:
vh = (v[0], v[1], v[2], 1)
R = m
R[3][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), vh )
R[3][1] = dot( (m[0][1], m[1][1], m[2][1], m[3][1]), vh )
R[3][2] = dot( (m[0][2], m[1][2], m[2][2], m[3][2]), vh )
R[3][3] = dot( (m[0][3], m[1][3], m[2][3], m[3][3]), vh )
Run Code Online (Sandbox Code Playgroud)
上面的代码计算 、、 by行的点积。是翻译的第4栏。注意平移矩阵定义为:mvhvhtt
c0 c1 c2 c3
---------------------
r0: 1 0 0 v[0]
r1: 0 1 0 v[1]
r2: 0 0 0 v[2]
r3: 0 0 0 1
Run Code Online (Sandbox Code Playgroud)
4x4 矩阵 ( R = m*t) 的串联是 和的行和列的点积 ,可以表示为:(请参阅OpenGL 着色语言 4.60 规范 - 5.10. 矢量和矩阵运算)mt
for i from 0 to 3
for j fro 0 to 3
R[i][j] = dot( (m[0][j], m[1][j], m[2][j], m[3][j]), t[i] )
Run Code Online (Sandbox Code Playgroud)
其中dot(a, b) == a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3],
(m[0][j], m[1][j], m[2][j], m[3][j])是的第j行m,
t[i]是 的第 i列t。
因为从、和glm::translate复制 、 和 就足够了。R[0]R[1]R[2]m[0]m[1]m[2]
例如对于 ( i=0, j=0):
R[0][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), t[0] )
R[0][0] = dot( (m[0][0], m[1][0], m[2][0], m[3][0]), (1, 0, 0, 0) )
R[0][0] = m[0][0] * 1 + m[1][0] * 0 + m[2][0] * 0 + m[3][0]) * 0
R[0][0] = m[0][0]
Run Code Online (Sandbox Code Playgroud)
GLM矩阵(如 OpenGL 矩阵)按列主序存储。如果您在调试器中研究矩阵,可能会导致混乱。
如果你有矩阵
c0 c1 c2 c3
-------------------
r0: Xx Yx Zx Tx
r1: Xy Yy Zy Ty
r2: Xz Yz Zz Tz
r3: 0 0 0 1
Run Code Online (Sandbox Code Playgroud)
那么 4*4 OpenGL 矩阵的内存图像如下所示:
Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1
Run Code Online (Sandbox Code Playgroud)
如果您在调试器中调查它,它可能看起来像:
[ [ Xx, Xy, Xz, 0 ],
[ Yx, Yy, Yz, 0 ],
[ Zx, Zy, Zz, 0 ],
[ Tx, Ty, Tz, 1 ] ]
Run Code Online (Sandbox Code Playgroud)