您现在的位置是:首页 >技术教程 >OpenGL via C++:glew+glfw3+glm,类引擎开发(一)网站首页技术教程

OpenGL via C++:glew+glfw3+glm,类引擎开发(一)

aaaa0ggMC 2024-09-27 00:01:03
简介OpenGL via C++:glew+glfw3+glm,类引擎开发(一)

        学习OpenGL的书我用的是《计算机图形学编程(使用OpenGL与C++》[英文版:Computer Graphics Programming in OpenGL with C++]。这本书初三就买了,但是看了一直没有实践(我还有一本D.Luna的DX11中文版也是一样的)。目前因为SFML让我有些生气,于是我就干脆自己用OpenGL重做自己的游戏。我便跟着这本书,并把它浓缩为一个类似引擎的东西。(技术不行,大家见谅)

OpenGL管线与我的Shader

                其实OpenGL有许多管线,但是我很菜,于是只支持了三种着色器,顶点着色器(Vertex Shader),片段着色器(Fragment Shader)还有几何着色器(Geometry Shader).着色器由一个shader来管理:

//Shader.h
///@Copyright aaaa0ggmc 2023
#ifndef SHADER_H_INCLUDED
#define SHADER_H_INCLUDED

/*
基本的运行库,我用的是静态的GLEW,因此,
我在codeblocks的build options里面设置了全局defines,
就相当于每个文件前面都默认有一个 #define GLEW_STATIC
*/
#include <GL/glew.h>

//Utilities 一些静态函数
#include "Utility.h"

#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

//My Engine => me
namespace me{
    using namespace std;

    //着色器 Uniform 管理
    class ShaderArg{
    public:
        ShaderArg(GLuint program = 0,GLuint offset = 0);
        GLuint GetOffset();
        GLuint GetProgram();
        void SetOffset(GLuint);
        void SetProgram(GLuint);
        //返回偏移是否有效
        bool IsAvailable();
        //这里重载了很多,从而支持ShaderArg = xxx就可以直接上传数据给OpenGL
        GLfloat operator=(GLfloat v);
        GLfloat* operator=(GLfloat* v);
        GLint operator=(GLint v);
        GLuint operator=(GLuint v);
        glm::mat4* operator=(glm::mat4& v);
        //double 与 float 容易搞混并且float的uniform不识别double值,因此额外处理
        //毕竟因该不会认多少人在着色器里写double uniform
        GLdouble UploadDouble(GLdouble v);
    private:
        //实际只有一点点占用,可以返回值而不是引用或者指针
        bool ava;//available
        //这个实际上现在没用了,之前用的是glProgramUniformXXX,改成了glUniformXXX
        GLuint program;
        //偏移量
        GLuint offset;
    };

    //着色器
    class Shader{
    public:
        //原本是在Shader里默认就创建,但是如果没有glewInit并成功的话会崩溃
        //所以提供了CreateProgram后面创建
        void CreateProgram();
        //获取uniform,后面准备用unordered_map直接返回已经索引过的uniform
        //也许能提高性能
        ShaderArg GetUniform(const char * s);
        ShaderArg GetUniform(const string & s);
        ShaderArg operator[](const char * s);
        ShaderArg operator[](string & s);
        //加载着色器(这一串只负责加载与编译,不负责link)
        //每个都提供两种函数是为了减少string的拷贝,从而加快速度
        int LoadLinkLogF(const char * vert,const char * frag,const char * geo = NULL);
        int LoadLinkLogM(const char * vert,const char * frag,const char * geo = NULL);
        int LoadFromFile(const string&file,GLenum type);
        int LoadFromFile(const char * file,GLenum type,size_t sz = ME_DETECT_SIZE);
        int LoadFromMemory(const string&str,GLenum type);
        int LoadFromMemory(const char * str,GLenum type,size_t sz = ME_DETECT_SIZE);
        //一次性加载多个,但是也不link
        int LoadsFromFile(const char * vert,const char * frag,const char * geometry = NULL);
        int LoadsFromMem(const char * vert,const char * frag,const char * geometry = NULL);
        //获得这些内部句柄,只有GetProgram返回GLuint是因为有可能其他的根本没创建,只有program
        //也许保证绝对有效
        int GetVertexShader();
        int GetFragmentShader();
        int GetGeometryShader();
        GLuint GetProgram();
        //绑定,相当于glUseProgram(Shader::program)
        void bind();
        //链接
        void LinkShader();
        //输出错误,日志...
        void Log();
        //提供获取日志,用了string的copy
        string GetLog();
        //存储日志,采取append模式
        void StoreLog(string& appender);
        //bind的静态形式,支持bind(NULL)来除去当前使用的program
        static void bind(Shader*);
        //如果shader作为全局变量,请传false,
        //如果在Window初始化并makeCurrent之后再定义,就可以传true或者不传
        //不然会崩
        Shader(bool initProgram = true);
    private:
        //好像用处不大的友元
        friend class Window;
        GLuint program;
        GLuint vertex,fragment,geometry;
        bool enabled[ME_SHADER_TYPEC];
    };
}

#endif // SHADER_H_INCLUDED

 实现如下:

//Shader.cpp
///@Copyright aaaa0ggmc 2023
#include "Shader.h"
#include <stdlib.h>
#include <stdio.h>
#include <direct.h>
#include "Utility.h"

using namespace me;
using namespace std;

Shader::Shader(bool x){
    memset(enabled,0,sizeof(bool) * ME_SHADER_TYPEC);
    vertex = fragment = geometry = 0;
    if(x)program = glCreateProgram();
    else program = 0;
}


void Shader::CreateProgram(){
    if(!program)program = glCreateProgram();
}

int Shader::LoadFromFile(const string&file,GLenum type){
    return LoadFromFile(file.c_str(),type,file.length());
}

//这一块的各种return 之后都会换成Utility::InvokeConsole+return从而告知错误
int Shader::LoadFromFile(const char * file,GLenum type,size_t sz){
    if(!file)return ME_NO_DATA;

    ///Declarations(突然不知为何就想写C99式代码了,有点SB)
    size_t rsz = (sz == 0?strlen(file):sz);
    char * buf;
    int ret;

    if(rsz == 0)return ME_EMPTY_STRING;
    rsz = Utility::file_size(file);
    if(!rsz)return ME_BAD_IO;
    ///Create File
    FILE * f = fopen(file,"r");
    buf = new char[rsz];
    if(!buf)return ME_BAD_MEM;
    memset(buf,0,sizeof(char) * rsz);
    fread(buf,sizeof(char),rsz,f);
    fclose(f);
    ///Call LoadFromMemory to make shader
    ret = LoadFromMemory(buf,type,rsz);
    ///Free buffer
    delete buf;
    ///Return the value
    return ret;
}

int Shader::LoadFromMemory(const string&str,GLenum type){
    return LoadFromMemory(str.c_str(),type,str.length());
}

int Shader::LoadFromMemory(const char * str,GLenum type,size_t sz){
    if(!str)return ME_NO_DATA;

    size_t rsz = (sz == 0?strlen(str):sz);
    GLuint *target;

    if(rsz == 0)return ME_EMPTY_STRING;

    if(type == ME_SHADER_VERTEX){
        if(enabled[0])return ME_ALREADY_EXI;
        target = &vertex;
        enabled[0] = true;
    }else if(type == ME_SHADER_FRAGMENT){
        if(enabled[1])return ME_ALREADY_EXI;
        target = &fragment;
        enabled[1] = true;
    }else if(type == ME_SHADER_GEOMETRY){
        if(enabled[2])return ME_ALREADY_EXI;
        target = &geometry;
        enabled[2] = true;
    }else return ME_BAD_TYPE;
    *target = glCreateShader(type);
    glShaderSource(*target,1,&str,NULL);
    glCompileShader(*target);
    glAttachShader(program,*target);
    return ME_NO_ERROR;
}

void Shader::LinkShader(){
    glLinkProgram(program);
}

void Shader::bind(Shader*s){
    if(!s)glUseProgram(0);
    else glUseProgram(s->program);
}

void Shader::bind(){
    glUseProgram(program);
}

void Shader::Log(){
    Utility::PrintProgramLog(program);
    if(enabled[0])Utility::PrintShaderLog(vertex);
    if(enabled[1])Utility::PrintShaderLog(fragment);
    if(enabled[2])Utility::PrintShaderLog(geometry);
}


string Shader::GetLog(){
    string x = "";
    StoreLog(x);
    return x;
}

void Shader::StoreLog(string & v){
    Utility::GetProgramLog(program,v);
    if(enabled[0])Utility::GetShaderLog(vertex,v);
    if(enabled[1])Utility::GetShaderLog(fragment,v);
    if(enabled[2])Utility::GetShaderLog(geometry,v);
}

GLuint Shader::GetProgram(){return program;}

int Shader::GetFragmentShader(){
    if(enabled[1])return (int)fragment;
    return ME_NO_DATA;
}

int Shader::GetGeometryShader(){
    if(enabled[2])return (int)geometry;
    return ME_NO_DATA;
}

int Shader::GetVertexShader(){
    if(enabled[0])return (int)vertex;
    return ME_NO_DATA;
}

int Shader::LoadsFromFile(const char * vert,const char * frag,const char * geometry){
    return LoadFromFile(vert,ME_SHADER_VERTEX) |
    LoadFromFile(frag,ME_SHADER_FRAGMENT) |
    LoadFromFile(geometry,ME_SHADER_GEOMETRY);
}

int Shader::LoadsFromMem(const char * vert,const char * frag,const char * geometry){
    return LoadFromMemory(vert,ME_SHADER_VERTEX) |
    LoadFromMemory(frag,ME_SHADER_FRAGMENT) |
    LoadFromMemory(geometry,ME_SHADER_GEOMETRY);
}


int Shader::LoadLinkLogF(const char * vert,const char * frag,const char * geo){
    int ret = LoadsFromFile(vert,frag,geo);
    LinkShader();
    Log();
    return ret;
}

int Shader::LoadLinkLogM(const char * vert,const char * frag,const char * geo){
    int ret = LoadsFromMem(vert,frag,geo);
    LinkShader();
    Log();
    return ret;
}

GLfloat ShaderArg::operator=(GLfloat v){
    if(!ava)return v;
    glUniform1f(offset,v);
    return v;
}


GLdouble ShaderArg::UploadDouble(GLdouble v){
    if(!ava)return v;
    glUniform1d(offset,v);
    return v;
}

GLint ShaderArg::operator=(GLint v){
    if(!ava)return v;
    glUniform1i(offset,v);
    return v;
}

GLuint ShaderArg::operator=(GLuint v){
    if(!ava)return v;
    glUniform1ui(offset,v);
    return v;
}

GLfloat* ShaderArg::operator=(GLfloat* v){
    if(!ava)return v;
    glUniformMatrix4fv(offset,1,GL_FALSE,v);
    return v;
}

ShaderArg::ShaderArg(GLuint a,GLuint b){
    SetProgram(a);
    SetOffset(b);
}

GLuint ShaderArg::GetOffset(){return offset;}
GLuint ShaderArg::GetProgram(){return program;}
void ShaderArg::SetOffset(GLuint a){
    offset = a;
}
void ShaderArg::SetProgram(GLuint b){
    if(b){
        ava = true;
        program = b;
    }
}
bool ShaderArg::IsAvailable(){return ava;}


ShaderArg Shader::GetUniform(const char * s){
    return ShaderArg(program,glGetUniformLocation(program,s));
}

ShaderArg Shader::GetUniform(const string & s){
    return GetUniform(s.c_str());
}


ShaderArg Shader::operator[](const char * s){
    return ShaderArg(program,glGetUniformLocation(program,s));
}

ShaderArg Shader::operator[](string & s){
    return GetUniform(s.c_str());
}

glm::mat4* ShaderArg::operator=(glm::mat4 &v){
    (*this) = glm::value_ptr(v);
    return &v;
}

依据上面的代码,你就可以写出这种形式的C++代码了:

Shader s;//已经创建好了窗口并且makeCurrent了
//下面的glsl代码没打草稿直接写出来了,可能有错误
//这里采用了C++多行字符串的特性
s.LoadLinkLogM(
    R"(
#version 430

uniform float fv;
uniform mat4 mv;

void main(){
    gl_Position = vec4(fv,0,0,1) * mv;
}
    )",
    R"(
#version 430

out vec4 color;

void main(){
    color = vec4(1,0,0,1);
}
    )"
);

