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

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签