摄像机类相关代码实现

内容:
1、LookAt数学函数
2、Camera类编写
3、Application中事件函数
4、示例

LookAt数学函数实现

mathFunctions.h

//返回摄像机视图矩阵viewMatrix
template<typename T, typename V>
Matrix44<T> lookAt(const Vector3<V>& eye, const Vector3<V>& center, const Vector3<V>& top)
{
//计算计算机的局部坐标系
Vector3<V>& f = normalize(center - eye);
Vector3<V>& r = normalize(cross(f, top));
Vector3<V>& u = normalize(cross(r, f));

Matrix44<T> result(static_cast<T>(1));
//第一行
result.set(0, 0, r.x);
result.set(0, 1, r.y);
result.set(0, 2, r.z);
result.set(0, 3, -dot(r, eye));

//第二行
result.set(1, 0, u.x);
result.set(1, 1, u.y);
result.set(1, 2, u.z);
result.set(1, 3, -dot(u, eye));

//第三行
result.set(2, 0, -f.x);
result.set(2, 1, -f.y);
result.set(2, 2, -f.z);
result.set(2, 3, dot(f, eye));

return result;
}

Camera类实现

camera.h

#pragma once
#include "../global/base.h"
#include "../math/math.h"

#define NO_MOVE 0
#define MOVE_LEFT 0x001
#define MOVE_RIGHT 0x002
#define MOVE_FRONT 0x004
#define MOVE_BACK 0x008

//1 接收到外部操作(鼠标/键盘)
//2 根据操作更改当前状态(移动状态/front向量)
//3 每一帧由用户主动调用update,更新viewMatrix矩阵

class Camera {
public:
Camera(float fovy, float aspect, float n, float f, const math::vec3f& top);

~Camera();

public:
//对事件的响应
void onRMouseDown(const int& x, const int& y);

void onRMouseUp(const int& x, const int& y);

void onMouseMove(const int& x, const int& y);

void onKeyDown(const uint32_t& key);

void onKeyUp(const uint32_t& key);

//根据摄像机的状态计算更新viewMatrix
void update();

math::mat4f getViewMatrix()const { return mViewMatrix; }

math::mat4f getProjectionMatrix()const { return mProjectionMatrix; }

private:
//用于计算front向量
void pitch(int yoffset);

void yaw(int xoffset);

private:
math::mat4f mViewMatrix;
math::mat4f mProjectionMatrix;

math::vec3f mPosition{ 0.0f,0.0f,0.0f };
math::vec3f mFront{ 0.0f,0.0f,-1.0f };
math::vec3f mTop{ 0.0f,1.0f,0.0f };
float mSpeed = { 0.01f };

float mPitch{ 0.0f };
float mYaw{ -90.0f };
float mSensitivity{ 0.1f };

uint32_t mMoveState{ NO_MOVE };
bool mMouseMoving{ false };
int mCurrentMouseX{ 0 };
int mCurrentMouseY{ 0 };

};

camera.cpp

#include "camera.h"

Camera::Camera(float fovy, float aspect, float n, float f, const math::vec3f& top) {
mTop = top;
mProjectionMatrix = math::perspective(fovy, aspect, n, f);
update();
}

Camera::~Camera() {}

void Camera::onRMouseDown(const int& x, const int& y) {
mMouseMoving = true;
mCurrentMouseX = x;
mCurrentMouseY = y;
}

void Camera::onRMouseUp(const int& x, const int& y) {
mMouseMoving = false;
}

void Camera::onMouseMove(const int& x, const int& y) {
if (mMouseMoving) {
int xOffset = x - mCurrentMouseX;
int yOffset = y - mCurrentMouseY;

mCurrentMouseX = x;
mCurrentMouseY = y;

pitch(-yOffset);
yaw(xOffset);
}
}

void Camera::onKeyDown(const uint32_t& key) {
switch (key) {
case KEY_W:
mMoveState |= MOVE_FRONT;
break;
case KEY_A:
mMoveState |= MOVE_LEFT;
break;
case KEY_S:
mMoveState |= MOVE_BACK;
break;
case KEY_D:
mMoveState |= MOVE_RIGHT;
break;
default:
break;
}
}