s["fv"] = 1.0f;//float赋值
s["mv"] = a_mat;//glm::mat4赋值(这个是引用)
//float*赋值(注意:float数组大小必须大于16(4*4),不然可能会崩)
s["mv"] = glm::value_ptr(a_mat);

s.bind();
Shader::bind(&s);
Shader::bind(NULL);

/*
下面是main函数中的实例
*/
void display(Window& window, double currentTime,Camera* c) {
    window.Clear();
	s.bind();

    //这里我还在学习啊
	///ISSUE:相机旋转也要负转矩阵吗??
	//cam.SetRotation(0,PI / 4,0);

	cam.UpdateModelMat();
	cube.UpdateModelMat();
	pyramid.UpdateModelMat();

	s["m_matrix"] = cube.mat;
	s["v_matrix"] = c->mat;
	s["proj_matrix"] = cam.perspec;
	s["tf"] = (float)currentTime;

	window.Draw(cube,36);

	s["m_matrix"] = pyramid.mat;
	window.Draw(pyramid,18);
}

我的窗口管理

        说白了我就是在glfw3上面套了一层C++的接口并实现了一些简单的扩展而已(比如FPS限制)。

        

//Window.h 这里顺便实现了GameObject
///@Copyright aaaa0ggmc 2023
#ifndef WINDOW_H_INCLUDED
#define WINDOW_H_INCLUDED
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "Utility.h"
#include "Shader.h"
#include "VertexObjects.h"
#include <string>
#include <memory>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <math.h>

