OpenGL---基于四元数的摄像机系统_opengl 四元数摄像机的实现-程序员宅基地

技术标签: 图形学  3d  图形学学习笔记  图形渲染  几何学  opengl  

目录

基础概念:

摄象机:

摄像机坐标系:

相机移动

相机缩放

相机旋转

欧拉角:

基础概念:

欧拉角实现的摄象机旋转:

欧拉角的缺点:

四元数:

基本概念:

四元数的使用:

四元数实现摄象机旋转:

完整代码:

学习资料:


---------------------------------------------------------博主:mx

基础概念:

摄象机:

OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动。

当我们讨论摄像机/观察空间(Camera/View Space)的时候,是在讨论以摄像机的视角作为场景原点时场景中所有的顶点坐标:观察矩阵把所有的世界坐标变换为相对于摄像机位置与方向的观察坐标。要定义一个摄像机,我们需要它在世界空间中的位置、观察的方向、一个指向它右测的向量以及一个指向它上方的向量。细心的读者可能已经注意到我们实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。

摄像机坐标系:

我们只需要求出摄象机的前向(Front)向量,然后根据世界坐标系的y轴的向上的向量(0.0f,1.0f,0.0f),就可以通过叉乘求出与Front垂直的右向(Right)向量,知道Front向量和Right向量之后再根据叉乘就能够求出向上(Up)向量,由此摄像机的xyz三个轴都求出来了。

由此我们再根据摄像机坐标和这三个轴,我们就能够求出来一个view矩阵,这个矩阵乘以任何向量都能够将这个向量变换到摄像机坐标系下。

(其中R是右向量,U是上向量,D是方向向量PP是摄像机位置向量。注意,位置向量是相反的,因为我们最终希望把世界平移到与我们自身移动的相反方向。)

对应opengl的api为

glm::lookAt(glm::vec3 Position, glm::vec3 Target, glm::vec3 Up);

glm::LookAt函数需要一个位置、目标和上向量。它会创建一个观察矩阵。

相机移动

只要改变相机的Position ,对应的view矩阵就会改变,物体和相机的相对距离也会改变。

相机缩放

视野(FOV)定义了我们可以看到场景中多大的范围。当视野变小时,场景投影出来的空间就会减小,产生放大(Zoom In)了的感觉。

相机旋转

现在旋转常见的方法有:欧拉角(Euler angles)和四元数(Quaternion)

欧拉角:

基础概念:

欧拉角是表示朝向的最简方法,只需存储绕X、Y、Z轴旋转的角度,分别叫Pitch\Yaw\Roll(俯仰角\偏航角\滚转角)

俯仰角是描述我们如何往上或往下看的角,可以在第一张图中看到。第二张图展示了偏航角,偏航角表示我们往左和往右看的程度。滚转角代表我们如何翻滚摄像机,通常在太空飞船的摄像机中使用。

这三个旋转是依次施加的,通常的顺序是:Y-Z-X(但并非一定要按照这种顺序)。顺序不同,产生的结果也不同。

欧拉角实现的摄象机旋转:

对于我们的摄像机系统来说,我们只关心俯仰角和偏航角,所以我们不会讨论滚转角。给定一个俯仰角和偏航角,我们可以把它们转换为一个代表新的方向向量的3D向量。

对于Pitch:

direction.y = sin(glm::radians(pitch)); // 注意我们先把角度转为弧度

direction.x = cos(glm::radians(pitch));

direction.z = cos(glm::radians(pitch));

对于Yaw:

direction.x = cos(glm::radians(yaw));

direction.z = sin(glm::radians(yaw));

把这两个合起来:

direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw));

direction.y = sin(glm::radians(pitch));

direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));

得到基于欧拉角的旋转

欧拉角的缺点:

  • 对两个朝向进行插值比较困难。简单地对X、Y、Z角度进行插值得到的结果不太理想。
  • 实施多次旋转很复杂且不精确:必须计算出最终的旋转矩阵,然后据此推测书欧拉角。
  • “臭名昭著”的“万向节死锁”(Gimbal Lock)问题有时会让旋转“卡死”。(无论什么样的旋转顺序如xyz,在某个轴旋转之后有可能使得另两个轴的旋转等效)其他一些奇异状态还会导致模型方向翻转。
  • 不同的角度可产生同样的旋转(例如-180°和180°)
  • 容易出错——如上所述,一般的旋转顺序是YZX,如果用了非YZX顺序的库,就有麻烦了。
  • 某些操作很复杂:如绕指定的轴旋转N角度。

