LearnGL - 08 - Camera - linmath.h 版


LearnGL - 学习笔记目录

本人才疏学浅,如有什么错误,望不吝指出。

上一篇:LearnGL - 07 - DrawCube - 旋转Cube、深度缓存/测试,了解如何绘制一个Cube立方体、旋转3D 几何体、深度缓存、深度测试都了一些基础的认识。

这一篇搬砖:矩阵来实现 摄像机/镜头 类,便于测试项目中对镜头的:位移,和部分轴向的旋转。


看了一下 摄像机 这篇实现的够详细的,再搬砖一下。


几何变换

之前的文章有将到,我们传入的顶点数据通常是在 对象空间 的,就是在 3D 建模软件是的坐标。

这些顶点坐标,最终会显示在屏幕上,中间会经历好几个坐标空间(坐标系)的变换:


总览变换

也可以参考我之前的:OpenGL Transformation 几何变换的顺序概要(MVP,NDC,Window坐标变换过程),这里再次简单描述一下

Model Matrix
Word2View Matrix
View2Projtion Matrix
Perspective Division
Viewport Mapping
Object/Local Space
Model/World Space
View/Eye/Camera Space
Projection/Clip Space
NDC Space
Screen/View Space
Object/Local Space
Model/World Space
View/Eye/Camera Space
Projection/Clip Space
NDC Space
Screen/View Space

Local to World

Model Matrix
Object/Local Space
Model/World Space

对象空间 转到 世界空间 一般是每个绘制对象都可能不一样的变换数据,也就是中间那个:Model Matrix 相对于每一个绘制对象来说,都可能是不一样的,这其中包含了:缩放、旋转、平移。


World to View

View Matrix
Model/World Space
View/Eye/Camera Space

这个一般是 摄像机 控制的变换,这里头的变换一般只有:旋转、平移。注意我们的World To View的坐标系是 右手坐标系的,即:Z轴正方向是从屏幕指向你的眼睛的方向。


View to Projection

Projection Matrix
View/Eye/Camera Space
Projection/Clip Space

这个一般也是由 摄像机 控制的变换,这里头的变换一般只有:是只有轴的缩放。它是负责将3D空间的坐标变换到 4D 的齐次坐标,并调整4D 坐标的第四个分量以便后续做透视除法后投影到 NDC 而是用的。


Projection to NDC

Perspective Division
Projection/Clip Space
NDC Space

这个除法处理,一般来说不需要我们处理,我们只要在 VS(Vertex Shader,顶点着色器)将坐标从 Object Space 通过 MVP(Model, View, Projection)变换后调整到用于投影用的空间:Clip Space(这里一般被称作:裁剪空间)。

OpenGL 的底层实现 会将 P o s i t i o n N D C = P o s i t i o n C l i p / P o s i t i o n c l i p . w ; Position_{NDC}=Position_{Clip} / Position_{clip}.w; PositionNDC=PositionClip/Positionclip.w;

的到的 NDC 坐标后,就会下一个阶段,直到传到 光栅阶段 后,光栅器 会将图元生成插值片元。

便于后续的 片元着色器 用。


NDC to Screen

Viewport Mapping
NDC Space
Screen/View Space

上面得到的 NDC 坐标后,光栅器生成片元后,在传给片元着色器之前,它会先将片元的坐标转换到屏幕坐标。

NDC的x,y范围是-1~1的,OpenGL 中的NDC.z也是-1~1的,DX的NDC.z是 0~1。

OpenGL 会将 NDC 坐标通过 glViewport 设置的x,y,w,h来定位于屏幕的坐标。

P o s i t i o n s c r e e n . x = ( P o s i t i o n N D C . x + 1 ) ∗ 0.5 ∗ W v i e w p o r t ; P o s i t i o n s c r e e n . y = ( P o s i t i o n N D C . y + 1 ) ∗ 0.5 ∗ H v i e w p o r t ; Position_{screen}.x = (Position_{NDC}.x + 1) * 0.5 * W_{viewport};\\ Position_{screen}.y = (Position_{NDC}.y + 1) * 0.5 * H_{viewport}; Positionscreen.x=(PositionNDC.x+1)0.5Wviewport;Positionscreen.y=(PositionNDC.y+1)0.5Hviewport;

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);
}

运行效果

在这里插入图片描述


References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值