#define ME_FRAME_ADJUST_V 0.08

namespace me{
    using PShader = std::unique_ptr<Shader>;

    class GObject{
    public:
        GObject(float x = 0,float y = 0,float z = 0,bool enableMovement = false);
        //设置坐标
        void SetPosition(float x,float y,float z = 0);
        void SetPosition(glm::vec3& v);
        glm::vec3 GetPosition();
        
        void Move(float x,float y,float z = 0);
        void Move(glm::vec3 & v);
        //依据left,up,forward向量移动(现在没做好)
        void MoveDirectional(float left,float up,float forward);
        
        //遗留物在mat为private的时候用得到
        glm::mat4* GetMat();
        //更新mat,相当于rotation + scale(还没做) + translate
        virtual void UpdateModelMat();
        //更新rmat,不与UpdateModelMat合并是为了节约性能
        void UpdateRotationMat();

        //CPU上构建MV矩阵,传进来的是view_matrix,可以传&Camera::mat也可以直接传&Camera
        void BuildMV(glm::mat4 * m);
        void BuildMV(GObject * g);

        //旋转
        void SetRotation(float x,float y,float z);
        void Rotate(float x,float y,float z);
        void RotateDirectional(float left,float up,float forward);
        void SetRotationD(float left,float up,float forward);

        //绑定VBO,获取VBO,这里的VBO是一个类,也支持=赋值上传数据
        void BindVBO(VBO invbo);
        VBO GetVBO();

        //设置是否支持left,up,forward,一般静态物体不需要就可以节省时间
        void SetMovement(bool = true);

        bool movement;

        glm::vec3 rotations;
        glm::vec3 position;

        glm::mat4 mvmat;
        glm::mat4 mat;
        glm::mat4 rmat;

        glm::mat4 tempMat;

        glm::vec3 left;
        glm::vec3 forward;
        glm::vec3 up;
    private:
        friend class Window;
        VBO vbo;
    };

    class Camera : public GObject{
    public:
        glm::mat4 perspec;
        Camera(float x = 0,float y = 0,float z = 0,bool = true);
        //View Matrix与model matrix不同,所以这里覆写
        void UpdateModelMat();
        void BuildPerspec(float fieldOfView,float ratio,float nearPlane,float farPlane);
        void BuildPerspec(float fieldOfView,float width,float height,float nearPlane,float farPlane);
        void BuildPerspec(float fieldOfView,void*w,float nearPlane,float farPlane);
    };