四元数:

四元数能够轻松的解决上述欧拉角的问题

基本概念:

四元数由4个数[x y z w]构成,表示了如下的旋转:

// RotationAxis,顾名思义即旋转轴。RotationAngle是旋转的角度。

x = RotationAxis.x * sin(RotationAngle / 2)

y = RotationAxis.y * sin(RotationAngle / 2)

z = RotationAxis.z * sin(RotationAngle / 2)

w = cos(RotationAngle / 2)

---------------------------------------------------------博主:mx

根据w分量我们可以清除的知道旋转的角度,然后再根据xyz可以推出旋转轴

四元数的使用:

四元数的创建:

#include <glm/gtc/quaternion.hpp>

#include <glm/gtx/quaternion.hpp>

// Creates an identity quaternion (no rotation) quat MyQuaternion;

// Direct specification of the 4 components

// You almost never use this directly MyQuaternion = quat(w,x,y,z);

// Conversion from Euler angles (in radians) to Quaternion

vec3 EulerAngles(90, 45, 0);

MyQuaternion = quat(EulerAngles);

// Conversion from axis-angle

// In GLM the angle must be in degrees here, so convert it.

MyQuaternion=gtx::quaternion::angleAxis(degrees(RotationAngle), RotationAxis);

注意如果是在shader中,应该把四元数转换成旋转矩阵,顶点会一如既往地随着MVP矩阵的变化而旋转。

mat4 RotationMatrix = quaternion::toMat4(quaternion);

...

mat4 ModelMatrix = TranslationMatrix * RotationMatrix * ScaleMatrix;

// You can now use ModelMatrix to build the MVP matrix

旋转向量/点:

rotated_point = orientation_quaternion * point;

//向量也一样

对两个四元数进行插值:

SLERP意为球面线性插值(Spherical Linear intERPolation)、可以用GLM中的mix函数进行SLERP:

glm::quat interpolatedquat = quaternion::mix(quat1, quat2, 0.5f); // or whatever factor

两个旋转的累计:

只需将两个四元数相乘即可。顺序和矩阵乘法一致。亦即逆序相乘:

quat combined_rotation = second_rotation * first_rotation;

两个向量之间的旋转:

基本思路很简单:

  • 两向量间的夹角很好找:由点积可知其cos值。
  • 旋转轴很好找:两向量的叉乘积

---------------------------------------------------------博主:mx

quat RotationBetweenVectors(vec3 start, vec3 dest){

start = normalize(start);

dest = normalize(dest);

float cosTheta = dot(start, dest);

vec3 rotationAxis;

if (cosTheta < -1 + 0.001f){

// special case when vectors in opposite directions:

// there is no "ideal" rotation axis

// So guess one; any will do as long as it's perpendicular to start

rotationAxis = cross(vec3(0.0f, 0.0f, 1.0f), start);

if (gtx::norm::length2(rotationAxis) < 0.01 ) // bad luck, they were parallel, try again!

rotationAxis = cross(vec3(1.0f, 0.0f, 0.0f), start);

rotationAxis = normalize(rotationAxis);

return gtx::quaternion::angleAxis(180.0f, rotationAxis);

}

rotationAxis = cross(start, dest);

float s = sqrt( (1+cosTheta)*2 );

float invs = 1 / s;

return quat(

s * 0.5f,

rotationAxis.x * invs,

rotationAxis.y * invs,

rotationAxis.z * invs

);

}

---------------------------------------------------------博主:mx

四元数实现摄象机旋转:

正确的做法:

先基于完整最开始的世界坐标系,我们假设屏幕空间是在上面,然后我们求出原生front向量,到我们鼠标点击的点到原点的向量,求出这两个向量需要的旋转四元数。然后将这个四元数应用于目前的front向量上。

