c-OpenGL:滚动旋转对其他旋转有不必要的影响



我用C语言编写了一个简单的程序,使用GLEW、SDL和cglm库用OpenGL 3.3绘制三角形。我希望能够围绕3轴旋转相机:

  • 向左或向右移动鼠标以绕Y轴旋转(偏航旋转(
  • 向下或向上移动鼠标以绕X轴旋转(俯仰旋转(
  • 按Q或E键绕Z轴旋转(滚动旋转(

问题:如果我进行正滚动旋转(按E键(,然后进行正偏航旋转(将鼠标向右移动(,则最后一次旋转将旋转到屏幕底部,而不是屏幕右侧。因此,滚转旋转会对其他旋转产生不必要的影响。你知道我该怎么解决吗?

";main.c":

#include <GL/glew.h> /* for glClear */
#include <SDL.h> /* for SDL_Init */
#include <cglm/cglm.h> /* for vec3 */
#include <fcntl.h> /* for open */
#include <unistd.h> /* for read */
#define BUF_SIZE 512
static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype)
{
int fd = -1;
int nbytes = -1;
GLchar sbuf[BUF_SIZE];
const GLchar *ssrc = NULL;
GLint ret = -1;
GLchar log[BUF_SIZE];
/* read the shader source */
fd = open(spath, O_RDONLY);
if(fd == -1)
{
SDL_Log("Error: open");
return -1;
}
nbytes = read(fd, &sbuf, BUF_SIZE);
if(nbytes == -1)
{
SDL_Log("Error: read");
return -1;
}
sbuf[nbytes] = '';
ssrc = sbuf;
if(close(fd) == -1)
{
SDL_Log("Error: close");
return -1;
}
/* create the shader */
*shader = glCreateShader(stype);
glShaderSource(*shader, 1, &ssrc, NULL);
glCompileShader(*shader);
glGetShaderiv(*shader, GL_COMPILE_STATUS, &ret);
if(ret == GL_FALSE)
{
glGetShaderInfoLog(*shader, BUF_SIZE, NULL, log);
SDL_Log("Error: glGetShaderiv: %s", log);
return -1;
}
return 0;
}
static int prog_create(GLuint *prog, const char *vspath, const char *fspath)
{
GLuint vshader = -1;
GLuint fshader = -1;
GLint ret = -1;
GLchar log[BUF_SIZE];
/* create the vertex shader */
if(prog_create_shader(&vshader, vspath, GL_VERTEX_SHADER) == -1)
{
SDL_Log("Error: prog_create_shader (%s)", vspath);
return -1;
}
/* create the fragment shader */
if(prog_create_shader(&fshader, fspath, GL_FRAGMENT_SHADER) == -1)
{
SDL_Log("Error: prog_create_shader (%s)", fspath);
return -1;
}
/* create the program */
*prog = glCreateProgram();
glAttachShader(*prog, vshader);
glAttachShader(*prog, fshader);
glLinkProgram(*prog);
glGetProgramiv(*prog, GL_LINK_STATUS, &ret);
if(ret == GL_FALSE)
{
glGetProgramInfoLog(*prog, BUF_SIZE, NULL, log);
SDL_Log("Error: glGetShaderiv: %s", log);
return -1;
}
glDeleteShader(fshader);
glDeleteShader(vshader);
glUseProgram(*prog);
return 0;
}
static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up)
{
float xoffset = xrel;
float yoffset = yrel;
float zoffset = zrel;
float sensitivity = 0.1;
xoffset *= sensitivity;
yoffset *= sensitivity;
zoffset *= sensitivity;
*yaw += xoffset;
*pitch += -yoffset;
*roll += zoffset;
vec3 front =
{
cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)),
sin(glm_rad(*pitch)),
sin(glm_rad(*yaw)) * cos(glm_rad(*pitch))
};
glm_vec3_normalize_to(front, camera_front);
vec3 up =
{
cos(glm_rad(*roll)),
sin(glm_rad(*roll)),
0
};
glm_vec3_normalize_to(up, camera_up);
}
static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll)
{
SDL_Event event = {0};
float cameraSpeed = 0.01;
vec3 tmp = {0};
const Uint8* keystates = NULL;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_MOUSEMOTION:
input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
*loop = 0;
break;
}
break;
case SDL_WINDOWEVENT:
switch(event.window.event)
{
case SDL_WINDOWEVENT_CLOSE:
*loop = 0;
break;
case SDL_WINDOWEVENT_RESIZED:
glViewport(0, 0, event.window.data1, event.window.data2);
break;
}
break;
}
}
keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_Q])
{
input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up);
}
if(keystates[SDL_SCANCODE_E])
{
input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up);
}
if(keystates[SDL_SCANCODE_W])
{
glm_vec3_scale(camera_front, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_S])
{
glm_vec3_scale(camera_front, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_A])
{
glm_vec3_cross(camera_front, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_D])
{
glm_vec3_cross(camera_front, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
}
static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
{
/* declare variables */
mat4 view = GLM_MAT4_IDENTITY_INIT;
mat4 projection = GLM_MAT4_IDENTITY_INIT;
mat4 model = GLM_MAT4_IDENTITY_INIT;
GLuint uniform = 0;
vec3 tmp = {0};
int width = 0;
int height = 0;
/* clear */
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* update the view matrix */
glm_vec3_add(camera_position, camera_front, tmp);
glm_lookat(camera_position, tmp, camera_up, view);
uniform = glGetUniformLocation(prog, "view");
glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]);
/* update the projection matrix */
SDL_GetWindowSize(window, &width, &height);
glm_perspective(glm_rad(45), width / (float)height, 0.1, 100, projection);
uniform = glGetUniformLocation(prog, "projection");
glUniformMatrix4fv(uniform, 1, GL_FALSE, projection[0]);
/* update the model matrix */
glm_translate(model, triangle_position);
uniform = glGetUniformLocation(prog, "model");
glUniformMatrix4fv(uniform, 1, GL_FALSE, model[0]);
/* draw the triangle */
glBindVertexArray(triangle_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
SDL_GL_SwapWindow(window);
}
int main()
{
/* declare variables */
int loop = 1;
SDL_DisplayMode dm = {0};
SDL_Window *window = NULL;
SDL_GLContext glcontext = NULL;
GLuint prog = 0;
GLuint triangle_vao = 0;
GLuint triangle_vbo = 0;
vec3 camera_position = {0, 0, 3};
vec3 camera_front = {0, 0, -1};
vec3 camera_up = {0, 1, 0};
float yaw = -90;
float pitch = 0;
float roll = 90;
float triangle_vertices[] =
{
-1, -1, +0,
+0, +1, +0,
+1, -1, +0
};
vec3 triangle_position = {0, 0, 0};
/* initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO) != 0)
{
SDL_Log("Error: SDL_Init: %s", SDL_GetError());
return 1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if(SDL_GetCurrentDisplayMode(0, &dm) < 0)
{
SDL_Log("Error: SDL_GetCurrentDisplayMode");
return 1;
}
window = SDL_CreateWindow("sdl2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w, dm.h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if(window == NULL)
{
SDL_Log("Error: SDL_CreateWindow");
return 1;
}
glcontext = SDL_GL_CreateContext(window);
SDL_SetRelativeMouseMode(SDL_TRUE);
/* initialize Glew */
if(glewInit() != GLEW_OK)
{
SDL_Log("Error: glewInit");
return 1;
}
/* print information */
printf("screen width: %dn", dm.w);
printf("screen height: %dn", dm.h);
printf("GL_VENDOR: %sn", glGetString(GL_VENDOR));
printf("GL_RENDERER: %sn", glGetString(GL_RENDERER));
printf("GL_VERSION: %sn", glGetString(GL_VERSION));
printf("GL_SHADING_LANGUAGE_VERSION: %sn", glGetString(GL_SHADING_LANGUAGE_VERSION));
/* create the program */
if(prog_create(&prog, "vs.sha", "fs.sha") == -1)
{
SDL_Log("Error: prog_create");
return 1;
}
/* enable features */
glEnable(GL_DEPTH_TEST);
/* create the vertex buffer object */
glGenBuffers(1, &triangle_vbo);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
/* create the vertex array object */
glGenVertexArrays(1, &triangle_vao);
glBindVertexArray(triangle_vao);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
/* loop */
while(loop)
{
input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll);
display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao);
}
/* free */
glDeleteBuffers(1, &triangle_vbo);
glDeleteVertexArrays(1, &triangle_vao);
glDeleteProgram(prog);
SDL_GL_DeleteContext(glcontext);
SDL_Quit();
return 0;
}