    class Window{
    public:
        typedef void (*WPaintFn)(Window&,double currentTime,Camera*cam);
        typedef void (*OnKeyPress)(Window&,double elapseus,Camera*cam);
        static Window * GetCurrent();
        Window(int major = 4,int minor = 3);
        int Create(unsigned int width,unsigned int height,const char * title,Window* share=NULL);
        int Create(unsigned int width,unsigned int height,std::string title,Window* share=NULL);
        GLFWwindow * GetGLFW();
        //这里返回HWND句柄,方便骚操作
        long GetSystemHandle();
        //现在基本用不到这个函数,之前我把Shader构造函数设置为私有的时候才经常用到
        int CreateShader(std::unique_ptr<Shader> &shader);
        //创建完后最好就MakeCurrent
        void MakeCurrent();
        static void MakeCurrent(Window *);
        unsigned int GetFramerateLimit();
        void Destroy();
        //默认不开启垂直同步
        static void SetSwapInterval(unsigned int = 0);
        //传0会取消帧率限制
        void SetFramerateLimit(unsigned int limit);
        bool ShouldClose();
        //每帧都会调用
        void Display();
        //封装了glClear(GL_XXXXX)
        void Clear(bool clearColor = true,bool clearDepth = true);
        //注册绘制函数
        void SetPaintFunction(WPaintFn);
        //注册按键输入函数
        void OnKeyPressEvent(OnKeyPress);

        //设置主要Camera,WPaintFn与OnKeyPress的第三个参数就是这个
        void UseCamera(Camera& cam);
        //绘制,可以绘制绑定了VBO的GObject,instance传0会警告一次
        void Draw(GObject&,GLuint triangles,GLuint instance = 1,GLuint bindingIndex = 0);
        //检测一个按键是否处于某种状态,也是一种封装
        bool KeyInputed(int key,int state = GLFW_PRESS);
        //获取两种大小,一个是窗口大小,一个是屏幕缓冲区大小
        glm::vec2 GetWindowSize();
        glm::vec2 GetBufferSize();
    private:
        static Window * current;
        GLFWwindow* win;
        WPaintFn paint;
        OnKeyPress press;
        unsigned int flimit;
        float twait,frame_start;
        bool limitedF;
        Camera * curCam;
        float firstTime;
    };
}


#endif // WINDOW_H_INCLUDED

代码如下:

//Window.cpp 不想额外写注释了QAQ,写个博客写注释累死我了
///@Copyright aaaa0ggmc 2023
#include "Window.h"
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h>
//最大程度上减少对系统的依赖,所以用unistd.h的usleep
#include <unistd.h>

using namespace me;

Window* Window::current = NULL;


void Window::Clear(bool clearColor,bool clearDepth){
    if(clearColor)glClear(GL_COLOR_BUFFER_BIT);
    if(clearDepth)glClear(GL_DEPTH_BUFFER_BIT);
}

Window::Window(int major,int minor){
    Utility::InitGLFW(major,minor);
    paint = NULL;
    win = NULL;
    curCam = NULL;
    flimit = frame_start = twait = 0;
    limitedF = false;
    press = NULL;
}

int Window::Create(unsigned int width,unsigned int height,const char* title,Window*share){
    if(win){
        Utility::InvokeConsole("The window is already created!",true,"Window::Draw",(long)this);
        return ME_ALREADY_EXI;
    }
    win = glfwCreateWindow(width,height,title,NULL,share?share->win:NULL);
    return win?ME_NO_ERROR:ME_BAD_MEM;
}


int Window::Create(unsigned int width,unsigned int height,std::string title,Window*share){
    return Create(width,height,title.c_str(),share);
}

GLFWwindow * Window::GetGLFW(){return win;}

long Window::GetSystemHandle(){
    return (long)glfwGetWin32Window(win);
}

int Window::CreateShader(std::unique_ptr<Shader>& s){
    if(current){
        s.reset(new Shader());
        return ME_NO_ERROR;
    }
    return ME_NO_DATA;
}

Window* Window::GetCurrent(){return current;}

void Window::MakeCurrent(){
    glfwMakeContextCurrent(this->win);
    current = this;
    Utility::InitGlew();
}

void Window::MakeCurrent(Window * v){
    if(v){
        glfwMakeContextCurrent(v->win);
    }else glfwMakeContextCurrent(NULL);
    current = v;
    Utility::InitGlew();
}

void Window::Destroy(){
    glfwDestroyWindow(win);
    this->win = NULL;
    MakeCurrent(NULL);
}

void Window::SetSwapInterval(unsigned int a){
    glfwSwapInterval(a);
}

bool Window::ShouldClose(){
    return glfwWindowShouldClose(win);
}

void Window::Display(){
    static bool warned = false;
    if(!curCam && !warned){
        warned = true;
        Utility::InvokeConsole("The main camera is NULL!Game may crash!Use Window::UseCamera to activate!",true,"Window::Display",(long)this);
    }
    firstTime = glfwGetTime();
    if(paint)paint(*this,glfwGetTime(),curCam);
    glfwSwapBuffers(win);
    glfwPollEvents();
    if(limitedF){
        while(glfwGetTime()+ME_FRAME_ADJUST_V < frame_start + twait){
            usleep((useconds_t)(twait)*1000 * 100);
        }
        frame_start += twait;
    }
    if(press)press(*this,glfwGetTime() - firstTime,curCam);
}