//之前写的是基于世界空间的求出来的四元数的旋转轴会出问题,会出现左右旋转一定角度上下旋转就有bug的问题,并且当Front接近WoldUp的时候会出现天旋地转的感觉(下面红色的代码是有问题的,蓝色的是我修改之后的)

求出两个向量之间的旋转

//glm::quat rot1 = RotationBetweenVectors(glm::vec3(0.0f, 0.0f, 1.0f),normalize(glm::vec3(0.008f * MouseSensitivity*xoffset, 0.008f * MouseSensitivity * yoffset, 1.0f)) );

    glm::vec3  ViewDest = Front + Right * xoffset * 0.008f * MouseSensitivity + Up * yoffset * 0.008f * MouseSensitivity;

    glm::quat rot1 = RotationBetweenVectors(Front, ViewDest);

将这个旋转四元数应用于当前的Front

Front = glm::normalize(glm::rotate(rot1, Front));

//Right = glm::normalize(glm::cross(Front, WorldUp));

 Right = glm::normalize(glm::cross(Front, Up));

Up = glm::normalize(glm::cross(Right, Front));

完整代码:

因为我尝试过三个方案所以旋转的代码有点多,上面四元数实现的旋转的函数名为updateCameraVectorsByQuat2

---------------------------------------------------------博主:mx

Shader:

#version460

layout(location =0) in vec3 aPos;

layout(location =1) in vec2 aTexCoord;

uniformfloat offset1;

uniformmat4 model;

uniformmat4 view;

uniformmat4 projection;

out vec2 TexCoord;

voidmain()

{

gl_Position =projection*view*model*vec4(aPos.x+offset1,-aPos.y,aPos.z,1.0f);

TexCoord = aTexCoord;

}

#version460

in vec2 TexCoord;

out vec4 FragColor;

uniformfloat offset2;

uniformsampler2D texture1;

uniformsampler2D texture2;

voidmain()

{

FragColor =mix(texture(texture1, TexCoord),texture(texture2,vec2(1-TexCoord.x,TexCoord.y)),0.8);

}

头文件:

#pragma once

#include <glad/glad.h>

#include <GLFW/glfw3.h>

#include "glm/glm.hpp"

#include "glm/gtc/matrix_transform.hpp"

#include "glm/gtc/type_ptr.hpp"

#include "glm/gtc/quaternion.hpp"

#include "glm/gtx/quaternion.hpp"

#define STB_IMAGE_IMPLEMENTATION

#include "stb_image.h"

代码:

#pragma once

#ifndef CAMERA_H

#define CAMERA_H

#include <glad/glad.h>

#include "glm/glm.hpp"

#include "glm/gtc/matrix_transform.hpp"

#include "glm/gtc/quaternion.hpp"

#include "glm/gtx/quaternion.hpp"

#include <vector>

enum class Camera_Movement {

FORWARD,

BACKWARD,

LEFT,

RIGHT

};

// Default camera values

const float YAW = -90.0f;

const float PITCH = 0.0f;

const float SPEED = 2.5f;

const float SENSITIVITY = 0.1f;

const float FOV = 45.0f;

class Camera

{

public:

// camera Attributes

glm::vec3 Position;

glm::vec3 Front;

glm::vec3 Up;

glm::vec3 Right;

glm::vec3 WorldUp;

glm::quat FrontQuaternion;

// euler Angles

float Yaw;

float Pitch;

// camera options

float MovementSpeed;

float MouseSensitivity;

float Fov;

// constructor with vectors

Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH);

// constructor with scalar values

Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch);

// returns the view matrix calculated using Euler Angles and the LookAt Matrix

glm::mat4 GetViewMatrix();

// processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)

void ProcessKeyboard(Camera_Movement direction, float deltaTime);

// processes input received from a mouse input system. Expects the offset value in both the x and y direction.

void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true);

// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis

void ProcessMouseScroll(float yoffset);

void updateCameraVectorsByQuat1(float xoffset, float yoffset);

void updateCameraVectorsByQuat2(float xoffset, float yoffset);

glm::quat RotationBetweenVectors(glm::vec3 start, glm::vec3 dest);

void ResetYawAndPitch();

private:

// calculates the front vector from the Camera's (updated) Euler Angles

void updateCameraVectors();

};