void Camera::onKeyUp(const uint32_t& key) {
switch (key) {
case KEY_W:
mMoveState &= ~MOVE_FRONT;
break;
case KEY_A:
mMoveState &= ~MOVE_LEFT;
break;
case KEY_S:
mMoveState &= ~MOVE_BACK;
break;
case KEY_D:
mMoveState &= ~MOVE_RIGHT;
break;
default:
break;
}
}

void Camera::pitch(int yoffset) {
mPitch += yoffset * mSensitivity;

if (mPitch >= 89.0f)
{
mPitch = 89.0f;
}

if (mPitch <= -89.0f)
{
mPitch = -89.0f;
}

mFront.y = sin(DEG2RAD(mPitch));
mFront.x = cos(DEG2RAD(mYaw)) * cos(DEG2RAD(mPitch));
mFront.z = sin(DEG2RAD(mYaw)) * cos(DEG2RAD(mPitch));

mFront = math::normalize(mFront);
}

void Camera::yaw(int xoffset) {
mYaw += xoffset * mSensitivity;

mFront.y = sin(DEG2RAD(mPitch));
mFront.x = cos(DEG2RAD(mYaw)) * cos(DEG2RAD(mPitch));
mFront.z = sin(DEG2RAD(mYaw)) * cos(DEG2RAD(mPitch));

mFront = math::normalize(mFront);
}

void Camera::update() {
// Ŀ ܽ л ܣ õ һ ƶ
math::vec3f moveDirection = {0.0f, 0.0f, 0.0f};

math::vec3f front = mFront;
math::vec3f right = math::normalize(cross(mFront, mTop));

if (mMoveState & MOVE_FRONT) {
moveDirection += front;
}

if (mMoveState & MOVE_BACK) {
moveDirection += -front;
}

if (mMoveState & MOVE_LEFT) {
moveDirection += -right;
}

if (mMoveState & MOVE_RIGHT) {
moveDirection += right;
}

if (math::lengthSquared(moveDirection) != 0) {
moveDirection = math::normalize(moveDirection);
mPosition += mSpeed * moveDirection;
}

mViewMatrix = math::lookAt<float>(mPosition, mPosition + mFront, mTop);
}

Application中的事件函数

application.h

#pragma once
#include "../global/base.h"
#include<Windows.h>
#include "camera.h"

#define app Application::getInstance()

class Application {
public:
static Application* getInstance();
Application();
~Application();

//注册窗体类,创建一个窗体,显示
bool initApplication(HINSTANCE hInstance, const uint32_t& width = 800, const uint32_t& height = 600);

//托管了wndProc捕获的消息,并且进行处理
void handleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

//每一帧/每一次循环,都会调用,捕获以及分发窗体消息
bool peekMessage();

void show();

uint32_t getWidth() const { return mWidth; }
uint32_t getHeight() const { return mHeight; }
void* getCanvas() const { return mCanvasBuffer; }

void setCamera(Camera* camera);//设置camera对象<-------------------------------+


private:
BOOL createWindow(HINSTANCE hInstance);
ATOM registerWindowClass(HINSTANCE hInstance);

private:
static Application* mInstance;

Camera* mCamera{ nullptr };//camera对象,接收鼠标按键事件<-------------------------------+

//为true表示当前窗体仍然在继续显示,程序一直在跑
//为false表示窗体已经被命令关闭,程序需要退出
bool mAlive{ true };

HINSTANCE mWindowInst;
WCHAR mWindowClassName[100] = L"AppWindow";
HWND mHwnd;

int mWidth = 800;
int mHeight = 600;

HDC mhDC;//当前窗口主dc
HDC mCanvasDC;//创建的于mhDC相兼容的绘图用的dc
HBITMAP mhBmp;//mCanvasDC内部生成的bitmap
void* mCanvasBuffer{ nullptr };//mhBmp对应的内存起始位置指针
};

application.cpp