bool Window::KeyInputed(int key,int state){
    return glfwGetKey(win,key) == state;
}

void Window::SetPaintFunction(WPaintFn fn){
    paint = fn;
}

void Window::OnKeyPressEvent(OnKeyPress fn){
    press = fn;
}

void Window::SetFramerateLimit(unsigned int limit){
    if(!limit){
        limitedF = false;
        flimit = UINT_MAX;
        return;
    }
    limitedF = true;
    frame_start = glfwGetTime();
    flimit = limit;
    twait = 1.0 / flimit;
}

unsigned int Window::GetFramerateLimit(){
    return flimit;
}

GObject::GObject(float x,float y,float z,bool m){
    position.x = x;
    position.y = y;
    position.z = z;
    movement = m;
    SetRotation(0,0,0);
}

void GObject::SetPosition(float x,float y,float z){
    position.x = x;
    position.y = y;
    position.z = z;
}

void GObject::SetPosition(glm::vec3 & v){
    position.x = v.x;
    position.y = v.y;
    position.z = v.z;
}

glm::vec3 GObject::GetPosition(){return position;}


void GObject::Move(float x,float y,float z){
    position.x += x;
    position.y += y;
    position.z += z;
}

void GObject::Move(glm::vec3 & v){
    position.x += v.x;
    position.y += v.y;
    position.z += v.z;
}

glm::mat4 * GObject::GetMat(){return &mat;}

void GObject::UpdateModelMat(){
    mat = glm::translate(glm::mat4(1.0),position);
    mat = mat * rmat;
}


void GObject::SetMovement(bool v){
    movement = v;
    Rotate(0,0,0);
}


void GObject::MoveDirectional(float l,float u,float f){
    if(!movement){
        ///处理错误,不需要很节省
        Utility::InvokeConsole("Using MoveDirectional without setting the movement field!",true,"MoveDirectional",(long)this);
        return;
    }
    float x = l * left.x + u * up.x + f * forward.x;
    float y = l * left.y + u * up.y + f * forward.y;
    float z = l * left.z + u * up.z + f * forward.z;
    Move(x,y,z);
}

void GObject::UpdateRotationMat(){
    rmat = glm::mat4(1.0);
    if(rotations.x != 0){
        rmat = glm::rotate(rmat,rotations.x,glm::vec3(1,0,0));
    }
    if(rotations.y != 0){
        rmat = glm::rotate(rmat,rotations.y,glm::vec3(0,1,0));
    }
    if(rotations.z != 0){
        rmat = glm::rotate(rmat,rotations.z,glm::vec3(0,0,1));
    }
    if(movement){
        left = glm::normalize(rmat * glm::vec4(1,0,0,0));
        forward = glm::normalize(glm::vec4(0,0,1,0) * rmat);
        up = glm::normalize(glm::vec4(0,1,0,0) * rmat);
    }
}


void GObject::RotateDirectional(float l,float u,float f){
    tempMat = glm::rotate(glm::mat4(1.0),l,left);
    tempMat = glm::rotate(tempMat,u,up);
    tempMat = glm::rotate(tempMat,f,forward);
    rmat = rmat * tempMat;
}

void GObject::SetRotationD(float l,float u,float f){
    tempMat = glm::rotate(glm::mat4(1.0),l,left);
    tempMat = glm::rotate(tempMat,u,up);
    tempMat = glm::rotate(tempMat,f,forward);
    rmat = tempMat;
}

///View Mat,not model
void Camera::UpdateModelMat(){
    mat = glm::translate(glm::mat4(1.0),glm::vec3(-position.x,-position.y,-position.z));
    mat = mat * rmat;
}

Camera::Camera(float x,float y,float z,bool m){
    SetPosition(x,y,z);
    movement = m;
    SetRotation(0,0,0);
}

void Window::UseCamera(Camera & c){
    curCam = &c;
}

void Camera::BuildPerspec(float fieldOfView,float ratio,float nearPlane,float farPlane){
    perspec = glm::perspective(fieldOfView,ratio,nearPlane,farPlane);
}

void Camera::BuildPerspec(float fieldOfView,float width,float height,float nearPlane,float farPlane){
    BuildPerspec(fieldOfView,width/height,nearPlane,farPlane);
}

void Camera::BuildPerspec(float fieldOfView,void*w,float nearPlane,float farPlane){
    glm::vec2 sz = ((Window*)(w))->GetBufferSize();
    BuildPerspec(fieldOfView,sz.x / sz.y,nearPlane,farPlane);
}

glm::vec2 Window::GetWindowSize(){
    int w,h;
    glfwGetWindowSize(win,&w,&h);
    return glm::vec2(w,h);
}

void Window::Draw(GObject& o,GLuint triangles,GLuint in,GLuint bindingIndex){
    if(!in){
        Utility::InvokeConsole("The count of instance is zero.",true,"Window::Draw",(long)this);
        return;
    }
    if(o.vbo.GetVBO()){
        o.vbo.BindingTo(bindingIndex);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        if(in <= 1)glDrawArrays(GL_TRIANGLES, 0, triangles);
        else glDrawArraysInstanced(GL_TRIANGLES,0,triangles,in);
    }
}

glm::vec2 Window::GetBufferSize(){
    int w,h;
    glfwGetFramebufferSize(win,&w,&h);
    return glm::vec2(w,h);
}

void GObject::BuildMV(glm::mat4 * m){
    mvmat = *m * mat;
}

