LearnGL - 08.1 - Camera - GLM 版


LearnGL - 學習筆記目錄

本人才疏學淺,如有什麼錯誤,望不吝指出。

上些篇:

這一篇:我們還是實現一樣的功能,只不過是: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 個索引對應的頂點
	}
}

運行效果一直,就不發了。

References

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章