我正在做一个OpenGL项目,它使用光线推进的图形,但我在控制相机时遇到了一些问题。
场景完全在顶点明暗器内生成,顶点明暗器在位于视区前面的四顶点平面上渲染。我的输入函数从用户那里获得按键和鼠标移动,并计算传递给片段着色器的相机的平移和旋转。
问题是,相机的旋转和移动行为非常奇怪。同时俯仰和偏航不仅会导致相机滚动(尽管我使用的是四元数来计算旋转),而且相机的移动(WASD)似乎只沿着世界的X和Z轴移动,而不是相机的相对轴。
任何帮助解决这个问题的人都将不胜感激。
Main.cpp:
# include <iostream>
# include <string>
# include <glad/glad.h>
# include <glfw/glfw3.h>
# include <glm/glm.hpp>
# include <glm/gtc/type_ptr.hpp>
# include "readshader.h"
# include "input.h"
# define GLM_SWIZZLE_XYZ
# define WIDTH 640
# define HEIGHT 480
using namespace std;
using namespace glm;
int main() {
GLFWwindow* window;
//Vertex array for screen plane
float vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};
unsigned int vbo;
unsigned int vao;
unsigned int vs;
unsigned int fs;
unsigned int shader_prog;
//Game logic variables
vec3 camera_pos = vec3(0.0f, 0.0f, 0.0f);
quat camera_rot = quat(0.0f, 0.0f, 0.0f, 0.0f);
vec2 screen_size = vec2(float(WIDTH), float(HEIGHT));
float delta_time = 0.0;
float last_time = 0.0;
unsigned int a_camera_pos;
unsigned int a_camera_rot;
unsigned int a_screen_size;
unsigned int a_delta_time;
//Read shader code from GLSL files
string vs_str = readShader("vert_pass.glsl");
string fs_str = readShader("frag_raymarch_test.glsl");
const char* vs_source = vs_str.c_str();
const char* fs_source = fs_str.c_str();
//Initialize GLFW and create window
glfwInit();
window = glfwCreateWindow(WIDTH, HEIGHT, "OpenGL", NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glViewport(0, 0, WIDTH, HEIGHT);
//Set GLFW input parameters
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
glfwSetCursorPosCallback(window, mouseCallback);
//Create VAO and VBO
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//Compile shaders and create shader program
vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
shader_prog = glCreateProgram();
glAttachShader(shader_prog, vs);
glAttachShader(shader_prog, fs);
glLinkProgram(shader_prog);
glDetachShader(shader_prog, vs);
glDetachShader(shader_prog, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glUseProgram(shader_prog);
//Setup attribute arrays and uniform variables for vertex shader
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
a_camera_pos = glGetUniformLocation(shader_prog, "vert_camera_pos");
a_camera_rot = glGetUniformLocation(shader_prog, "vert_camera_rot");
a_screen_size = glGetUniformLocation(shader_prog, "vert_screen_size");
a_delta_time = glGetUniformLocation(shader_prog, "vert_delta_time");
//Main event loop
while (!glfwWindowShouldClose(window)) {
//Calculate delta time
delta_time = glfwGetTime() - last_time;
last_time = glfwGetTime();
//Pass game information to shader program
glProgramUniform3fv(shader_prog, a_camera_pos, 1, value_ptr(camera_pos));
glProgramUniform4fv(shader_prog, a_camera_rot, 1, value_ptr(camera_rot));
glProgramUniform2fv(shader_prog, a_screen_size, 1, value_ptr(screen_size));
glProgramUniform1f(shader_prog, a_delta_time, delta_time);
//Process user input
input(window, &camera_pos, &camera_rot, delta_time);
//Rendering procedure
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glfwSwapBuffers(window);
}
//Cleanup and exit
glDeleteProgram(shader_prog);
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
Input.h:
# pragma once
# include <iostream>
# include <glfw/glfw3.h>
# include <glm/glm.hpp>
# define GLM_SWIZZLE_XYZW
# define SPEED 5.0
# define SENS 0.001
using namespace glm;
float camera_pitch = 0.0;
float camera_yaw = 0.0;
bool first = true;
float lastx = 0.0;
float lasty = 0.0;
float offsetx;
float offsety;
void input(GLFWwindow* window, vec3* camera_pos, quat* camera_rot, float delta_time) {
float step = SPEED * delta_time;
vec3 camera_fwd = vec3(0.0f, 0.0f, -1.0f);
vec3 camera_up = vec3(0.0f, 1.0f, 0.0f);
vec3 camera_right = vec3(-1.0f, 0.0f, 0.0f);
quat quatx;
quat quaty;
glfwPollEvents();
//Build rotation quaternion from camera angles
quatx = angleAxis(camera_yaw, camera_up);
quaty = angleAxis(camera_pitch, camera_right);
*camera_rot = quatx * quaty;
//Update camera axis positions
camera_fwd = *camera_rot * camera_fwd * conjugate(*camera_rot);
camera_right = *camera_rot * camera_right * conjugate(*camera_rot);
//Check keyboard presses
if (glfwGetKey(window, GLFW_KEY_W)) {
*camera_pos -= camera_fwd * step;
}
if (glfwGetKey(window, GLFW_KEY_S)) {
*camera_pos += camera_fwd * step;
}
if (glfwGetKey(window, GLFW_KEY_A)) {
*camera_pos -= normalize(cross(camera_fwd, camera_up)) * step;
}
if (glfwGetKey(window, GLFW_KEY_D)) {
*camera_pos += normalize(cross(camera_fwd, camera_up)) * step;
}
if (glfwGetKey(window, GLFW_KEY_SPACE)) {
*camera_pos += camera_up * step;
}
if (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL)) {
*camera_pos -= camera_up * step;
}
}
void mouseCallback(GLFWwindow* window, double posx, double posy) {
if (first) {
lastx = -posx;
lasty = -posy;
first = false;
}
offsetx = -posx - lastx;
offsety = lasty + posy;
lastx = -posx;
lasty = -posy;
offsetx *= SENS;
offsety *= SENS;
camera_yaw += offsetx;
camera_pitch += offsety;
if (camera_pitch > 89.0) {
camera_pitch = 89.0;
}
if (camera_pitch < -89.0) {
camera_pitch = -89.0;
}
}
Vertex_pass.glsl:
# version 460
//Passes variables from attributes to next shader
layout(location = 0) in vec3 pos;
uniform vec3 vert_camera_pos;
uniform vec4 vert_camera_rot;
uniform vec2 vert_screen_size;
uniform float vert_delta_time;
out vec3 camera_pos;
out vec4 camera_rot;
out vec2 screen_size;
out float delta_time;
void main() {
gl_Position = vec4(pos, 1.0);
camera_pos = vert_camera_pos;
camera_rot = vert_camera_rot;
screen_size = vert_screen_size;
delta_time = vert_delta_time;
}
Frag_raymarch_est.glsl:
# version 460
# define MAX_STEPS 100
# define MAX_DIST 10000.0
# define SURF_DIST 0.001
# define NORMAL_SAMPLE_SIZE 0.001
# define TAU 6.283185
# define PI 3.141592
in vec3 camera_pos;
in vec4 camera_rot;
in vec2 screen_size;
in float delta_time;
out vec3 color;
vec3 rotateVector(vec4 quat, vec3 vec) {
return vec + 2.0 * cross(cross(vec, quat.xyz) + quat.w * vec, quat.xyz);
}
//SDF
float getSD(vec3 p) {
vec4 s = vec4(0, 1, 6, 1);
float sphere_dist = length(p - s.xyz) - s.w;
float plane_dist = p.y;
float d = min(sphere_dist, plane_dist);
return d;
}
float getDist(vec3 p) {
float dist = 0;
//SDF
dist = getSD(p);
return dist;
}
float raymarch(vec3 ro, vec3 rd) {
float dist = 0.0;
for(int i = 0; i < MAX_STEPS; i++) {
vec3 p = ro + rd * dist;
dist += getDist(p);
if(dist >= MAX_DIST || dist <= SURF_DIST) {
break;
}
}
return dist;
}
void main() {
vec2 uv = (gl_FragCoord.xy - 0.5 * screen_size) / screen_size.y;
//vec3 ro = vec3(0.0, 1.0, 0.0);
//vec3 ro = camera_pos;
vec3 ro = camera_pos;
vec3 fd = normalize(vec3(uv.x, uv.y, 1.0));
vec3 rd = rotateVector(camera_rot, fd);
color = vec3(raymarch(ro, rd) / 6.0);
}
1条答案
按热度按时间xfb7svmp1#
我想通了。我回到了使用欧拉角而不是四元数,并使用它们来计算相机的向前向量。问题是,在碎片着色器中,我使用了Camera_fwd向量作为注视点,而我本应使用Camera_pos+Camera_fwd。