";vs.sha";(顶点着色器(:

#version 330 core
layout (location = 0) in vec3 pos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(pos, 1);
}

";fs.sha";(碎片着色器(:

#version 330 core
out vec4 color;
void main()
{
color = vec4(1, 0.5, 0, 1);
}

结果(SDL窗口的视频(:https://youtu.be/XIfA6pRNi24

补丁#1

我试着应用了RedRuin的建议。这是补丁:

$ cat main.c.patch 
--- old/main.c  2022-07-25 06:36:48.165479193 +0200
+++ new/main.c  2022-07-26 01:53:55.221830761 +0200
@@ -4,6 +4,21 @@
#include <fcntl.h> /* for open */
#include <unistd.h> /* for read */
#define BUF_SIZE 512
+void rotate_camera_x(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+    glm_vec3_rotate(camera_forward, angle, camera_right);
+    glm_vec3_rotate(camera_up,      angle, camera_right);
+}
+void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+    glm_vec3_rotate(camera_forward, angle, camera_up);
+    glm_vec3_rotate(camera_right,   angle, camera_up);
+}
+void rotate_camera_z(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+    glm_vec3_rotate(camera_up,      angle, camera_forward);
+    glm_vec3_rotate(camera_right,   angle, camera_forward);
+}
static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype)
{
int fd = -1;
@@ -80,7 +95,7 @@
glUseProgram(*prog);
return 0;
}
-static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up)
+static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
{
float xoffset = xrel;
float yoffset = yrel;
@@ -92,22 +107,14 @@
*yaw += xoffset;
*pitch += -yoffset;
*roll += zoffset;
-   vec3 front =
-   {
-       cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)),
-       sin(glm_rad(*pitch)),
-       sin(glm_rad(*yaw)) * cos(glm_rad(*pitch))
-   };
-   glm_vec3_normalize_to(front, camera_front);
-   vec3 up =
-   {
-       cos(glm_rad(*roll)),
-       sin(glm_rad(*roll)),
-       0
-   };
-   glm_vec3_normalize_to(up, camera_up);
+   if(xrel != 0)
+       rotate_camera_y(*yaw, camera_forward, camera_up, camera_right);
+   if(yrel != 0)
+       rotate_camera_x(*pitch, camera_forward, camera_up, camera_right);
+   if(zrel != 0)
+       rotate_camera_z(*roll, camera_forward, camera_up, camera_right);
}
-static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll)
+static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right, float *yaw, float *pitch, float *roll)
{
SDL_Event event = {0};
float cameraSpeed = 0.01;
@@ -118,7 +125,7 @@
switch(event.type)
{
case SDL_MOUSEMOTION:
-               input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up);
+               input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_forward, camera_up, camera_right);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
@@ -144,38 +151,38 @@
keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_Q])
{
-       input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, -1, yaw, pitch, roll, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_E])
{
-       input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, 1, yaw, pitch, roll, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_W])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_S])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_A])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_D])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
}
-static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
+static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
{
/* declare variables */
mat4 view = GLM_MAT4_IDENTITY_INIT;
@@ -189,7 +196,7 @@
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* update the view matrix */
-   glm_vec3_add(camera_position, camera_front, tmp);
+   glm_vec3_add(camera_position, camera_forward, tmp);
glm_lookat(camera_position, tmp, camera_up, view);
uniform = glGetUniformLocation(prog, "view");
glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]);
@@ -218,8 +225,9 @@
GLuint triangle_vao = 0;
GLuint triangle_vbo = 0;
vec3 camera_position = {0, 0, 3};
-   vec3 camera_front = {0, 0, -1};
+   vec3 camera_forward = {0, 0, -1};
vec3 camera_up = {0, 1, 0};
+   vec3 camera_right = {1, 0, 0};
float yaw = -90;
float pitch = 0;
float roll = 90;
@@ -285,8 +293,8 @@
/* loop */
while(loop)
{
-       input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll);
-       display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao);
+       input(&loop, camera_position, camera_forward, camera_up, camera_right, &yaw, &pitch, &roll);
+       display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao);
}
/* free */
glDeleteBuffers(1, &triangle_vbo);
$ patch -p1 < main.c.patch
$ gcc -std=c99 -pedantic -Wall -Werror -g `pkg-config sdl2 --cflags` `pkg-config glew --cflags` -o main.out main.c  `pkg-config sdl2 --libs-only-L` `pkg-config glew --libs-only-L` `pkg-config sdl2 --libs-only-l` `pkg-config glew --libs-only-l` -lm
$ ./main.out 
screen width: 1920
screen height: 1080
GL_VENDOR: AMD
GL_RENDERER: AMD Radeon RX 580 Series (polaris10, LLVM 11.0.0, DRM 3.46, 5.18.10-desktop)
GL_VERSION: 4.6 (Core Profile) Mesa 22.1.3
GL_SHADING_LANGUAGE_VERSION: 4.60