void GObject::BuildMV(GObject * v){
    BuildMV(&(v->mat));
}

VBO GObject::GetVBO(){return vbo;}

void GObject::SetRotation(float x,float y,float z){
    rotations = glm::vec3(x,y,z);
    UpdateRotationMat();
}

void GObject::BindVBO(VBO invbo){
    this->vbo = invbo;
}

void GObject::Rotate(float x,float y,float z){
    rotations += glm::vec3(x,y,z);
    ///re build rmat
    UpdateRotationMat();
}

VBO 与 VAO

        之后便是VBO(Vertex Buffer Object)与VAO(Vertex Array Object):

        

//VertexObjects.h
///@Copyright aaaa0ggmc 2023
#ifndef VERTEXOBJECTS_H_INCLUDED
#define VERTEXOBJECTS_H_INCLUDED
#include <GL/glew.h>
#include <vector>

namespace me{
    using namespace std;

    class VBO{
    public:
        VBO();
        //上传数据
        vector<GLfloat>* operator=(vector<GLfloat>& v);
        void Set(vector<GLfloat> v);
        void Set(GLfloat *d,size_t sz);
        void bind();
        static void AttributePointer(GLuint bindingIndex,GLuint typesPerI = 3,GLenum type = GL_FLOAT,GLboolean normalized = GL_FALSE,GLsizei stride = 0,const void * pointer = NULL);
        static void EnableArray(GLuint index);
        void BindingTo(GLuint index);
        GLuint GetVBO();
    private:
        friend class VBOs;
        VBO(GLuint v);
        void SetVBO(GLuint v);
        GLuint vbo;
    };

    class VBOs{
    public:
        VBO operator[](unsigned int index);
        vector<VBO> GetVBOs();
        vector<GLuint> GetGLVBOs();
        void AppendVBOs(unsigned int count);
    private:
        vector<VBO> vbos;
    };

    //对VAO不太了解,还没做
    class VertexArrayObject{
    public:

    private:
        vector<VBOs> vbos;
    };
}


#endif // VERTEXOBJECTS_H_INCLUDED
//VertexObjects.cpp
///@Copyright aaaa0ggmc 2023
#include "VertexObjects.h"
#include <iostream>

using namespace std;
using namespace me;

VBO::VBO(){
    vbo = 0;
}

VBO::VBO(GLuint v){
    vbo = v;
}

void VBO::Set(GLfloat d[],size_t sz){
    if(!vbo || !sz)return;
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
    glBufferData(GL_ARRAY_BUFFER,sz,d,GL_STATIC_DRAW);
}

void VBO::Set(vector<GLfloat> v){
    (*this) = v;
}

void VBO::SetVBO(GLuint v){vbo = v;}

GLuint VBO::GetVBO(){return vbo;}

vector<GLfloat>* VBO::operator=(vector<GLfloat> & v){
    if(!vbo)return &v;
    size_t sz = v.size() * sizeof(GLfloat);
    GLfloat * buf = new GLfloat[sz];
    for(unsigned int i =0;i < sz;++i){
        buf[i] = v[i];
    }
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
    glBufferData(GL_ARRAY_BUFFER,sz,buf,GL_STATIC_DRAW);
    delete buf;
    return &v;
}


void VBOs::AppendVBOs(unsigned int c){
    GLuint *gvbo = new GLuint[c];
    glGenBuffers(c,gvbo);
    for(unsigned int i = 0; i < c;++i){
        vbos.push_back(VBO(gvbo[i]));
    }
}

vector<GLuint> VBOs::GetGLVBOs(){
    vector<GLuint> uv;
    for(const VBO & va : vbos){
        uv.push_back(va.vbo);
    }
    return uv;
}

vector<VBO> VBOs::GetVBOs(){return vbos;}

VBO VBOs::operator[](unsigned int index){
    if(index >= vbos.size())return VBO(0);
    return vbos[index];
}

void VBO::bind(){
    glBindBuffer(GL_ARRAY_BUFFER,vbo);
}


void VBO::AttributePointer(GLuint index,GLuint typesPerI,GLenum type,GLboolean normalized,GLsizei stride,const void * pointer){
    glVertexAttribPointer(index,typesPerI,type,normalized,stride,pointer);
}

void VBO::EnableArray(GLuint index){
    glEnableVertexAttribArray(index);
}


void VBO::BindingTo(GLuint index){
    this->bind();
    AttributePointer(index);
    EnableArray(index);
}

以及前面基本上都会用到的Utility

//Utility.h
///@Copyright aaaa0ggmc 2023
#ifndef UTILITY_H_INCED
#define UTILITY_H_INCED
#include <GL/glew.h>
#include <string>
#include <iostream>
#include <unordered_set>


#define ME_DETECT_SIZE 0

#define ME_NO_ERROR 0x00000000
#define ME_OPENGL_ERROR 0x00000001
#define ME_EMPTY_STRING 0x00000010
#define ME_BAD_IO 0x00000100
#define ME_BAD_MEM 0x00001000
#define ME_BAD_TYPE 0x00010000
#define ME_NO_DATA 0x00100000
#define ME_ALREADY_EXI 0x01000000

#define ME_SHADER_VERTEX GL_VERTEX_SHADER
#define ME_SHADER_FRAGMENT GL_FRAGMENT_SHADER
#define ME_SHADER_GEOMETRY GL_GEOMETRY_SHADER

#define ME_SHADER_TYPEC 3


namespace me{
    using namespace std;

