本人才疏学浅,如有什么错误,望不吝指出。
上些篇:
这一篇:我们还是实现一样的功能,只不过是:GLM 线性数学库版本的(这个库不有线性代数库,也包含了其他的)。
顺便一提:UE5 中的 Nanite 技术,虚拟多边形,官方暂时也未公布技术细节,说明几何体可以不限制顶点数量、多边形的数量,可以做到非常密集的的数量,密集到一个像素还可以容纳到数个多边形。
目前我也完全不知道是怎么个思路,TQL。
多人认为 Nanite 是用 Mesh Shader 来实现的::如何评价虚幻5引擎的Nanite虚拟微多边形几何体技术?。
技术不断在升级改革(科技服务在升级、在变化)。
唯独不变的还是数学的内容,所以说,数学的魅力有多大(运算模型不变)。
做了一些调整,添加了 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
使用起来比 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 个索引对应的顶点
}
}
运行效果一直,就不发了。