#endif

#include "Camera.h"

Camera::Camera(glm::vec3 position, glm::vec3 up, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Fov(FOV)

{

Position = position;

WorldUp = up;

Yaw = yaw;

Pitch = pitch;

updateCameraVectors();

}

Camera::Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Fov(FOV)

{

Position = glm::vec3(posX, posY, posZ);

WorldUp = glm::vec3(upX, upY, upZ);

Yaw = yaw;

Pitch = pitch;

updateCameraVectors();

}

glm::mat4 Camera::GetViewMatrix()

{

return glm::lookAt(Position, Position + Front, Up);

}

void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime)

{

float velocity = MovementSpeed * deltaTime;

if (direction == Camera_Movement::FORWARD)

Position += Front * velocity;

if (direction == Camera_Movement::BACKWARD)

Position -= Front * velocity;

if (direction == Camera_Movement::LEFT)

Position -= Right * velocity;

if (direction == Camera_Movement::RIGHT)

Position += Right * velocity;

}

void Camera::ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch)

{

xoffset *= MouseSensitivity;

yoffset *= MouseSensitivity;

Yaw += xoffset;

Pitch += yoffset;

// make sure that when pitch is out of bounds, screen doesn't get flipped

if (constrainPitch)

{

if (Pitch > 89.0f)

Pitch = 89.0f;

if (Pitch < -89.0f)

Pitch = -89.0f;

}

// update Front, Right and Up Vectors using the updated Euler angles

updateCameraVectors();

}

void Camera::ProcessMouseScroll(float yoffset)

{

Fov -= (float)yoffset;

if (Fov < 1.0f)

Fov = 1.0f;

if (Fov > 45.0f)

Fov = 45.0f;

}

/// <summary>

/// upadate CameraVector by Euler

/// </summary>

void Camera::updateCameraVectors()

{

// calculate the new Front vector

glm::vec3 front;

front.x = cos(glm::radians(Yaw))* cos(glm::radians(Pitch));

front.y = sin(glm::radians(Pitch));

front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));

/*

front.x = cos(glm::radians(Yaw));

front.y = sin(glm::radians(Pitch));

front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));

*/

Front = glm::normalize(front);

// also re-calculate the Right and Up vector