    class Counter{
    public:
        Counter(bool start = false);
        float GetCyclePerS();
        void ReStart();
        void Stop();
        void SimpOut();
        void Increase();
        double start,end;
        unsigned long cycles;
    };

    class Utility{
    public:
        static unordered_set<string> sessions;
        static bool initedGlew;
        static bool initedGLFW;
        static bool GetOpenGLError(string&appender,const char * sigStr = "Find OpenGL Error:");
        static void PrintOpenGLError();
        static size_t file_size(const char*filename);
        static int InitGlew();
        static void InitGLFW(int major = 4,int minor = 3);
        static bool GetShaderLog(GLuint shader,string&appender);
        static void PrintShaderLog(GLuint shader);
        static bool GetProgramLog(GLuint prog,string&appender);
        static void PrintProgramLog(GLuint prog);
        static void RegisterTerminal();
        static void OnTerminal();
        static void InvokeConsole(const char * s,bool onlyOnce = false,const char * sessionId = NULL,long = 0);
        Utility() = delete;
        ~Utility() = delete;
        void operator=(Utility&) = delete;
    };
}

#endif // UTILITY_H_INCED
//Utility.cpp
///@Copyright aaaa0ggmc 2023
#include "Utility.h"
#include <sys/stat.h>
#include <GLFW/glfw3.h>
#include <cstdlib>

using namespace me;
using namespace std;

bool me::Utility::initedGlew = false;
bool me::Utility::initedGLFW = false;

bool Utility::GetOpenGLError(std::string&appender,const char * sigStr){
    bool hasError = false;
    GLenum glErr;
    glErr = glGetError();
    while(glErr != GL_NO_ERROR){
        appender.append(sigStr);
        appender.append(to_string(glErr));
        appender.append("
");
        hasError = true;
        glErr = glGetError();
    }
    return hasError;
}

void Utility::PrintOpenGLError(){
    string inner = "";
    if(GetOpenGLError(inner)){
        cout << inner << endl;
    }
}

size_t Utility::file_size(const char*filename){
    struct stat statbuf;
    int ret;
    ret = stat(filename,&statbuf);//调用stat函数
    if(ret != 0) return 0;//获取失败。 2023 6 5:这里有改动
    return statbuf.st_size;//返回文件大小。
}

int Utility::InitGlew(){
    if(!initedGlew){
        int ret = glewInit();
        if(!ret)initedGlew = true;
        return ret;
    }
    return GLEW_OK;
}

void Utility::InitGLFW(int major,int minor){
    if(!initedGLFW){
        initedGLFW = true;
        glfwInit();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,major);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,minor);
    }
}

bool Utility::GetShaderLog(GLuint shader,string&appender){
    int len = 0,chWritten = 0;
    glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&len);
    if(len <= 0)return false;
    char * data = new char[len];
    glGetShaderInfoLog(shader,len,&chWritten,data);
    appender.append(data);
    free(data);
    return true;
}

void Utility::PrintShaderLog(GLuint shader){
    string s = "";
    bool ret = GetShaderLog(shader,s);
    if(ret){
        cout << s << endl;
    }
}


bool Utility::GetProgramLog(GLuint shader,string&appender){
    int len = 0,chWritten = 0;
    glGetProgramiv(shader,GL_INFO_LOG_LENGTH,&len);
    if(len <= 0)return false;
    char * data = new char[len];
    glGetProgramInfoLog(shader,len,&chWritten,data);
    appender.append(data);
    free(data);
    return true;
}

void Utility::RegisterTerminal(){
    std::atexit(Utility::OnTerminal);
}

Counter::Counter(bool st){
    cycles = 0;
    end = 0;
    if(st)ReStart();
}

float Counter::GetCyclePerS(){
    double elapse = (end - start);
    return cycles / elapse;
}

void Counter::ReStart(){
    start = glfwGetTime();
}

void Counter::Stop(){
    end = glfwGetTime();
}

void Counter::Increase(){++cycles;}

void Counter::SimpOut(){
    cout << "FPS average:" << (end-start)*1000 << "ms " << GetCyclePerS() << "cycles/s
";
}

void Utility::OnTerminal(){
    #ifdef DEBUG
        cout << "Game Terminated" << endl;
    #endif
    glfwTerminate();
}

void Utility::PrintProgramLog(GLuint shader){
    string s = "";
    bool ret = GetProgramLog(shader,s);
    if(ret){
        cout << s << endl;
    }
}

unordered_set<string> Utility::sessions;

void Utility::InvokeConsole(const char * s,bool onlyOnce,const char * sessionId,long sig){
    #ifdef DEBUG
        string sd = sessionId?sessionId:"";
        sd += " : ";
        sd += to_string(sig);
        auto it = sessions.find(sd);
        if(onlyOnce &&  it != sessions.end())return;
        if(it == sessions.end() && sessionId)sessions.insert(sd);
        cout << "Invoked[" << sd << "]:" << s << "
";
    #endif // DEBUG
}

以及main和glsl

//main.cpp
///@Copyright aaaa0ggmc
#include <string>
#include <cstdlib>
#include <iostream>
#include <math.h>
#include "com/Window.h"
#include "com/VertexObjects.h"

#define PI 3.1415926
#define rad2deg(r) (180*((r)/PI))
#define deg2rad(d) (((d)/180)*PI)

using namespace std;
using namespace me;
using namespace glm;

#define numVAOs 1
#define numVBOs 2

GLuint vao[numVAOs];
VBOs vbo;
Shader s(false);
Camera cam(0,0,8,true);
GObject cube(0,-2,0),pyramid(-5,2,0);
Window window;
float camSpeed = 10;

