LearnGL - 學習筆記目錄
本人才疏學淺,如有什麼錯誤,望不吝指出。
上些篇:
- LearnGL - 08 - Camera - linmath.h 版,瞭解OpenGL 的一些幾何變換,其中包含了攝像機的封裝。
- LearnGL - 08.0.1 - Camera - GLM版前置篇,瞭解 GLM 的簡單介紹,是很適合 OpenGL 數學庫使用的。
這一篇:我們還是實現一樣的功能,只不過是:GLM 線性數學庫版本的(這個庫不有線性代數庫,也包含了其他的)。
順便一提:UE5 中的 Nanite 技術,虛擬多邊形,官方暫時也未公佈技術細節,說明幾何體可以不限制頂點數量、多邊形的數量,可以做到非常密集的的數量,密集到一個像素還可以容納到數個多邊形。
目前我也完全不知道是怎麼個思路,TQL。
多人認爲 Nanite 是用 Mesh Shader 來實現的::如何評價虛幻5引擎的Nanite虛擬微多邊形幾何體技術?。
技術不斷在升級改革(科技服務在升級、在變化)。
唯獨不變的還是數學的內容,所以說,數學的魅力有多大(運算模型不變)。
算術 - my_math_util.h
做了一些調整,添加了 glm 使用的向量、矩陣的類型using別名。
// my_math_util.h
/* author : jave.lin
數學工具,使用的向量、矩陣類型定義
*/
#ifndef _MY_MATH_UTIL__H_
#define _MY_MATH_UTIL__H_
#include<stdlib.h>
//#define MY_MATH_UTIL_LINMATH_H
//#define MY_MATH_UTIL_GLM_H
#ifdef MY_MATH_UTIL_GLM_H
#include"glm/glm.hpp"
#include"glm/gtc/matrix_transform.hpp"
using vec2 = glm::vec2;
using vec3 = glm::vec3;
using vec4 = glm::vec4;
using mat4 = glm::mat4;
#define PIf glm::pi<glm::float32_t>()
#define PId glm::pi<glm::float64_t>()
// 角度轉弧度
template<typename T>
inline constexpr T D2R(T v) {
return (glm::radians<T>(v));
}
// 弧度轉角度
template<typename T>
inline constexpr T R2D(T v) {
return (glm::degrees<T>(v));
}
#else // MY_MATH_UTIL_LINMATH_H
#include"linmath.h"
//using vec3 = vec3;
#define PI 3.14159265359f
// 角度轉弧度
#define D2R(v) (v * (float) PI / 180.f)
// 弧度轉角度
#define R2D(v) (v * (float) 180.f / PI)
// vec3 賦值
void set_vec3_by_scalar(vec3 vector3, float v1, float v2, float v3) {
vector3[0] = v1;
vector3[1] = v2;
vector3[2] = v3;
}
void set_vec3_by_vec(vec3 a, vec3 b) {
a[0] = b[0];
a[1] = b[1];
a[2] = b[2];
}
#endif
#endif
相機類 - my_camera_glm.h
使用起來比 linmath.h 的舒服很多。
GLM 唯一不太好的是,目前這個版本還是沒有對原始的 vec, mat 的引用(指針)對象來修改數據,如:identity,是返回新的一個棧上的臨時成員,找不到 identity(glm::mat& mat)
或是 identity(glm::mat* mat)
// my_camera_glm.h
/* author : jave.lin
鏡頭/攝像機類 glm 版本的
Reference/參考:https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
*/
#ifndef _MY_CAMERA__H_
#define _MY_CAMERA__H_
#include"glm/glm.hpp"
#include"glm/gtc/matrix_transform.hpp"
#include"my_math_util.h"
namespace my_util {
// 定義一些鏡頭可能移動選項。
enum Camera_Movement {
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// 默認的鏡頭參數值
const float C_YAW = -90.0f;
const float C_PITCH = 0.0f;
const float C_SPEED = 2.5f;
const float C_SENSITIVITY = 0.1f;
const float C_FOV = 45.0f;
const float C_ASPECT = 1.0f;
const float C_NEAR = 0.1f;
const float C_FAR = 100.0f;
const float C_SIZE = 500; // near/far plane 的高的一半,例如,1000 的高,1000 / 2 = 500
// 相機/攝像機/鏡頭數據類
class Camera {
public:
// 鏡頭屬性
vec3 mPosition; // 鏡頭的位置
vec3 mFront; // 鏡頭前方向(透鏡正方向)
vec3 mUp; // 鏡頭上方向
vec3 mRight; // 鏡頭右方向
vec3 mWorldUp; // 參考的世界 “上/頂” 的方向
// 鏡頭選項
float mFov; // field of view
float mAspect; // the ratio of width / height to viewport
float mNear; // distance of near plane
float mFar; // distance of far palne
float mSize; // 正交用,
// 使用向量的構造函數,mFront 默認爲:{ 0.0f, 0.0f, -1.0f }
Camera(
vec3 pos = vec3(0.0, 0.0, 0.0), vec3 worldUp = vec3(0.0f, 1.0f, 0.0f), float fov = C_FOV, float aspect = C_ASPECT,
float n = C_NEAR, float f = C_FAR, // 這裏不能用near,far於window sdk的minwindef的宏定義有重名
float size = C_SIZE);
// 使用標量的構造函數
Camera(
float posX = 0.0f, float posY = 0.0f, float posZ = 0.0f, float upX = 0.0f, float upY = 1.0f, float upZ = 0.0f,
float fov = C_FOV, float aspect = C_ASPECT,
float n = C_NEAR, float f = C_FAR, // 這裏不能用near,far於window sdk的minwindef的宏定義有重名
float size = C_SIZE);
};
Camera::Camera(
vec3 pos, vec3 worldUp, float fov, float aspect,
float n, float f, // 這裏不能用near,far於window sdk的minwindef的宏定義有重名
float size)
:
mPosition(pos),
mWorldUp(worldUp),
mFront(vec3(0.0f, 0.0f, -1.0f)),
mFov(fov),
mAspect(aspect),
mNear(n),
mFar(f),
mSize(size)
{
}
Camera::Camera(
float posX, float posY, float posZ, float upX, float upY, float upZ,
float fov, float aspect,
float n, float f, // 這裏不能用near,far於window sdk的minwindef的宏定義有重名
float size)
:
mPosition(vec3(posX, posY, posZ)),
mWorldUp(vec3(upX, upY, upZ)),
mFront(vec3(0.0f, 0.0f, -1.0f)),
mFov(fov),
mAspect(aspect),
mNear(n),
mFar(f),
mSize(size)
{
}
// 相機/鏡頭控制類
class CameraController {
public:
CameraController(Camera* cam, float yaw = C_YAW, float pitch = C_PITCH);
~CameraController();
// 獲取視圖矩陣
inline mat4 getViewMatrix();
// 獲取投影矩陣
inline mat4 getProjectionMatrix();
// 處理鍵盤輸入。
void processKeyboard(Camera_Movement direction, float deltaTime);
// 處理鼠標輸入:鼠標移動的 x 和 y 方向。
void processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true);
// 處理鼠標滾輪輸入。僅處理垂直滾輪的輸入。
void processMouseScroll(float yoffset);
// 歐拉角
float mYaw;
float mPitch;
// 鏡頭選項
float mMovementSpeed;
float mMouseSensitivity;
// 控制的鏡頭對象
Camera* mCam;
// 是否正交相機
bool isOrthorgrahic = false;
private:
// 根據鏡頭的歐拉角計算出來前、右、上向量
void updateCameraVectors();
// 獲取透視矩陣
inline mat4 getPerspectiveMatrix();
// 獲取正交矩陣
inline mat4 getOrthographicMatrix();
};
CameraController::CameraController(Camera* cam, float yaw, float pitch)
:
mCam(cam),
mYaw(yaw),
mPitch(pitch),
mMovementSpeed(C_SPEED),
mMouseSensitivity(C_SENSITIVITY)
{
updateCameraVectors();
}
CameraController::~CameraController() {
this->mCam = NULL;
}
inline mat4 CameraController::getViewMatrix() {
return glm::lookAt(mCam->mPosition, mCam->mPosition + mCam->mFront, mCam->mUp);
}
inline mat4 CameraController::getProjectionMatrix() {
if (isOrthorgrahic) return getOrthographicMatrix();
else return getPerspectiveMatrix();
}
void CameraController::processKeyboard(Camera_Movement direction, float deltaTime) {
vec3 value;
switch (direction)
{
case my_util::FORWARD:
value = mCam->mFront;
break;
case my_util::BACKWARD:
value = -mCam->mFront;
break;
case my_util::LEFT:
value = -mCam->mRight;
break;
case my_util::RIGHT:
value = mCam->mRight;
break;
default:
throw "direciton unhandle : " + direction;
break;
}
value *= mMovementSpeed * deltaTime;
mCam->mPosition += value;
}
void CameraController::processMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch) {
xoffset *= mMouseSensitivity;
yoffset *= mMouseSensitivity;
mYaw += xoffset;
mPitch += yoffset;
// 確保pitch(沿着x軸旋轉)超出了邊界,因爲屏幕不能翻轉
if (constrainPitch) {
if (mPitch > 89.0f) mPitch = 89.0f;
if (mPitch < -89.0f) mPitch = -89.0f;
}
// 使用歐拉角更新 前、右 和 上 向量
updateCameraVectors();
}
void CameraController::processMouseScroll(float yoffset) {
float fov = mCam->mFov - yoffset;
if (fov < 1.0f)
fov = 1.0f;
if (fov > 60.0f)
fov = 60.0f;
mCam->mFov = fov;
}
void CameraController::updateCameraVectors() {
// 計算前向量
// method 1:
vec3 front;
front.x = cos(D2R(mYaw));
front.y = sin(D2R(mPitch));
front.z = sin(D2R(mYaw));
mCam->mFront = glm::normalize(front);
// method 2:
//vec3 front;
//front.x = cos(D2R(mYaw)) * cos(D2R(mPitch));
//front.y = sin(D2R(mPitch));
//front.z = sin(D2R(mYaw)) * cos(D2R(mPitch));
//mCam->mFront = glm::normalize(front);
// 也需要重新計算 右 和 上 向量
mCam->mRight = glm::normalize(glm::cross(mCam->mFront, mCam->mWorldUp)); // 需要單位化(歸一化),因爲cross叉乘的參數都是歸一化,但計算出來的不一定是單位向量
mCam->mUp = glm::normalize(glm::cross(mCam->mRight, mCam->mFront)); // 需要單位化(歸一化),因爲cross叉乘的參數都是歸一化,但計算出來的不一定是單位向量
}
inline mat4 CameraController::getPerspectiveMatrix() {
return glm::perspective(D2R(mCam->mFov), mCam->mAspect, mCam->mNear, mCam->mFar);
}
inline mat4 CameraController::getOrthographicMatrix() {
float top = mCam->mSize;
float bottom = -top;
float right = -(top * mCam->mAspect);
float left = -right;
return glm::ortho(left, right, bottom, top, mCam->mNear, mCam->mFar);
}
}
#endif
使用
重載了算術運算符用起來很方便。
void _stdcall OnUpdateCallback() {
cam->mAspect = (GLfloat)win_width / (GLfloat)win_height;
// check key status
if (isPressKey(GLFW_KEY_W)) {
camCtrl->processKeyboard(FORWARD, Timer::Get()->scaledDeltaTime);
}
if (isPressKey(GLFW_KEY_S)) {
camCtrl->processKeyboard(BACKWARD, Timer::Get()->scaledDeltaTime);
}
if (isPressKey(GLFW_KEY_A)) {
camCtrl->processKeyboard(LEFT, Timer::Get()->scaledDeltaTime);
}
if (isPressKey(GLFW_KEY_D)) {
camCtrl->processKeyboard(RIGHT, Timer::Get()->scaledDeltaTime);
}
glClearColor(0.1f, 0.2f, 0.1f, 0.f); // 設置清理顏色緩存時,填充的顏色值
glClearDepthf(1.0f); // 設置清理深度緩存時,填充的深度值
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清理顏色緩存 與 深度緩存
mat4 tMat, rMat, sMat; // model matrix == TRS matrix
mat4 mvpMat, mMat, vMat, pMat;
vec4 time; // 傳入shader的uniform vec4 _Time;
time[0] = Timer::Get()->time;
time[1] = Timer::Get()->deltaTime;
time[2] = Timer::Get()->scaledDeltaTime;
time[3] = Timer::Get()->preseveTime;
shaderProgram->use(); // 使用此着色器程序
// system uniform
shaderProgram->setVec4("_Time", time[0], time[1], time[2], time[3]); // time info
vMat = camCtrl->getViewMatrix();
pMat = camCtrl->getProjectionMatrix();
shaderProgram->setInt("main_tex", 0); // 主 紋理設置採樣器採樣 0 索引紋理單元
shaderProgram->setInt("mask_tex", 1); // 遮罩 紋理設置採樣器採樣 1 索引紋理單元
shaderProgram->setInt("flash_light_tex", 2); // 閃光/流光 紋理設置採樣器採樣 2 索引紋理單元
shaderProgram->setMatrix4x4("vMat", glm::value_ptr(vMat)); // view matrix
shaderProgram->setMatrix4x4("pMat", glm::value_ptr(pMat)); // projection matrix
glBindVertexArray(vertex_array_object); // 先繪製 VAO[0] 的 VBO,EBO,VAF,ENABLED
int objCount = sizeof(offsets) / (sizeof(offsets[0]) * 3); // 每三個便宜點爲一個對象
for (size_t i = 0; i < objCount; i++) {
GLfloat scale = scales[i];
GLfloat rot_scaled = rots[i];
GLfloat offset_x = offsets[i * 3 + 0];
GLfloat offset_y = offsets[i * 3 + 1];
GLfloat offset_z = offsets[i * 3 + 2];
sMat = glm::identity<mat4>();
rMat = glm::identity<mat4>();
tMat = glm::identity<mat4>();
sMat = glm::scale(sMat, vec3(scale));
rMat = glm::rotate(rMat, Timer::Get()->time * PIf * 0.5f * rot_scaled, vec3(0.0, 1.0f, 0.0f));
rMat = glm::rotate(rMat, Timer::Get()->time * PIf * 0.25f * rot_scaled, vec3(1.0, 0.0f, 0.0f));
rMat = glm::rotate(rMat, Timer::Get()->time * PIf * 0.125f * rot_scaled, vec3(0.0, 0.0f, 1.0f));
tMat = glm::translate(tMat, vec3(offset_x, offset_y, offset_z));
mMat = tMat * rMat * sMat; // model matrix == TRS(translate, rotate, scale)
shaderProgram->setMatrix4x4("mMat", glm::value_ptr(mMat)); // model matrix
glDrawElements(GL_TRIANGLES, sizeof(vertices) / sizeof(vertices[0]), GL_UNSIGNED_INT, (GLvoid*)(0)); // 繪製 36 個索引對應的頂點
}
}
運行效果一直,就不發了。