Right = glm::normalize(glm::cross(Front, WorldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.

Up = glm::normalize(glm::cross(Right, Front));

FrontQuaternion = glm::quat(Front);

}

/// <summary>

/// update CameraVector by Quat(Maybe have some question)

/// </summary>

/// <param name="xoffset"></param>

/// <param name="yoffset"></param>

/// <param name="constrainPitch"></param>

void Camera::updateCameraVectorsByQuat1( float xoffset, float yoffset)

{

Yaw += 90;

xoffset *= 0.008f*MouseSensitivity;

yoffset *= 0.008f * MouseSensitivity;

Yaw = xoffset;

Pitch= yoffset;

// make sure that when pitch is out of bounds, screen doesn't get flipped

if (Pitch > 90.0f)

Pitch -= 90.0f;

if (Pitch < -90.0f)

Pitch += 90.0f;

if (Yaw > 90.0f)

Yaw -= 90.0f;

if (Yaw < -90.0f)

Yaw += 90.0f;

glm::vec3 axis = glm::cross(Front, Up);

glm::quat pitchQuat = glm::angleAxis(Pitch, axis);

//determine heading quaternion from the camera up vector and the heading angle

axis = glm::cross(Front, axis);

glm::quat yawQuat = glm::angleAxis(Yaw, Up);

//add the two quaternions

glm::quat combinedRotation = pitchQuat*yawQuat;

Front = glm::rotate(combinedRotation, Front);

Front = glm::normalize(Front);

Right = glm::normalize(glm::cross(Front, WorldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.

Up = glm::normalize(glm::cross(Right, Front));

Yaw -= 90;

}

/// <summary>

/// update CameraVector by Quat (right)

/// </summary>

/// <param name="xoffset"></param>

/// <param name="yoffset"></param>

void Camera::updateCameraVectorsByQuat2(float xoffset, float yoffset)

{

//这个还是有问题的,因为求出来的四元数旋转轴是在世界空间的,会导致旋转一定角度后上下旋转会反过来,并且在Front接近WorldUp的时候会有天旋地转的感觉,所以这里我用/**/注释掉,更换新的标蓝色的

/*

// Find the rotation between the front of the object (that we assume towards +Z,

// but this depends on your model) and the desired direction

glm::quat rot1 = RotationBetweenVectors(glm::vec3(0.0f, 0.0f, 1.0f),normalize(glm::vec3(0.008f * MouseSensitivity*xoffset, 0.008f * MouseSensitivity * yoffset, 1.0f)) );

Front = glm::normalize(glm::rotate(rot1, Front));

Right = glm::normalize(glm::cross(Front, WorldUp)); // normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.

Up = glm::normalize(glm::cross(Right, Front));

*/

    glm::vec3  ViewDest = Front + Right * xoffset * 0.008f * MouseSensitivity + Up * yoffset * 0.008f * MouseSensitivity;

    glm::quat rot1 = RotationBetweenVectors(Front, ViewDest);

    Front = glm::normalize(glm::rotate(rot1, Front));
 
    Right = glm::normalize(glm::cross(Front, Up));
   
    Up = glm::normalize(glm::cross(Right, Front));

 

}

/// <summary>

/// get rotation between two vectors

/// </summary>

/// <param name="start"></param>

/// <param name="dest"></param>

/// <returns></returns>

glm::quat Camera::RotationBetweenVectors(glm::vec3 start, glm::vec3 dest)

{

start = normalize(start);

dest = normalize(dest);

float cosTheta = dot(start, dest);

glm::vec3 rotationAxis;

if (cosTheta < -1 + 0.001f) {

// special case when vectors in opposite directions:

// there is no "ideal" rotation axis

// So guess one; any will do as long as it's perpendicular to start

rotationAxis = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), start);

if (glm::length2(rotationAxis) < 0.01) // bad luck, they were parallel, try again!

rotationAxis = glm::cross(glm::vec3(1.0f, 0.0f, 0.0f), start);

rotationAxis = normalize(rotationAxis);

return glm::angleAxis(180.0f, rotationAxis);

}

rotationAxis = cross(start, dest);

float s = sqrt((1 + cosTheta) * 2);

float invs = 1 / s;

return glm::quat(

s * 0.5f,

rotationAxis.x * invs,

rotationAxis.y * invs,

rotationAxis.z * invs

);

}

void Camera::ResetYawAndPitch()

{

Yaw = YAW;

Pitch = PITCH;

}

#pragma once

#ifndef SHADER_H

#define SHADER_H

#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件

#include "glm/glm.hpp"

#include <string>

#include <fstream>

#include <sstream>

#include <iostream>

class Shader

{

public:

// 程序ID

unsigned int ID;

// 构造器读取并构建着色器

Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const char* geometryPath=nullptr);

~Shader();

// 使用/激活程序

void use(); // uniform工具函数

void setBool(const std::string& name, bool value)const;

void setInt(const std::string& name, int value)const;

void setFloat(const std::string& name, float value)const;

void setVec2(const std::string& name, const glm::vec2& value) const;

void setVec2(const std::string& name, float x, float y) const;

void setVec3(const std::string& name, const glm::vec3& value) const;

void setVec3(const std::string& name, float x, float y, float z) const;

void setVec4(const std::string& name, const glm::vec4& value) const;

void setVec4(const std::string& name, float x, float y, float z, float w) const;

void setMat2(const std::string& name, const glm::mat2& mat) const;

void setMat3(const std::string& name, const glm::mat3& mat) const;

void setMat4(const std::string& name, const glm::mat4& mat) const;

};

#endif

#include "Shader.h"

Shader::Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const char* geometryPath)

{

// 1. 从文件路径中获取顶点/片段着色器

std::string vertexCode;

std::string fragmentCode;

std::string geometryCode;

std::ifstream vShaderFile;

std::ifstream fShaderFile;

std::ifstream gShaderFile;

// 保证ifstream对象可以抛出异常:

vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);

fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);

gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);

