您现在的位置是:首页 >技术教程 >OpenGL via C++:glew+glfw3+glm,类引擎开发(一)网站首页技术教程
OpenGL via C++:glew+glfw3+glm,类引擎开发(一)
简介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;
}
这便是我现在写完的一坨答辩,让我没想到的是引擎我竟然只写了三四个小时左右
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。