但当我移动鼠标或按下Q或E键时,旋转会发生奇怪的事情。

补丁#2

我试着应用RedRuin的新建议。这是补丁:

$ cat main.c.patch 
--- 20220725/main.c 2022-07-25 06:36:48.165479193 +0200
+++ 20220727/main.c 2022-07-27 22:55:56.805646313 +0200
@@ -4,6 +4,21 @@
#include <fcntl.h> /* for open */
#include <unistd.h> /* for read */
#define BUF_SIZE 512
+void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+   printf("rotate_camera_y: [1] angle(%+.3f deg, %+.3f rad), camera_forward(%+.3f, %+.3f, %+.3f), camera_right(%+.3f, %+.3f, %+.3f)n",
+           angle,
+           glm_rad(angle),
+           camera_forward[0],  camera_forward[1],  camera_forward[2],
+           camera_up[0],  camera_up[1],  camera_up[2]);
+   glm_vec3_rotate(camera_forward, glm_rad(angle), camera_up);
+   glm_vec3_rotate(camera_right,   glm_rad(angle), camera_up);
+   printf("rotate_camera_y: [2] angle(%+.3f deg, %+.3f rad), camera_forward(%+.3f, %+.3f, %+.3f), camera_right(%+.3f, %+.3f, %+.3f)n",
+           angle,
+           glm_rad(angle),
+           camera_forward[0],  camera_forward[1],  camera_forward[2],
+           camera_up[0],  camera_up[1],  camera_up[2]);
+}
static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype)
{
int fd = -1;
@@ -80,7 +95,7 @@
glUseProgram(*prog);
return 0;
}
-static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up)
+static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
{
float xoffset = xrel;
float yoffset = yrel;
@@ -92,22 +107,10 @@
*yaw += xoffset;
*pitch += -yoffset;
*roll += zoffset;
-   vec3 front =
-   {
-       cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)),
-       sin(glm_rad(*pitch)),
-       sin(glm_rad(*yaw)) * cos(glm_rad(*pitch))
-   };
-   glm_vec3_normalize_to(front, camera_front);
-   vec3 up =
-   {
-       cos(glm_rad(*roll)),
-       sin(glm_rad(*roll)),
-       0
-   };
-   glm_vec3_normalize_to(up, camera_up);
+   if(xrel != 0)
+       rotate_camera_y(45, camera_forward, camera_up, camera_right);
}
-static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll)
+static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right, float *yaw, float *pitch, float *roll)
{
SDL_Event event = {0};
float cameraSpeed = 0.01;
@@ -118,7 +121,7 @@
switch(event.type)
{
case SDL_MOUSEMOTION:
-               input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up);
+               input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_forward, camera_up, camera_right);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
@@ -144,38 +147,38 @@
keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_Q])
{
-       input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, -1, yaw, pitch, roll, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_E])
{
-       input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, 1, yaw, pitch, roll, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_W])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_S])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_A])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_D])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
}
-static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
+static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
{
/* declare variables */
mat4 view = GLM_MAT4_IDENTITY_INIT;
@@ -189,7 +192,7 @@
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* update the view matrix */
-   glm_vec3_add(camera_position, camera_front, tmp);
+   glm_vec3_add(camera_position, camera_forward, tmp);
glm_lookat(camera_position, tmp, camera_up, view);
uniform = glGetUniformLocation(prog, "view");
glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]);
@@ -218,8 +221,9 @@
GLuint triangle_vao = 0;
GLuint triangle_vbo = 0;
vec3 camera_position = {0, 0, 3};
-   vec3 camera_front = {0, 0, -1};
+   vec3 camera_forward = {0, 0, -1};
vec3 camera_up = {0, 1, 0};
+   vec3 camera_right = {1, 0, 0};
float yaw = -90;
float pitch = 0;
float roll = 90;
@@ -285,8 +289,8 @@
/* loop */
while(loop)
{
-       input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll);
-       display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao);
+       input(&loop, camera_position, camera_forward, camera_up, camera_right, &yaw, &pitch, &roll);
+       display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao);
}
/* free */
glDeleteBuffers(1, &triangle_vbo);