try {

// 打开文件

vShaderFile.open(vertexPath);

fShaderFile.open(fragmentPath);

std::stringstream vShaderStream, fShaderStream; // 读取文件的缓冲内容到数据流中

vShaderStream << vShaderFile.rdbuf();

fShaderStream << fShaderFile.rdbuf();

// 关闭文件处理器

vShaderFile.close();

fShaderFile.close();

// 转换数据流到string

vertexCode = vShaderStream.str();

fragmentCode = fShaderStream.str();

unsigned int geometry;

if (geometryPath != nullptr)

{

gShaderFile.open(geometryPath);

std::stringstream gShaderStream;

gShaderStream << gShaderFile.rdbuf();

gShaderFile.close();

geometryCode = gShaderStream.str();

}

}

catch (std::ifstream::failure e)

{

std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;

}

const char* vShaderCode = vertexCode.c_str();

const char* fShaderCode = fragmentCode.c_str();

// 2. 编译着色器

unsigned int vertex, fragment;

int success;

char infoLog[512];

// 顶点着色器

vertex = glCreateShader(GL_VERTEX_SHADER);

glShaderSource(vertex, 1, &vShaderCode, NULL);

glCompileShader(vertex);

// 打印编译错误(如果有的话)

glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);

if (!success)

{

glGetShaderInfoLog(vertex, 512, NULL, infoLog); std::cout << "ERROR::SHADER::vertex::COMPILATION_FAILED\n" << infoLog << std::endl;

};

// 片段着色器

fragment = glCreateShader(GL_FRAGMENT_SHADER);

glShaderSource(fragment, 1, &fShaderCode, NULL);

glCompileShader(fragment);

// 打印编译错误(如果有的话)

glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);

if (!success)

{

glGetShaderInfoLog(fragment, 512, NULL, infoLog); std::cout << "ERROR::SHADER::fragment::COMPILATION_FAILED\n" << infoLog << std::endl;

};

//几何着色器

unsigned int geometry;

if (geometryPath != nullptr)

{

const char* gShaderCode = geometryCode.c_str();

geometry = glCreateShader(GL_GEOMETRY_SHADER);

glShaderSource(geometry, 1, &gShaderCode, NULL);

glCompileShader(geometry);

glGetShaderiv(geometry, GL_COMPILE_STATUS, &success);

if (!success)

{

glGetShaderInfoLog(geometry, 512, NULL, infoLog); std::cout << "ERROR::SHADER::fragment::COMPILATION_FAILED\n" << infoLog << std::endl;

};

}

// 着色器程序

ID = glCreateProgram();

glAttachShader(ID, vertex);

glAttachShader(ID, fragment);

if (geometryPath != nullptr)

glAttachShader(ID, geometry);

glLinkProgram(ID);

// 打印连接错误(如果有的话)

glGetProgramiv(ID, GL_LINK_STATUS, &success);

if(!success)

{

glGetProgramInfoLog(ID, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;

}

// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了

glDeleteShader(vertex);

glDeleteShader(fragment);

if (geometryPath != nullptr)

glDeleteShader(geometry);

}

Shader::~Shader()

{

glDeleteProgram(ID);

}

void Shader::use()

{

glUseProgram(ID);

}

void Shader::setBool(const std::string& name, bool value) const

{

glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);

}

void Shader::setInt(const std::string& name, int value) const

{

glUniform1i(glGetUniformLocation(ID, name.c_str()), value);

}

void Shader::setFloat(const std::string& name, float value) const

{

glUniform1f(glGetUniformLocation(ID, name.c_str()), value);

}

// ------------------------------------------------------------------------

void Shader::setVec2(const std::string& name, const glm::vec2& value) const

{

glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);

}

void Shader::setVec2(const std::string& name, float x, float y) const

{

glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);

}

// ------------------------------------------------------------------------

void Shader::setVec3(const std::string& name, const glm::vec3& value) const

{

glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);

}

void Shader::setVec3(const std::string& name, float x, float y, float z) const

{

glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);

}

// ------------------------------------------------------------------------

void Shader::setVec4(const std::string& name, const glm::vec4& value) const

{

glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);

}

void Shader::setVec4(const std::string& name, float x, float y, float z, float w) const

{

glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);

}

// ------------------------------------------------------------------------

void Shader::setMat2(const std::string& name, const glm::mat2& mat) const