void Application::handleMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_KEYDOWN: {
if (mCamera) {
mCamera->onKeyDown(wParam);
}
break;
}
case WM_KEYUP: {
if (mCamera) {
mCamera->onKeyUp(wParam);
}
break;
}
case WM_RBUTTONDOWN: {
if (mCamera) {
mCamera->onRMouseDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
}
case WM_RBUTTONUP: {
if (mCamera) {
mCamera->onRMouseUp(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
}

case WM_MOUSEMOVE: {
if (mCamera) {
mCamera->onMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
}
break;
}
case WM_CLOSE: {
DestroyWindow(hWnd);//此处销毁窗体,会自动发出WM_DESTROY
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY: {
PostQuitMessage(0);//发出线程终止请求
mAlive = false;
break;
}
}
}

示例

uint32_t WIDTH = 800;
uint32_t HEIGHT = 600;

Camera* camera = nullptr;

//两个三角形,三个属性对应vbo
uint32_t positionVbo = 0;

uint32_t colorVbo = 0;

uint32_t uvVbo = 0;

//三角形的indices
uint32_t ebo = 0;

//两个三角形专属vao
uint32_t vao = 0;

//使用的Shader
TextureShader* shader = nullptr;

//使用的texture
uint32_t texture = 0;
Image* image = nullptr;

//mvp变换矩阵
math::mat4f modelMatrix;

void render() {
shader->mModelMatrix = modelMatrix;
shader->mViewMatrix = camera->getViewMatrix();
shader->mProjectionMatrix = camera->getProjectionMatrix();
shader->mDiffuseTexture = texture;

sgl->clear();
sgl->useProgram(shader);

sgl->bindVertexArray(vao);
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
sgl->drawElement(DRAW_TRIANGLES, 0, 3);

}

void prepare() {
camera = new Camera(60.0f, (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f, {0.0f, 1.0f, 0.0f});
app->setCamera(camera);

shader = new TextureShader();

//制造纹理
image = Image::createImage("assets/textures/test.jpg");
texture = sgl->genTexture();
sgl->bindTexture(texture);
sgl->texImage2D(image->mWidth, image->mHeight, image->mData);
sgl->texParameter(TEXTURE_FILTER, TEXTURE_FILTER_LINEAR);
sgl->texParameter(TEXTURE_WRAP_U, TEXTURE_WRAP_REPEAT);
sgl->texParameter(TEXTURE_WRAP_V, TEXTURE_WRAP_REPEAT);
sgl->bindTexture(0);

sgl->enable(CULL_FACE);
sgl->frontFace(FRONT_FACE_CCW);
sgl->cullFace(BACK_FACE);

float positions[] = {
-0.5f, 0.0f, -1.0f,
0.5f, 0.0f, -1.0f,
0.0f, 0.5f, -1.0f,
};

float colors[] = {
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
};

float uvs[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.5f, 1.0f,
};

uint32_t indices[] = { 0, 1, 2 };

//生成indices对应ebo
ebo = sgl->genBuffer();
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
sgl->bufferData(ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * 3, indices);
sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, 0);

//生成vao并且绑定
vao = sgl->genVertexArray();
sgl->bindVertexArray(vao);

//position
positionVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, positionVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 9, positions);
sgl->vertexAttributePointer(0, 3, 3 * sizeof(float), 0);

//color
colorVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, colorVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 12, colors);
sgl->vertexAttributePointer(1, 4, 4 * sizeof(float), 0);

//uv
uvVbo = sgl->genBuffer();
sgl->bindBuffer(ARRAY_BUFFER, uvVbo);
sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 6, uvs);
sgl->vertexAttributePointer(2, 2, 2 * sizeof(float), 0);

sgl->bindBuffer(ARRAY_BUFFER, 0);
sgl->bindVertexArray(0);
}

int APIENTRY wWinMain(
_In_ HINSTANCE hInstance, //本应用程序实例句柄,唯一指代当前程序
_In_opt_ HINSTANCE hPrevInstance, //本程序前一个实例,一般是null
_In_ LPWSTR lpCmdLine, //应用程序运行参数
_In_ int nCmdShow) //窗口如何显示(最大化、最小化、隐藏),不需理会
{
if (!app->initApplication(hInstance, WIDTH, HEIGHT)) {
return -1;
}

//将bmp指向的内存配置到sgl当中
sgl->initSurface(app->getWidth(), app->getHeight(), app->getCanvas());

prepare();

bool alive = true;
while (alive) {
alive = app->peekMessage();
camera->update();

render();
app->show();
}

delete shader;
Image::destroyImage(image);
sgl->deleteTexture(texture);
delete camera;

return 0;
}