我正在尝试使用 OpenGL 制作一些 3D 图形,并使用 cglm 进行数学计算。
我遵循的指南引导我将顶点位置乘以 3 个矩阵:model、view 和 projection。
由于教程和我的语言不同,我无法一一遵循,所以我用我能找到的方式做了投影矩阵,用cglm
将顶点乘以这些矩阵给了我一个黑屏(对象未渲染),我注意到它只是由于投影矩阵而发生。
我检查了我的矩阵值,它们与我要求的一个投影矩阵匹配ChatGPT,但不起作用,所以我自己做了一个而不是使用cglm,并且它起作用了(有一些失真,但有效)。
我想知道为什么 cglm 失败了,因为我宁愿使用他们的。
这是一个重要的代码片段(C):
mat4 model, view, proj;
glm_mat4_identity(model);
glm_mat4_identity(proj);
glm_rotate(model, PI4, (vec3) { 1, 0, 0 });
// glm_perspective(cam.fov, (f32) cam.width / cam.height, cam.near, cam.far, proj);
proj[0][0] = PI4;
proj[1][1] = PI4;
proj[2][2] = (cam.far + cam.near) / (cam.near - cam.far);
proj[2][3] = -1;
proj[3][2] = 2 * cam.far * cam.near / (cam.near - cam.far);
glUniformMatrix4fv(UNI(shader_program, "MODEL"), 1, GL_FALSE, (const f32*) { model[0] });
glUniformMatrix4fv(UNI(shader_program, "PROJ"), 1, GL_FALSE, (const f32*) { proj[0] });
while (!glfwWindowShouldClose(canvas.window)) {
glm_mat4_identity(view);
glm_translate(view, (vec3) { -cam.pos[0], -cam.pos[1], -cam.pos[2] });
glUniformMatrix4fv(UNI(shader_program, "VIEW"), 1, GL_FALSE, (const f32*) { view[0] });
canvas_clear((RGBA) { 0, 0, 0, 1 });
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(canvas.window);
glfwPollEvents();
handle_inputs(canvas.window);
}
完整代码(C):
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <cglm/cglm.h>
#include <stdio.h>
#include "shader.h"
#include "misc.h"
#define ASSERT(x, str) if (!(x)) {printf(str); exit(1);}
#define UNI(shader, name) (glGetUniformLocation(shader, name))
#define PI 3.14159
#define PI2 PI / 2
#define PI4 PI / 4
#define TAU PI * 2
#define VERTEX_SHADER "shd/vertex.glsl"
#define FRAGMENT_SHADER "shd/fragment.glsl"
#define TEXTURE_0 "img/bills.ppm"
#define TEXTURE_1 "img/ilu.ppm"
#define WIDTH 800
#define HEIGHT 800
#define MOVEMENT_SPEED 0.01
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef float f32;
typedef double f64;
// --- Type
typedef struct { f32 R, G, B, A; } RGBA;
typedef struct {
i16 width, height;
f32 fov, near, far;
f32 pos[3];
f32 ang[2];
} Camera;
typedef struct {
GLFWwindow* window;
i16 width, height;
} Canvas;
u32 canvas_create_texture(GLenum unit, char path[], u32 shader, char uniform_name[], i32 uniform_value);
void canvas_config_texture(GLenum wrap_s, GLenum wrap_t, GLenum min_filter, GLenum mag_filter);
void key_callback(GLFWwindow* window, i32 key, i32 scancode, i32 action, i32 mods);
void handle_inputs(GLFWwindow* window);
void canvas_init(Canvas* canvas);
void canvas_clear(RGBA color);
u32 canvas_create_VBO();
u32 canvas_create_VAO();
u32 canvas_create_EBO();
// --- Setup
u8 wireframe_mode;
Canvas canvas = { NULL, WIDTH, HEIGHT };
Camera cam = { WIDTH, HEIGHT, PI4, 0.1, 100, { 0, 0, 0 }, { 0, 0 } };
f32 vertices[] = {
+0.00, +0.45, 0, 0.5, 0.0,
-0.50, -0.50, 0, 1.0, 1.0,
+0.50, -0.50, 0, 0.0, 1.0,
};
// --- Main
i8 main() {
canvas_init(&canvas);
glfwSetKeyCallback(canvas.window, key_callback);
u32 VBO = canvas_create_VBO();
u32 VAO = canvas_create_VAO();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void*) 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void*) (sizeof(f32) * 3));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
u32 shader_program = shader_create_program(VERTEX_SHADER, FRAGMENT_SHADER);
canvas_config_texture(GL_MIRRORED_REPEAT, GL_MIRRORED_REPEAT, GL_NEAREST, GL_NEAREST);
canvas_create_texture(GL_TEXTURE0, TEXTURE_0, shader_program, "TEXTURE_0", 0);
canvas_create_texture(GL_TEXTURE1, TEXTURE_1, shader_program, "TEXTURE_1", 1);
mat4 model, view, proj;
glm_mat4_identity(model);
glm_mat4_identity(proj);
glm_rotate(model, PI4, (vec3) { 1, 0, 0 });
// glm_perspective(cam.fov, (f32) cam.width / cam.height, cam.near, cam.far, proj);
proj[0][0] = PI4;
proj[1][1] = PI4;
proj[2][2] = (cam.far + cam.near) / (cam.near - cam.far);
proj[2][3] = -1;
proj[3][2] = 2 * cam.far * cam.near / (cam.near - cam.far);
glUniformMatrix4fv(UNI(shader_program, "MODEL"), 1, GL_FALSE, (const f32*) { model[0] });
glUniformMatrix4fv(UNI(shader_program, "PROJ"), 1, GL_FALSE, (const f32*) { proj[0] });
while (!glfwWindowShouldClose(canvas.window)) {
glm_mat4_identity(view);
glm_translate(view, (vec3) { -cam.pos[0], -cam.pos[1], -cam.pos[2] });
glUniformMatrix4fv(UNI(shader_program, "VIEW"), 1, GL_FALSE, (const f32*) { view[0] });
canvas_clear((RGBA) { 0, 0, 0, 1 });
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(canvas.window);
glfwPollEvents();
handle_inputs(canvas.window);
}
glfwTerminate();
return 0;
}
// --- Function
void canvas_init(Canvas* canvas) {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
canvas->window = glfwCreateWindow(canvas->width, canvas->height, "Coordinate System", NULL, NULL);
ASSERT(canvas->window, "Failed creating a window");
glfwMakeContextCurrent(canvas->window);
ASSERT(gladLoadGLLoader((GLADloadproc) glfwGetProcAddress), "Failed loading glad");
glViewport(0, 0, WIDTH, HEIGHT);
}
void canvas_clear(RGBA color) {
glClearColor(color.R, color.G, color.B, color.A);
glClear(GL_COLOR_BUFFER_BIT);
}
void canvas_config_texture(GLenum wrap_s, GLenum wrap_t, GLenum min_filter, GLenum mag_filter) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
}
u32 canvas_create_texture(GLenum unit, char path[], u32 shader, char uniform_name[], i32 uniform_value) {
u32 texture_ID;
glGenTextures(1, &texture_ID);
glActiveTexture(unit);
glBindTexture(GL_TEXTURE_2D, texture_ID);
u16 width, height;
get_image_size(path, &width, &height);
f32* image_buffer = malloc(sizeof(f32) * width * height * 3);
load_image(path, image_buffer, width * height);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, image_buffer);
glGenerateMipmap(GL_TEXTURE_2D);
glUniform1i(UNI(shader, uniform_name), uniform_value);
free(image_buffer);
return texture_ID;
}
void key_callback(GLFWwindow* window, i32 key, i32 scancode, i32 action, i32 mods) {
if (action != GLFW_PRESS) return;
switch (key) {
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(canvas.window, 1);
break;
case GLFW_KEY_M:
if (wireframe_mode) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
else glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
wireframe_mode = 1 - wireframe_mode;
}
}
void handle_inputs(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
cam.pos[2] += MOVEMENT_SPEED;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
cam.pos[2] -= MOVEMENT_SPEED;
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
cam.pos[0] -= MOVEMENT_SPEED;
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
cam.pos[0] += MOVEMENT_SPEED;
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
cam.pos[1] += MOVEMENT_SPEED;
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
cam.pos[1] -= MOVEMENT_SPEED;
}
u32 canvas_create_VBO() {
u32 VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
return VBO;
}
u32 canvas_create_VAO() {
u32 VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
return VAO;
}
u32 canvas_create_EBO() {
u32 EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
return EBO;
}
我的顶点着色器(GLSL):
# version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTex;
uniform mat4 MODEL;
uniform mat4 VIEW;
uniform mat4 PROJ;
out vec2 _tex;
void main() {
_tex = aTex;
gl_Position = PROJ * VIEW * MODEL * vec4(aPos, 1);
}
我还打印了矩阵:
Mine
0.785398 0.000000 0.000000 0.000000
0.000000 0.785398 0.000000 0.000000
0.000000 0.000000 -1.002002 -1.000000
0.000000 0.000000 -0.200200 1.000000
CGLM
2.414216 0.000000 0.000000 0.000000
0.000000 2.414216 0.000000 0.000000
0.000000 0.000000 -1.002002 -1.000000
0.000000 0.000000 -0.200200 0.000000
我现在也注意到,我的错了,因为它不应该具有最后一个值1,它之所以发生是因为我之前正在做一个单位矩阵,当我删除那个1时,它停止工作
透视投影矩阵的计算已完成。对称透视投影矩阵为:
column 0: 1/(ta*a) 0 0 0
column 1: 0 1/ta 0 0
column 2: 0 0 -(f+n)/(f-n) -1
column 3: 0 0 -2*f*n/(f-n) 0
其中
a = w / h
和 ta = tan(fov_y / 2)
对于您的代码来说,这意味着:
float aspect = (float)window_width / (float)wondow_height;
proj[0][0] = 1 / (tan(PI4 / 2) * aspect);
proj[1][1] = 1 / tan(PI4 / 2);
proj[2][2] = (cam.far + cam.near) / (cam.near - cam.far);
proj[2][3] = -1;
proj[3][2] = 2 * cam.far * cam.near / (cam.near - cam.far);
proj[3][3] = 0;
注意,
glm_mat4_identity
用单位矩阵初始化矩阵。所以proj[3][3]
需要设置为0。