LearnGL - 08.1 - Camera - GLM 版

文章目录

  • 算术 - my_math_util.h
  • 相机类 - my_camera_glm.h
  • 使用
  • References

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

//#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()
	#define PId glm::pi()

	// 角度转弧度
	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;

	// system uniform
	shaderProgram->setVec4("_Time", time[0], time[1], time[2], time[3]);	// time info

	vMat = camCtrl->getViewMatrix();
	pMat = camCtrl->getProjectionMatrix();

	shaderProgram->use();												// 使用此着色器程序
	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

  • 变换

你可能感兴趣的:(OpenGL)