{

glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);

}

// ------------------------------------------------------------------------

void Shader::setMat3(const std::string& name, const glm::mat3& mat) const

{

glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);

}

// ------------------------------------------------------------------------

void Shader::setMat4(const std::string& name, const glm::mat4& mat) const

{

glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);

}

#pragma once

#include"header.h"

#include"Shader.h"

#include"Camera.h"

#include <iostream>

using namespace std;

const unsigned int SCR_WIDTH = 1920;

const unsigned int SCR_HEIGHT = 1080;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);

void processInput(GLFWwindow* window);

void mouse_callback(GLFWwindow* window, double xposIn, double yposIn);

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));

float lastX = SCR_WIDTH / 2.0f;

float lastY = SCR_HEIGHT / 2.0f;

bool firstMouse = true;

// timing

float deltaTime = 0.0f; // time between current frame and last frame

float lastFrame = 0.0f;

int main()

{

glfwInit();

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);

glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);//mac下使用

GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

if (window == NULL)

{

cout << " Failed to create GLFW window" << endl;

glfwTerminate();

return -1;

}

glfwMakeContextCurrent(window);

glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

glfwSetCursorPosCallback(window, mouse_callback);

glfwSetScrollCallback(window, scroll_callback);

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))

{

std::cout << "Failed to initialize GLAD" << std::endl;

return -1;

}

glViewport(0, 0, 1920, 1080);

glEnable(GL_DEPTH_TEST);

Shader ourShader("D:\\LearnOpenGL\\OpenGLProject\\Learning1\\Shader\\Vertex.txt", "D:\\LearnOpenGL\\OpenGLProject\\Learning1\\Shader\\Fragment.txt");

float vertices[] = {

-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,

0.5f, -0.5f, -0.5f, 1.0f, 0.0f,

0.5f, 0.5f, -0.5f, 1.0f, 1.0f,

0.5f, 0.5f, -0.5f, 1.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

0.5f, -0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 1.0f,

0.5f, 0.5f, 0.5f, 1.0f, 1.0f,

-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, -0.5f, 1.0f, 1.0f,

0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

0.5f, -0.5f, -0.5f, 1.0f, 1.0f,

0.5f, -0.5f, 0.5f, 1.0f, 0.0f,

0.5f, -0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,

0.5f, 0.5f, -0.5f, 1.0f, 1.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,

-0.5f, 0.5f, -0.5f, 0.0f, 1.0f

};

unsigned int indices[] = {

0, 1, 3, // first triangle

1, 2, 3 // second triangle

};

glm::vec3 cubePositions[] = {

glm::vec3(0.0f, 0.0f, 0.0f),

glm::vec3(2.0f, 5.0f, -15.0f),

glm::vec3(-1.5f, -2.2f, -2.5f),

glm::vec3(-3.8f, -2.0f, -12.3f),

glm::vec3(2.4f, -0.4f, -3.5f),

glm::vec3(-1.7f, 3.0f, -7.5f),

glm::vec3(1.3f, -2.0f, -2.5f),

glm::vec3(1.5f, 2.0f, -2.5f),

glm::vec3(1.5f, 0.2f, -1.5f),

glm::vec3(-1.3f, 1.0f, -1.5f)

};

unsigned int VBO, EBO;

unsigned int VAO;

glGenVertexArrays(1, &VAO);

glBindVertexArray(VAO);

glGenBuffers(1, &VBO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

//glGenBuffers(1, &EBO);

//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// 位置属性

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);

glEnableVertexAttribArray(0);

颜色属性

//glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

//glEnableVertexAttribArray(1);

// texture coord attribute

glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

glEnableVertexAttribArray(1);

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindVertexArray(0);

//生成纹理

unsigned int texture;

glGenTextures(1, &texture);

glBindTexture(GL_TEXTURE_2D, texture);

// 为当前绑定的纹理对象设置环绕、过滤方式

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// 加载并生成纹理

int width, height, nrChannels;

unsigned char* data = stbi_load("D:\\LearnOpenGL\\Texture\\wall.jpg", &width, &height, &nrChannels, 0);

if (data)

{

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

glGenerateMipmap(GL_TEXTURE_2D);

}

