文章目錄
LearnGL - 學習筆記目錄
本人才疏學淺,如有什麼錯誤,望不吝指出。
上一篇:LearnGL - 07 - DrawCube - 旋轉Cube、深度緩存/測試,瞭解如何繪製一個Cube立方體、旋轉3D 幾何體、深度緩存、深度測試都了一些基礎的認識。
這一篇搬磚:矩陣來實現 攝像機/鏡頭 類,便於測試項目中對鏡頭的:位移,和部分軸向的旋轉。
看了一下 攝像機 這篇實現的夠詳細的,再搬磚一下。
幾何變換
之前的文章有將到,我們傳入的頂點數據通常是在 對象空間 的,就是在 3D 建模軟件是的座標。
這些頂點座標,最終會顯示在屏幕上,中間會經歷好幾個座標空間(座標系)的變換:
總覽變換
也可以參考我之前的:OpenGL Transformation 幾何變換的順序概要(MVP,NDC,Window座標變換過程),這裏再次簡單描述一下
Local to World
從 對象空間 轉到 世界空間 一般是每個繪製對象都可能不一樣的變換數據,也就是中間那個:Model Matrix 相對於每一個繪製對象來說,都可能是不一樣的,這其中包含了:縮放、旋轉、平移。
World to View
這個一般是 攝像機 控制的變換,這裏頭的變換一般只有:旋轉、平移。注意我們的World To View的座標系是 右手座標系的,即:Z軸正方向是從屏幕指向你的眼睛的方向。
View to Projection
這個一般也是由 攝像機 控制的變換,這裏頭的變換一般只有:是隻有軸的縮放。它是負責將3D空間的座標變換到 4D 的齊次座標,並調整4D 座標的第四個分量以便後續做透視除法後投影到 NDC 而是用的。
Projection to NDC
這個除法處理,一般來說不需要我們處理,我們只要在 VS(Vertex Shader,頂點着色器)將座標從 Object Space 通過 MVP(Model, View, Projection)變換後調整到用於投影用的空間:Clip Space(這裏一般被稱作:裁剪空間)。
OpenGL 的底層實現 會將
的到的 NDC 座標後,就會下一個階段,直到傳到 光柵階段 後,光柵器 會將圖元生成插值片元。
便於後續的 片元着色器 用。
NDC to Screen
上面得到的 NDC 座標後,光柵器生成片元后,在傳給片元着色器之前,它會先將片元的座標轉換到屏幕座標。
NDC的x,y範圍是-1~1的,OpenGL 中的NDC.z也是-1~1的,DX的NDC.z是 0~1。
OpenGL 會將 NDC 座標通過 glViewport 設置的x,y,w,h來定位於屏幕的座標。
NDC to Screen 也是 OpenGL 底層實現的。不需要我們處理。
相機類 - my_camera.h
這篇我們主要是實現 攝像機 的功能,也就是 :WorldToView、ViewToProjection 的變換矩陣我們要生成用於實現攝像機的定位,與投影。
首先是一個純數據:Camera
類,然後是 CameraController
攝像機控制類:
CameraController
提供簡單的自由視角操作:
- 鏡頭視角的左右上下旋轉
- 鏡頭根據視角方向左右上下的平移
文章標題爲何叫 linmath.h 版,因爲我們的線性代數(矩陣、向量、四元數)使用的是 GLFW的 linmath.h 頭文件中帶有的功能:github glfw/deps/linmath.h、後續我會再使用一個 GLM 的版本,因爲 linmath.h 的太簡潔,vec2,3,4, matrix沒有封裝都只是float數組,並提供vec, matrix的方法而已。所以再一些使用方法中,如果需要對參數爲:vec,matrix的賦值會相當的麻煩,特別是默認值,只能是 NULL
,所以後續還是使用 GLM。
// my_camera.h
/* author : jave.lin
鏡頭/攝像機類
Reference/參考:https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/camera.h
*/
#ifndef _MY_CAMERA__H_
#define _MY_CAMERA__H_
#include"linmath.h"
#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 = 10.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 }
// @mPosition 如果 == NULL,則默認爲:{ 0.0f, 0.0f, 0.0f }
// @up 如果 == NULL,則默認爲:{ 0.0f, 1.0f, 0.0f }
Camera(
vec3 pos = NULL, vec3 up = NULL, 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 up, float fov, float aspect,
float n, float f, // 這裏不能用near,far於window sdk的minwindef的宏定義有重名
float size)
:
mFov(fov),
mAspect(aspect),
mNear(n),
mFar(f),
mSize(size)
{
if (mPosition == NULL) {
set_vec3_by_scalar(mPosition, 0.0f, 0.0f, 0.0f);
}
else {
set_vec3_by_vec(mPosition, mPosition);
}
if (up == NULL) {
set_vec3_by_scalar(mWorldUp, 0.0f, 1.0f, 0.0f);
}
else {
set_vec3_by_vec(mWorldUp, up);
}
set_vec3_by_scalar(mFront, 0.0f, 0.0f, -1.0f);
}
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)
:
mFov(fov),
mAspect(aspect),
mNear(n),
mFar(f),
mSize(size)
{
set_vec3_by_scalar(mPosition, posX, posY, posZ);
set_vec3_by_scalar(mWorldUp, upX, upY, upZ);
set_vec3_by_scalar(mFront, 0.0f, 0.0f, -1.0f);
}
// 相機/鏡頭控制類
class CameraController {
public:
CameraController(Camera* cam, float yaw = C_YAW, float pitch = C_PITCH);
~CameraController();
// 獲取視圖矩陣
void getViewMatrix(mat4x4 result);
// 獲取投影矩陣
void getProjectionMatrix(mat4x4 result);
// 處理鍵盤輸入。
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();
// 獲取透視矩陣
void getPerspectiveMatrix(mat4x4 result);
// 獲取正交矩陣
void getOrthographicMatrix(mat4x4 result);
};
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;
}
void CameraController::getViewMatrix(mat4x4 result) {
vec3 target;
vec3_add(target, mCam->mPosition, mCam->mFront);
mat4x4_look_at(result, mCam->mPosition, target, mCam->mUp);
}
void CameraController::getProjectionMatrix(mat4x4 result) {
if (isOrthorgrahic) getOrthographicMatrix(result);
else getPerspectiveMatrix(result);
}
void CameraController::processKeyboard(Camera_Movement direction, float deltaTime) {
float velocity = mMovementSpeed * deltaTime;
vec3 value;
if (direction == FORWARD) {
vec3_scale(value, mCam->mFront, velocity);
vec3_add(mCam->mPosition, mCam->mPosition, value);
}
if (direction == BACKWARD) {
vec3_scale(value, mCam->mFront, velocity);
vec3_sub(mCam->mPosition, mCam->mPosition, value);
}
if (direction == LEFT) {
vec3_scale(value, mCam->mRight, velocity);
vec3_sub(mCam->mPosition, mCam->mPosition, value);
}
if (direction == RIGHT) {
vec3_scale(value, mCam->mRight, velocity);
vec3_add(mCam->mPosition, 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:
vec4 front;
front[0] = cos(D2R(mYaw));
front[1] = sin(D2R(mPitch));
front[2] = sin(D2R(mYaw));
vec3_norm(mCam->mFront, front);
// method 2:
//vec3 front;
//front[0] = cos(D2R(mYaw)) * cos(D2R(mPitch));
//front[1] = sin(D2R(mPitch));
//front[2] = sin(D2R(mYaw)) * cos(D2R(mPitch));
//vec3_norm(mCam->mFront, front);
// 也需要重新計算 右 和 上 向量
vec3_mul_cross(mCam->mRight, mCam->mFront, mCam->mWorldUp);
vec3_norm(mCam->mRight, mCam->mRight); // 需要單位化(歸一化),因爲cross叉乘的參數都是歸一化,但計算出來的不一定是單位向量
vec3_mul_cross(mCam->mUp, mCam->mRight, mCam->mFront);
vec3_norm(mCam->mUp, mCam->mUp);
}
void CameraController::getPerspectiveMatrix(mat4x4 result) {
// 有BUG,暫時不用
//mat4x4_identity(result);
//float top = mCam->mSize;
//float bottom = -top;
//float right = -(top * mCam->mAspect);
//float left = -right;
//mat4x4_frustum(result, left, right, bottom, top, mCam->mNear, mCam->mFar);
mat4x4_perspective(result, D2R(mCam->mFov), mCam->mAspect, mCam->mNear, mCam->mFar);
}
void CameraController::getOrthographicMatrix(mat4x4 result) {
// 有BUG,暫時不用
mat4x4_identity(result);
float top = mCam->mSize;
float bottom = -top;
float right = -(top * mCam->mAspect);
float left = -right;
mat4x4_ortho(result, left, right, bottom, top, mCam->mNear, mCam->mFar);
}
}
#endif
時間類 - my_timer.h
後面的動畫內容需要用到統一的時間管理封裝,縮寫弄了:my_timer.h
// my_gltime.h
/* author : jave.lin
時間管理類
*/
#ifndef _MY_TIMER__H_
#define _MY_TIMER__H_
#include"glad/glad.h"
#include"GLFW/glfw3.h"
namespace my_util {
class Timer;
namespace internal_ns { // 內部專用 namepsace
Timer* g_Timer = NULL;
}
class Timer {
public:
static Timer* Get(); // 單例
public:
GLfloat scaled = 1; // 縮放值
GLfloat time = 0; // 從 glfw 啓動到現在的時間(秒)
GLfloat deltaTime = 0; // 無縮放的每幀的間隔時間(秒)
GLfloat scaledDeltaTime = 0; // 有縮放的每幀的間隔時間(秒)
GLfloat preseveTime = 0; // 預留的時間值
void update();
~Timer();
private:
Timer();
GLfloat lastTime = 0; // 上一幀的 從 glfw 啓動到現在的時間(秒)
};
Timer* Timer::Get() {
if (internal_ns::g_Timer == NULL) {
internal_ns::g_Timer = new Timer();
}
return internal_ns::g_Timer;
}
Timer::Timer() {
}
Timer::~Timer() {
internal_ns::g_Timer = NULL;
}
void Timer::update() {
time = (float)glfwGetTime();
deltaTime = time - lastTime;
scaledDeltaTime = deltaTime * scaled;
lastTime = time;
}
}
#endif
然後在設置shader uniform時,只要這麼弄就可以了:
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;
// system uniform
shaderProgram->setVec4("_Time", time[0], time[1], time[2], time[3]); // time info
算術 - my_math.h
提供一些對於 常量定義、角度與弧度轉換、linmath.h 額外的操作:
// my_math_util.h
/* author : jave.lin
數學工具
*/
#ifndef _MY_MATH_UTIL__H_
#define _MY_MATH_UTIL__H_
#include<stdlib.h>
#include"linmath.h"
#define PI 3.14159265359
// 角度轉弧度
#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
繪製數據
主要我們現在設置的數據和之前的不太一樣了,之前我們的頂點,直接就是在 NDC 座標下的值。
現在這些座標是經過各個座標系(空間)變換的,所以我們的頂點座標是 Object Space的。
頂點的索引順序有一些調整:
GLfloat vertices[] = {
// x, y, z
// 直接放 24 個頂點
// back
-0.5f, 0.5f, -0.5f, // 第0 個頂點
0.5f, 0.5f, -0.5f, // 第1 個頂點
0.5f, -0.5f, -0.5f, // 第2 個頂點
-0.5f, -0.5f, -0.5f, // 第3 個頂點
// front
-0.5f, -0.5f, 0.5f, // 第4 個頂點
0.5f, -0.5f, 0.5f, // 第5 個頂點
0.5f, 0.5f, 0.5f, // 第6 個頂點
-0.5f, 0.5f, 0.5f, // 第7 個頂點
// left
-0.5f, -0.5f, -0.5f, // 第8 個頂點
-0.5f, -0.5f, 0.5f, // 第9 個頂點
-0.5f, 0.5f, 0.5f, // 第10個頂點
-0.5f, 0.5f, -0.5f, // 第11個頂點
// right
0.5f, -0.5f, 0.5f, // 第12個頂點
0.5f, -0.5f, -0.5f, // 第13個頂點
0.5f, 0.5f, -0.5f, // 第14個頂點
0.5f, 0.5f, 0.5f, // 第15個頂點
// top
-0.5f, 0.5f, 0.5f, // 第16個頂點
0.5f, 0.5f, 0.5f, // 第17個頂點
0.5f, 0.5f, -0.5f, // 第18個頂點
-0.5f, 0.5f, -0.5f, // 第19個頂點
// bottom
-0.5f, -0.5f, -0.5f, // 第20個頂點
0.5f, -0.5f, -0.5f, // 第21個頂點
0.5f, -0.5f, 0.5f, // 第22個頂點
-0.5f, -0.5f, 0.5f, // 第23個頂點
};
GLfloat colors[] = {
// back
1.0, 0.0f, 0.0f, 1.0,
1.0, 0.0f, 0.0f, 1.0,
1.0, 0.0f, 0.0f, 1.0,
1.0, 0.0f, 0.0f, 1.0,
// front
0.0, 1.0f, 0.0f, 1.0,
0.0, 1.0f, 0.0f, 1.0,
0.0, 1.0f, 0.0f, 1.0,
0.0, 1.0f, 0.0f, 1.0,
// left
0.0, 0.0f, 1.0f, 1.0,
0.0, 0.0f, 1.0f, 1.0,
0.0, 0.0f, 1.0f, 1.0,
0.0, 0.0f, 1.0f, 1.0,
// right
0.0, 1.0f, 1.0f, 1.0,
0.0, 1.0f, 1.0f, 1.0,
0.0, 1.0f, 1.0f, 1.0,
0.0, 1.0f, 1.0f, 1.0,
// top
1.0, 1.0f, 0.0f, 1.0,
1.0, 1.0f, 0.0f, 1.0,
1.0, 1.0f, 0.0f, 1.0,
1.0, 1.0f, 0.0f, 1.0,
// bottom
1.0, 1.0f, 1.0f, 1.0,
1.0, 1.0f, 1.0f, 1.0,
1.0, 1.0f, 1.0f, 1.0,
1.0, 1.0f, 1.0f, 1.0,
};
GLfloat uvs[] = { // 頂點的 uv 座標
// back
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
// front
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
// left
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
// right
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
// top
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
// bottom
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
1.0f, 1.0f, // 右上角
0.0f, 1.0f, // 左上角
};
GLuint indices[] = { // 注意索引從0開始!通過索引緩存來指定 圖元 組成 用的 頂點有哪些
// back
0, 1, 2, // 第 0 個三角面
2, 3, 0, // 第 1 個三角面
// front
4, 5, 6, // 第 2 個三角面
6, 7, 4, // 第 3 個三角面
// left
8, 9, 10, // 第 4 個三角面
10, 11, 8, // 第 5 個三角面
// right
12, 13, 14, // 第 6 個三角面
14, 15, 12, // 第 7 個三角面
// top
16, 17, 18, // 第 8 個三角面
18, 19, 16, // 第 9 個三角面
// bottom
20, 21, 22, // 第 10個三角面
22, 23, 20, // 第 11個三角面
};
GLfloat scales[] = { // 每個繪製對象的縮放
1,
0.9f,
0.8f,
0.7f,
0.6f,
0.5f,
0.4f,
0.3f,
0.2f,
0.1f,
};
GLfloat rots[] = { // 每個繪製對象的旋轉
1.0f,
2.0f,
0.5f,
0.25f,
3.0f,
2.5f,
4.0f,
1.0f,
2.0f,
3.0f
};
GLfloat offsets[] = { // 每個繪製對象的平移
0, 0, 0,
1, 0, 0,
-1, 0, 0,
0, 1, 0,
0, -1, 0,
0, 0, -2,
1, 0, -2,
-1, 0, -2,
0, 1, -2,
0, -1, -2,
};
使用
void _stdcall OnInitCallback() {
...
cam = new Camera(0.0f, 0.0f, 3.0f);
camCtrl = new CameraController(cam);
...
}
void _stdcall OnMouseMovelCallback(const float deltaX, const float deltaY) {
camCtrl->processMouseMovement(deltaX, deltaY);
}
void _stdcall OnMouseScrollCallback(const float deltaY) {
camCtrl->processMouseScroll(deltaY);
}
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); // 清理顏色緩存 與 深度緩存
mat4x4 tMat, rMat, sMat; // model matrix == TRS matrix
mat4x4 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;
// system uniform
shaderProgram->setVec4("_Time", time[0], time[1], time[2], time[3]); // time info
camCtrl->getViewMatrix(vMat);
camCtrl->getProjectionMatrix(pMat);
shaderProgram->use(); // 使用此着色器程序
shaderProgram->setInt("main_tex", 0); // 主 紋理設置採樣器採樣 0 索引紋理單元
shaderProgram->setInt("mask_tex", 1); // 遮罩 紋理設置採樣器採樣 1 索引紋理單元
shaderProgram->setInt("flash_light_tex", 2); // 閃光/流光 紋理設置採樣器採樣 2 索引紋理單元
shaderProgram->setMatrix4x4("vMat", (const GLfloat*)vMat); // view matrix
shaderProgram->setMatrix4x4("pMat", (const GLfloat*)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];
mat4x4_identity(sMat);
mat4x4_identity(rMat);
mat4x4_scale(sMat, sMat, scale);
sMat[3][3] = 1; // linmath.h 的mat4x4_scale有bug,這個應該是1,分則會影響透視效果
mat4x4_rotate_Y(rMat, rMat, Timer::Get()->time * PI * 0.5f * rot_scaled);
mat4x4_rotate_X(rMat, rMat, Timer::Get()->time * PI * 0.25f * rot_scaled);
mat4x4_rotate_Z(rMat, rMat, Timer::Get()->time * PI * 0.125f * rot_scaled);
mat4x4_translate(tMat, offset_x, offset_y, offset_z);
mat4x4_mul(mMat, rMat, sMat);
mat4x4_mul(mMat, tMat, mMat);
shaderProgram->setMatrix4x4("mMat", (const GLfloat*)mMat); // model matrix
glDrawElements(GL_TRIANGLES, sizeof(vertices) / sizeof(vertices[0]), GL_UNSIGNED_INT, (GLvoid*)(0)); // 繪製 36 個索引對應的頂點
}
}
着色器
着色器我們需要是對 VS 添加:M(Model)、V(View)、P(Projection) 三個矩陣(也可以使用一個MVP合併後的矩陣)
// jave.lin - testing_camera1.vert - 測試鏡頭1
#version 450 compatibility
uniform mat4 mMat;
uniform mat4 vMat;
uniform mat4 pMat;
attribute vec3 vPos;
attribute vec2 vUV;
attribute vec4 vCol;
varying vec2 fUV;
varying vec4 fCol;
void main() {
gl_Position = pMat * vMat * mMat * vec4(vPos, 1.0);
fUV = vUV;
fCol = vCol;
}
// jave.lin - testing_camera1.frag - 測試鏡頭1
#version 450 compatibility
varying vec2 fUV; // uv 座標
varying vec3 fPos;
varying vec4 fCol;
uniform sampler2D main_tex; // 主紋理
uniform sampler2D mask_tex; // 遮罩紋理
uniform sampler2D flash_light_tex; // 閃光/流光紋理
uniform vec4 _Time; // 時間(秒)用於動畫
void main() {
vec3 mainCol = texture(main_tex, fUV).rgb;
float mask = texture(mask_tex, fUV).r;
vec4 flashCol = texture(flash_light_tex, fUV + vec2(-_Time.x, 0));
flashCol *= flashCol.a * mask;
mainCol = mainCol + flashCol.rgb;
gl_FragColor = mix(vec4(mainCol, 1.0), fCol, 0.5);
// gl_FragColor = mix(vec4(mainCol, 1.0), fCol, 0.8);
}