因此,印刷的价值观似乎是好的。在补丁#1中;旋转现在正在做奇怪的事情";,我的意思是,这很难描述,但就像它旋转得太快了,但在一个好的轴上。

补丁#3

我终于明白了旋转太快出了什么问题。在原始代码(未修补(中,我使用旋转角度的绝对值,这是存储在变量pitchyawroll中的一个大值。现在,在修补的代码中,我必须使用旋转角度的相对值,这是一个小值,我不需要再将其存储到变量pitchyawroll中。

我试着应用这个。这是补丁:

$ cat main.c.patch 
--- 20220725/main.c 2022-07-25 06:36:48.165479193 +0200
+++ 20220728/main.c 2022-07-28 04:05:37.677937509 +0200
@@ -4,6 +4,21 @@
#include <fcntl.h> /* for open */
#include <unistd.h> /* for read */
#define BUF_SIZE 512
+void rotate_camera_x(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+   glm_vec3_rotate(camera_forward, glm_rad(angle), camera_right);
+   glm_vec3_rotate(camera_up,      glm_rad(angle), camera_right);
+}
+void rotate_camera_y(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+   glm_vec3_rotate(camera_forward, glm_rad(angle), camera_up);
+   glm_vec3_rotate(camera_right,   glm_rad(angle), camera_up);
+}
+void rotate_camera_z(float angle, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
+{
+   glm_vec3_rotate(camera_up,      glm_rad(angle), camera_forward);
+   glm_vec3_rotate(camera_right,   glm_rad(angle), camera_forward);
+}
static int prog_create_shader(GLuint *shader, const char *spath, GLenum stype)
{
int fd = -1;
@@ -80,34 +95,23 @@
glUseProgram(*prog);
return 0;
}
-static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, float *yaw, float *pitch, float *roll, vec3 camera_front, vec3 camera_up)
+static void input_mouse(Sint16 xrel, Sint16 yrel, Sint16 zrel, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
{
-   float xoffset = xrel;
-   float yoffset = yrel;
+   float xoffset = -xrel;
+   float yoffset = -yrel;
float zoffset = zrel;
float sensitivity = 0.1;
xoffset *= sensitivity;
yoffset *= sensitivity;
zoffset *= sensitivity;
-   *yaw += xoffset;
-   *pitch += -yoffset;
-   *roll += zoffset;
-   vec3 front =
-   {
-       cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)),
-       sin(glm_rad(*pitch)),
-       sin(glm_rad(*yaw)) * cos(glm_rad(*pitch))
-   };
-   glm_vec3_normalize_to(front, camera_front);
-   vec3 up =
-   {
-       cos(glm_rad(*roll)),
-       sin(glm_rad(*roll)),
-       0
-   };
-   glm_vec3_normalize_to(up, camera_up);
+   if(xrel != 0)
+       rotate_camera_y(xoffset, camera_forward, camera_up, camera_right);
+   if(yrel != 0)
+       rotate_camera_x(yoffset, camera_forward, camera_up, camera_right);
+   if(zrel != 0)
+       rotate_camera_z(zoffset, camera_forward, camera_up, camera_right);
}
-static void input(int *loop, vec3 camera_position, vec3 camera_front, vec3 camera_up, float *yaw, float *pitch, float *roll)
+static void input(int *loop, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 camera_right)
{
SDL_Event event = {0};
float cameraSpeed = 0.01;
@@ -118,7 +122,7 @@
switch(event.type)
{
case SDL_MOUSEMOTION:
-               input_mouse(event.motion.xrel, event.motion.yrel, 0, yaw, pitch, roll, camera_front, camera_up);
+               input_mouse(event.motion.xrel, event.motion.yrel, 0, camera_forward, camera_up, camera_right);
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
@@ -144,38 +148,38 @@
keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_Q])
{
-       input_mouse(0, 0, -1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, -1, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_E])
{
-       input_mouse(0, 0, 1, yaw, pitch, roll, camera_front, camera_up);
+       input_mouse(0, 0, 1, camera_forward, camera_up, camera_right);
}
if(keystates[SDL_SCANCODE_W])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_S])
{
-       glm_vec3_scale(camera_front, cameraSpeed, tmp);
+       glm_vec3_scale(camera_forward, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_A])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_sub(camera_position, tmp, camera_position);
}
if(keystates[SDL_SCANCODE_D])
{
-       glm_vec3_cross(camera_front, camera_up, tmp);
+       glm_vec3_cross(camera_forward, camera_up, tmp);
glm_vec3_normalize(tmp);
glm_vec3_scale(tmp, cameraSpeed, tmp);
glm_vec3_add(camera_position, tmp, camera_position);
}
}
-static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_front, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
+static void display(SDL_Window *window, GLuint prog, vec3 camera_position, vec3 camera_forward, vec3 camera_up, vec3 triangle_position, GLuint triangle_vao)
{
/* declare variables */
mat4 view = GLM_MAT4_IDENTITY_INIT;
@@ -189,7 +193,7 @@
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* update the view matrix */
-   glm_vec3_add(camera_position, camera_front, tmp);
+   glm_vec3_add(camera_position, camera_forward, tmp);
glm_lookat(camera_position, tmp, camera_up, view);
uniform = glGetUniformLocation(prog, "view");
glUniformMatrix4fv(uniform, 1, GL_FALSE, view[0]);
@@ -218,11 +222,9 @@
GLuint triangle_vao = 0;
GLuint triangle_vbo = 0;
vec3 camera_position = {0, 0, 3};
-   vec3 camera_front = {0, 0, -1};
+   vec3 camera_forward = {0, 0, -1};
vec3 camera_up = {0, 1, 0};
-   float yaw = -90;
-   float pitch = 0;
-   float roll = 90;
+   vec3 camera_right = {1, 0, 0};
float triangle_vertices[] =
{
-1, -1, +0,
@@ -285,8 +287,8 @@
/* loop */
while(loop)
{
-       input(&loop, camera_position, camera_front, camera_up, &yaw, &pitch, &roll);
-       display(window, prog, camera_position, camera_front, camera_up, triangle_position, triangle_vao);
+       input(&loop, camera_position, camera_forward, camera_up, camera_right);
+       display(window, prog, camera_position, camera_forward, camera_up, triangle_position, triangle_vao);
}
/* free */
glDeleteBuffers(1, &triangle_vbo);

所以它的效果非常好。问题已解决。

结果(SDL窗口的视频(:https://youtu.be/w_4J_XwmnSg

考虑一下它的作用:

vec3 front =
{
cos(glm_rad(*yaw)) * cos(glm_rad(*pitch)),
sin(glm_rad(*pitch)),
sin(glm_rad(*yaw)) * cos(glm_rad(*pitch))
};

此计算考虑了偏航和俯仰,但没有考虑滚转。因此,当你的up矢量被滚动时,你的front矢量对此一无所知,并且(基于OpenGL的坐标系(假设";向上";总是正Y。对于该代码位;右";始终是同一方向,无论相机的方向如何。这就是为什么向右移动鼠标会向上移动三角形;相机的方向改变了,但旋转的轴没有改变。

你可以尝试更新你的前矢量,以包括滚转参数(从这个答案中提取(:

vec3 front =
{
-cos(glm_rad(*yaw)) * sin(glm_rad(*pitch)) * sin(glm_rad(*roll)) - sin(glm_rad(*yaw)) * cos(glm_rad(*roll)),
-sin(glm_rad(*yaw)) * sin(glm_rad(*pitch)) * sin(glm_rad(*roll)) + cos(glm_rad(*yaw)) * cos(glm_rad(*roll)),
cos(glm_rad(*pitch)) * sin(glm_rad(*roll))
}

不幸的是,这是非常不可怕的,你迟早会遇到Gimbal Lock。你可以使用一个矩阵或一个称为四元数的特殊4维向量来完全绕过万向节锁定,但它们很难直观(与滚转-俯仰-偏航相比(,对于这样的简单应用程序来说可能会有些过头。

我将提出一个中间立场,我已经成功地使用了像这样的简单相机。跟踪您的相机的3个基本矢量,这些矢量定义了相机的参考方向:

// For example, we might start with basis vectors like this
vec3 camera_forward = {0, 0, -1};
vec3 camera_up = {0, 1, 0};
vec3 camera_right = {1, 0, 0};

然后,我们可以使用glm_vec3_rotate()函数来围绕彼此旋转这些基向量,而不是基本XYZ轴:

void rotate_camera_x(float angle)
{
// rotate around the camera's x axis, or camera_right (pitch)
glm_vec3_rotate(&camera_forward, angle, camera_right)
glm_vec3_rotate(&camera_up,      angle, camera_right)
// technically we could also rotate camera_right around camera_right, but that's redundant
}
void rotate_camera_y(float angle)
{
// rotate around the camera's y axis, or camera_up (yaw)
glm_vec3_rotate(&camera_forward, angle, camera_up)
glm_vec3_rotate(&camera_right,   angle, camera_up)
}
void rotate_camera_z(float angle)
{
// rotate around the camera's z axis, or camera_forward (roll)
glm_vec3_rotate(&camera_up,      angle, camera_forward)
glm_vec3_rotate(&camera_right,   angle, camera_forward)
}

这样做的诀窍是,我们旋转的轴会随着相机的旋转而变化;现在,当你滚动时,你改变了camera_rightcamera_up的方向,改变了下一次旋转的轴。这样可以确保摄影机的移动始终与其方向相关联。

最新更新