else

{

std::cout << "Failed to load texture" << std::endl;

}

stbi_image_free(data);

unsigned int texture2;

glGenTextures(1, &texture2);

glBindTexture(GL_TEXTURE_2D, texture2);

// 为当前绑定的纹理对象设置环绕、过滤方式

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

data = stbi_load("D:\\LearnOpenGL\\Texture\\ganyu.jpg", &width, &height, &nrChannels, 0);

if (data)

{

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

glGenerateMipmap(GL_TEXTURE_2D);

}

else

{

std::cout << "Failed to load texture" << std::endl;

}

stbi_image_free(data);

ourShader.use();

ourShader.setInt("texture1", 0);

ourShader.setInt("texture2", 1);

float timeValue = 0;

int UniformHandle1;

int UniformHandle2;

// 渲染循环

while (!glfwWindowShouldClose(window))

{

float currentFrame = static_cast<float>(glfwGetTime());

deltaTime = currentFrame - lastFrame;

lastFrame = currentFrame;

// 输入

processInput(window);

// 渲染指令

glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

ourShader.use();

ourShader.setFloat("offset2", abs(cos(glfwGetTime())));

//glm::mat4 model = glm::mat4(1.0f); // make sure to initialize matrix to identity matrix first

glm::mat4 view = camera.GetViewMatrix();

//glm::mat4 view = glm::toMat4(camera.FrontQuaternion);

glm::mat4 projection = glm::mat4(1.0f);

//model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));

view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

projection = glm::perspective(glm::radians(camera.Fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

//ourShader.setMat4("model", model);

ourShader.setMat4("view", view);

ourShader.setMat4("projection", projection);

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, texture);

glActiveTexture(GL_TEXTURE1);

glBindTexture(GL_TEXTURE_2D, texture2);

glBindVertexArray(VAO);

for (unsigned int i = 0; i < 10; i++)

{

// calculate the model matrix for each object and pass it to shader before drawing

glm::mat4 model = glm::mat4(1.0f);

model = glm::translate(model, cubePositions[i]);

float angle = 20.0f * (i+1);

model = glm::rotate(model, glm::radians(angle*(float)glfwGetTime()), glm::vec3(1.0f, 0.3f, 0.5f));

ourShader.setMat4("model", model);

glDrawArrays(GL_TRIANGLES, 0, 36);

}

// 检查并调用事件,交换缓冲

glfwSwapBuffers(window);

glfwPollEvents();

}

glDeleteBuffers(1, &VBO);

glDeleteVertexArrays(1, &VAO);

//glDeleteBuffers(1, &EBO);

glfwTerminate();

return 0;

}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)

{

glViewport(0, 0, width, height);

}

void processInput(GLFWwindow* window)

{

if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)

glfwSetWindowShouldClose(window, true);

if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)

camera.ProcessKeyboard(Camera_Movement::FORWARD, deltaTime);

if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)

camera.ProcessKeyboard(Camera_Movement::BACKWARD, deltaTime);

if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)

camera.ProcessKeyboard(Camera_Movement::LEFT, deltaTime);

if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)

camera.ProcessKeyboard(Camera_Movement::RIGHT, deltaTime);

}

void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)

{

if (glfwGetMouseButton(window, 0))

{

float xpos = static_cast<float>(xposIn);

float ypos = static_cast<float>(yposIn);

if (firstMouse)

{

lastX = xpos;

lastY = ypos;

firstMouse = false;

}

float xoffset = lastX - xpos;

float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

lastX = xpos;

lastY = ypos;

// camera.updateCameraVectorsByQuat1(-xoffset, -yoffset);

camera.updateCameraVectorsByQuat2(-xoffset, yoffset);

}

else

{

lastX = SCR_WIDTH / 2.0f;

lastY = SCR_HEIGHT / 2.0f;

}

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)

{

camera.ProcessMouseScroll(static_cast<float>(yoffset));

}

学习资料:

1.摄像机 - LearnOpenGL CN

2.https://github.com/cybercser/OpenGL_3_3_Tutorial_Translation/blob/master/Tutorial%2017%20Rotations.md

---------------------------------------------------------博主:mx

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_56399931/article/details/122762617

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文