void setupVertices(void) {
	float vertexPositions[108] = {
	 -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
	 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
	 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
	 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
	 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
	 -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
	 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
	 -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
	 -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
	 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f,
	 -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
	 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f
	};

	float pyramidPos [54]= {
    -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // front face
	 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // right face
	 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // back face
	 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // left face
	 -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, // base – left front
	 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f // base – right back
	};
	glGenVertexArrays(1, vao);
	glBindVertexArray(vao[0]);
	vbo.AppendVBOs(numVBOs+1);
    vbo[0].Set(vertexPositions,sizeof(vertexPositions));
    vbo[1].Set(pyramidPos,sizeof(pyramidPos));
    cube.BindVBO(vbo[0]);
    pyramid.BindVBO(vbo[1]);
	cam.BuildPerspec(1.0472f, &window , 0.1f, 1000.0f);
}

void display(Window& window, double currentTime,Camera* c) {
    window.Clear();
	s.bind();

	///ISSUE:相机旋转也要负转矩阵吗??
	//cam.SetRotation(0,PI / 4,0);

	cam.UpdateModelMat();
	cube.UpdateModelMat();
	pyramid.UpdateModelMat();

	s["m_matrix"] = cube.mat;
	s["v_matrix"] = c->mat;
	s["proj_matrix"] = cam.perspec;
	s["tf"] = (float)currentTime;

	window.Draw(cube,36);

	s["m_matrix"] = pyramid.mat;
	window.Draw(pyramid,18);
}

void input(Window& w,double elapseus,Camera * c){
    if(w.KeyInputed(GLFW_KEY_SPACE))
        c->MoveDirectional(0,camSpeed*elapseus,0);
    else if(w.KeyInputed(GLFW_KEY_LEFT_SHIFT))
        c->MoveDirectional(0,-camSpeed*elapseus,0);

    if(w.KeyInputed(GLFW_KEY_A))
        c->MoveDirectional(-camSpeed*elapseus,0,0);
    else if(w.KeyInputed(GLFW_KEY_D))
        c->MoveDirectional(camSpeed*elapseus,0,0);

    if(w.KeyInputed(GLFW_KEY_S))
        c->MoveDirectional(0,0,camSpeed*elapseus);
    else if(w.KeyInputed(GLFW_KEY_W))
        c->MoveDirectional(0,0,-camSpeed*elapseus);

    if(w.KeyInputed(GLFW_KEY_Q)){
        cube.Rotate(0,0,deg2rad(0.5));
    }
}

int main(void){
    Utility::RegisterTerminal();

	window.Create(800, 600, "Chapter 4 - program 1" , NULL);
	window.MakeCurrent();
	window.SetPaintFunction(display);
	window.SetFramerateLimit(60);
	window.UseCamera(cam);
	window.OnKeyPressEvent(input);

	s.CreateProgram();
	s.LoadLinkLogF("res/0.vert","res/0.frag");

	setupVertices();

    Counter c(true);
	while (!window.ShouldClose()) {
		window.Display();
        c.Increase();
	}
	c.Stop();
	c.SimpOut();

	window.Destroy();
	return 0;
}
//res/0.frag
#version 430

out vec4 color;
in vec4 vcolor;

void main(){
    color = vcolor;
}
//res/0.vert
#version 430

layout(location = 0) in vec3 pos;

uniform mat4 m_matrix;
uniform mat4 v_matrix;
uniform mat4 proj_matrix;
uniform float tf;

out vec4 vcolor;

mat4 rotateX(float a);
mat4 rotateY(float a);
mat4 rotateZ(float a);
mat4 translate(float x,float y,float z);
mat4 scale(float x,float y,float z);

void main(){
    float i = gl_InstanceID + tf;
    float a = sin(2*i) * 8;
    float b = sin(3*i) * 8;
    float c = sin(4*i) * 8;

    mat4 x = rotateX(i);
    mat4 y = rotateY(i);
    mat4 z = rotateZ(i);

    mat4 trans = translate(a,b,c);

    mat4 mv_matrix = v_matrix * m_matrix * x * y * z;

    gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);
    vcolor = vec4(pos,1.0) * 0.5 + vec4(0.5,0.5,0.5,0.5);
}

mat4 rotateX(float a){
    mat4 x = mat4(
       1.0,0.0,0.0,0.0,
       0.0,cos(a),-sin(a),0.0,
       0.0,sin(a),cos(a),0.0,
       0.0,0.0,0.0,1.0
    );
    return x;
}

mat4 rotateY(float a){
    mat4 y = mat4(
        cos(a),0.0,sin(a),0,
        0.0,1.0,0.0,0.0,
        -sin(a),0.0,cos(a),0.0,
        0.0,0.0,0.0,1.0
    );
    return y;
}

mat4 rotateZ(float a){
    mat4 z = mat4(
    cos(a),-sin(a),0,0,
    sin(a),cos(a),0,0,
    0,0,1,0,
    0,0,0,1
    );
    return z;
}

mat4 translate(float x,float y,float z){
    mat4 t = mat4(
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        x,y,z,1
    );
    return t;
}

mat4 scale(float x,float y,float z){
    mat4 s =  mat4(
        x,0,0,0,
        0,y,0,0,
        0,0,z,0,
        0,0,0,1
    );
    return s;
}

这便是我现在写完的一坨答辩,让我没想到的是引擎我竟然只写了三四个小时左右

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。