文章目录
LearnGL - 学习笔记目录
- VAO 结构
- GL LIKE 测试项目
- gl_like.h
- gl_like_gv.h
- gl_like.cpp
- Main.cpp
- 运行效果
本人才疏学浅,如有什么错误,望不吝指出。
上一篇:LearnGL - 03 - DrawQuad - VBO/EBO,学会了如何使用 EBO/IBO 画一个四边形。
这一篇:我将使用C++写个类似 OpenGL 在使用 VAO 期间的功能:
VAO 结构VAO的结果差不多就如下图:
我模仿 OpenGL 一部分的 API 编写了测试项目:
gl_like.h// gl_like.h
// jave.lin - 测试模仿 opengl 的规范,没有参考 opengl specification 规范,只是自己的理解
// 不做过多的封装,只为简单的理解结束结构,大致的功能
//(什么 private fields, public getter/setter都不封装),实际项目时是不能这么封装的
#ifndef _GL_LIKE__H_
#define _GL_LIKE__H_
#include
#include
#include
// 获取地址类型对象指针
#define GET_POINSTER_BY_VALUE(type, addressValue) ((type)((void*)addressValue))
// 获取指针对象地址值
#define GET_ADDRESS_VALUE(address) ((uint)address);
//
// symbol constants - 符号常量,参考 glad 的定义
//
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_STATIC_DRAW 0x88E4
#define GL_BYTE 0x1400
#define GL_UNSIGNED_BYTE 0x1401
#define GL_SHORT 0x1402
#define GL_UNSIGNED_SHORT 0x1403
#define GL_INT 0x1404
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_POINTS 0x0000
#define GL_LINES 0x0001
#define GL_LINE_LOOP 0x0002
#define GL_LINE_STRIP 0x0003
#define GL_TRIANGLES 0x0004
#define GL_TRIANGLE_STRIP 0x0005
#define GL_TRIANGLE_FAN 0x0006
//
// custom symbol constants - 符号常量,自定义的,暂时还没有看到的参考资料
//
#define GL_MAX_NUM_OF_VAO 4096
#define GL_MAX_NUM_OF_VBO 4096
#define GL_TRUE 1
#define GL_FALSE 0
#define GL_INVALIDATED_CONTEXT 1
#define GL_OUT_BO_MAX_COUNT 2
#define GL_CREATE_FAILURE 3
#define GL_CONTEXT_NOT_NULL 4
#define GL_BINDING_UN_HANDLE_BUFF_TYPE 5
#define GL_SET_BUFF_VALUES_BUFF_NULL 6
//
// declaretion - empty - 空定义
//
//
// typdef - 参考 glad 的 typedef 别名(C++中的话,使用using会更好,因为支持template,但typedef一直到c会更好)
//
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef void GLvoid;
typedef signed char GLbyte;
typedef unsigned char GLubyte;
typedef signed short GLshort;
typedef unsigned short GLushort;
typedef int GLint;
typedef unsigned int GLuint;
typedef int GLclampx;
typedef int GLsizei;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void* GLeglClientBufferEXT;
typedef void* GLeglImageOES;
typedef char GLchar;
typedef char GLcharARB;
typedef signed long int GLsizeiptr;
typedef GLbyte* _buffer_t;
typedef GLubyte* _ubuffer_T;
//
// declaretion - sold
//
class _BuffObject {
public:
_ubuffer_T data = NULL;
GLsizei size = 0;
GLsizei capacity = 0;
~_BuffObject();
};
class BufferObject { // 缓存对象
public:
_BuffObject* buf = NULL;
~BufferObject();
};
class VertexAttributeFormat {
public:
GLuint index = -1; // location 索引
GLint numOfComponent = 0; // 分量的数量
GLenum typeOfComponent = 0; // 分量的类型
GLboolean normalized = 0; // 分量值是否归一化
GLsizei stride = 0; // 顶点属性所在数据数组的每个分段大小
void* pointer = 0; // 在对应分段的字节偏移
GLuint setupToVBO = 0; // 安装到vbo
~VertexAttributeFormat();
};
class VertexArrayObject { // VAO
public:
BufferObject* vbo = NULL; // VBO 指针
BufferObject* ebo = NULL; // EBO 指针
std::vector* vafs = NULL; // VBO 格式(规范)
std::vector* vafs_enabled = NULL; // 启用的 VBO 格式
VertexArrayObject();
~VertexArrayObject();
};
class Context {
public:
GLuint major = 0;
GLuint minor = 0;
VertexArrayObject* bindingVAO = NULL; // 绑定之后的VAO
VertexArrayObject* defalultVAO = NULL; // 未绑定的VAO
std::vector* VAOs = NULL; // 已申请的所有 VAO 对象
std::vector* BOs = NULL; // 已申请的所有 BO 对象
Context();
~Context();
inline VertexArrayObject* GetUsingVAO() const; // 获取当前用的VAO
};
//
// function - others
//
void Terminate();
void MakeCurrentContext(GLuint context);
void CheckBuffType(GLenum buffType);
void Error(const GLuint ec, const GLchar* msg);
GLboolean CreateContext(GLuint major, GLuint minor, GLuint* context);
void glGenVertexArrays(GLsizei n, GLuint* arrays);
void glDeleteVertexArrays(GLsizei n, GLuint* arrays);
void glBindVertexArray(GLuint array);
void glGenBuffers(GLsizei n, GLuint* buffers);
void glDeleteBuffers(GLsizei n, GLuint* buffers);
void glBindBuffer(GLenum target, GLuint buffer);
void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
void glEnableVertexAttribArray(GLuint index);
void glDisableVertexAttribArray(GLuint index);
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
#endif // #ifndef _GL_LIKE__H_
gl_like_gv.h
// gl_like_gv.h
// jave.lin - global variables
#include"gl_like.h"
#ifndef _GL_LIKE_GV__H_
#define _GL_LIKE_GV__H_
Context* currentContext = NULL;
GLint errorCode = 0;
GLchar* errorMsg = NULL;
#endif
gl_like.cpp
// gl_like.cpp
// jave.lin - 定义以及实现
// 实现不一定是按 OpenGL 来的,都只是自己看了一些官方文档了解功能后,而编写的功能
// 所以会出现,只实现了部分的功能,会之不对的功能
#include
#include
#include
#include"gl_like_gv.h"
#include"gl_like.h"
extern Context* currentContext;
extern GLint errorCode;
extern GLchar* errorMsg;
//
// function - definition
//
/* 暂时没用上,这时对 glVertexAttribPointer 是的 normalized 来确定要不要归一化数据额类型的,
需要再传入(复制)到 GPU 前就得处理的归一化
// jave.lin : 这是处理数据类型归一化到单精度的过程
// 有符号数据类型归一化处理
template
inline GLfloat Normalized_signed(DataType value) {
// jave.lin : 对应 Katex 是: f = \frac{c}{2^b-1}
return (GLfloat)((value) / (std::pow(2, sizeof(DataType) * 8 - 1)));
}
// 无符号数据类型归一化处理
template
inline GLfloat Normalized_unsigned(DataType value) {
// jave.lin : 对应 Katex 是: f = \frac{2c + 1}{2^b-1}
return (GLfloat)((value * 2 + 1) / (std::pow(2, sizeof(DataType) * 8 - 1)));
}
*/
void PrintVertex(GLuint vertexID, VertexAttributeFormat* format, _ubuffer_T buffer) {
std::cout
begin();
std::vector::iterator end_va0 = this->VAOs->end();
for (; begin_vao != end_va0; begin_vao++) {
delete* begin_vao;
}
this->VAOs->clear();
this->VAOs = NULL;
}
if (this->BOs != NULL) {
std::vector::iterator begin_vbo = this->BOs->begin();
std::vector::iterator end_vb0 = this->BOs->end();
for (; begin_vbo != end_vb0; begin_vbo++) {
delete* begin_vbo;
}
this->BOs->clear();
this->BOs = NULL;
}
if (this->defalultVAO != NULL) {
delete this->defalultVAO;
this->defalultVAO = NULL;
}
this->bindingVAO = NULL;
} // ~Context
void Terminate() {
if (currentContext != NULL) {
delete currentContext;
currentContext = NULL;
}
} // Terminater
void MakeCurrentContext(GLuint context) {
currentContext = (Context*)((void*)context);
} // MakeCurrentContext
void CheckBuffType(GLenum buffType) {
switch (buffType)
{
case GL_ARRAY_BUFFER: // VBO
break;
case GL_ELEMENT_ARRAY_BUFFER: // EBO
break;
default:
GLchar str[512];
sprintf_s(str, "binding unhandle buffer type : %d", buffType);
Error(GL_BINDING_UN_HANDLE_BUFF_TYPE, (const GLchar*)str);
break;
} // end switch
} // end CheckBuffType
void Error(const GLuint ec, const GLchar* msg) {
errorCode = ec;
if (errorMsg == NULL) {
errorMsg = (GLchar*)malloc(512);
}
strcpy_s(errorMsg, 512, msg);
throw msg;
} // end Error
GLboolean CreateContext(GLuint major, GLuint minor, GLuint* context) {
if (currentContext != NULL) {
Error(GL_CONTEXT_NOT_NULL, (const GLchar*)"context not null");
return GL_FALSE;
}
Context* p = new Context();
*context = (GLuint) (p);
if (p == NULL) {
Error(GL_CREATE_FAILURE, (const GLchar*)"create context failure");
return GL_FALSE;
}
p->major = major;
p->minor = minor;
return GL_TRUE;
} // end CreateContext
//
// gllike api
//
void glGenVertexArrays(GLsizei n, GLuint* arrays) {
*arrays = 0;
if (currentContext == NULL) {
Error(GL_INVALIDATED_CONTEXT, (const GLchar*)"context == null");
return;
}
if ((currentContext->VAOs->size() + n) >= GL_MAX_NUM_OF_VAO) {
Error(GL_OUT_BO_MAX_COUNT, (const GLchar*)"out of vao buffer object max count");
return;
}
for (size_t i = 0; i VAOs->push_back(vao);
}
}
} // end glGenVertexArrays
void glDeleteVertexArrays(GLsizei n, GLuint* arrays) {
for (size_t i = 0; i VAOs->begin();
std::vector::const_iterator end = currentContext->VAOs->end();
for (; begin != end; begin++) {
if (*begin == p) {
currentContext->VAOs->erase(begin);
break;
}
}
delete p;
*(arrays + i) = 0;
}
} // end glDeleteVertexArrays
void glBindVertexArray(GLuint array) {
currentContext->bindingVAO = (VertexArrayObject*)array;
currentContext->defalultVAO->vbo = NULL;
currentContext->defalultVAO->ebo = NULL;
if (currentContext->defalultVAO->vafs != NULL) {
std::vector::const_iterator begin_vaf, end_vaf;
begin_vaf = currentContext->defalultVAO->vafs->begin();
end_vaf = currentContext->defalultVAO->vafs->end();
for (; begin_vaf != end_vaf; begin_vaf++) {
delete *begin_vaf;
}
currentContext->defalultVAO->vafs->clear();
}
if (currentContext->defalultVAO->vafs_enabled != NULL) {
currentContext->defalultVAO->vafs_enabled->clear();
}
} // end glBindVertexArray
void glGenBuffers(GLsizei n, GLuint* buffers) {
*buffers = 0;
if (currentContext == NULL) {
Error(GL_INVALIDATED_CONTEXT, (const GLchar*)"context == null");
return;
}
if ((currentContext->BOs->size() + n) >= GL_MAX_NUM_OF_VBO) {
Error(GL_OUT_BO_MAX_COUNT, (const GLchar*)"out of bo buffer object max count");
return;
}
for (size_t i = 0; i BOs->push_back(vbo);
}
}
} // glGenBuffers
void glDeleteBuffers(GLsizei n, GLuint* buffers) {
for (size_t i = 0; i BOs->begin();
std::vector::const_iterator end = currentContext->BOs->end();
for (; begin != end; begin++) {
if (*begin == p) {
currentContext->BOs->erase(begin);
break;
}
}
delete p;
*(buffers + i) = 0;
}
} // glDeleteBuffers
void glBindBuffer(GLenum target, GLuint buffer) {
CheckBuffType(target);
switch (target)
{
case GL_ARRAY_BUFFER: // VBO
currentContext->GetUsingVAO()->vbo = (BufferObject*)(buffer);
break;
case GL_ELEMENT_ARRAY_BUFFER: // EBO
currentContext->GetUsingVAO()->ebo = (BufferObject*)(buffer);
break;
default:
throw "un handle glBindBuffer target type";
break;
}
} // glBindBuffer
void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
CheckBuffType(target);
// usage 参数不使用,因为这个与硬件加速相关
GLboolean validated = true;
_BuffObject** buffObj = NULL;
// 目前只对两种缓存类型做处理
switch (target)
{
case GL_ARRAY_BUFFER: // VBO
buffObj = &(currentContext->GetUsingVAO()->vbo->buf);
break;
case GL_ELEMENT_ARRAY_BUFFER: // EBO
buffObj = &(currentContext->GetUsingVAO()->ebo->buf);
break;
default:
validated = false;
GLchar str[512];
sprintf_s(str, "binding unhandle buffer type : %d", target);
Error(GL_BINDING_UN_HANDLE_BUFF_TYPE, (const GLchar*)str);
break;
} // end switch
if (validated) {
if (*buffObj == NULL) {
*buffObj = new _BuffObject();
}
if ((*buffObj)->size data == NULL) {
(*buffObj)->data = (_ubuffer_T)malloc(size);
}
else {
(*buffObj)->data = (_ubuffer_T)realloc((void*)((*buffObj)->data), size);
}
(*buffObj)->capacity = size;
}
(*buffObj)->size = size;
memcpy((*buffObj)->data, data, size);
}
} // end glBufferData
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer) {
std::vector* pVec = currentContext->GetUsingVAO()->vafs;
std::vector::const_iterator begin = pVec->begin();
std::vector::const_iterator end = pVec->end();
bool found = false;
VertexAttributeFormat* p = NULL;
for (; begin != end; begin++) {
p = *begin;
if (p->index == index) { // 先看看目前有无重复设置的
found = true; // 有重复的
break;
}
}
if (!found) { // 没有重复的,则新建一个
p = new VertexAttributeFormat;
p->index = index;
pVec->push_back(p);
}
if (pVec->size() > 128) { // 假装最多只能有 128 个 vertex attribute
GLchar str[512];
sprintf_s(str, "glVertexAttribPointer number of vertex attribute more than 128");
throw str;
}
p->numOfComponent = size;
p->typeOfComponent = type;
p->normalized = normalized;
p->stride = stride;
p->pointer = (void*)pointer;
p->setupToVBO = (GLuint)((void*)currentContext->GetUsingVAO()->vbo); // 记录安装时的 VBO 是哪个
} // end glVertexAttribPointer
void glEnableVertexAttribArray(GLuint index) {
std::vector* pVec = currentContext->GetUsingVAO()->vafs_enabled; // 启用 attribute 的数据记录
std::vector::const_iterator begin = pVec->begin();
std::vector::const_iterator end = pVec->end();
VertexAttributeFormat* p = NULL;
bool found = false;
for (; begin != end; begin++) {
if ((*begin) == index) { // 如果本身是启用的,则不添加
found = true;
break;
}
}
if (found == false) { // 如果没有启用,则添加
pVec->push_back(index);
}
} // end glEnableVertexAttribArray
void glDisableVertexAttribArray(GLuint index) {
std::vector* pVec = currentContext->GetUsingVAO()->vafs_enabled;
std::vector::const_iterator begin = pVec->begin();
std::vector::const_iterator end = pVec->end();
VertexAttributeFormat* p = NULL;
for (; begin != end; begin++) {
if ((*begin) == index) {
pVec->erase(begin); // 找到启用的 attribute 索引,就删除
break;
}
}
} // end glDisableVertexAttribArray
void glDrawArrays(GLenum mode, GLint first, GLsizei count) {
if (mode == GL_TRIANGLES) { // 目前只支持三角形
std::cout end(); // 顶点规范的启用索引数组
end_vaf = vao->vafs->end();
// 遍历顶点数
for (size_t i = 0; i vafs_enabled->begin();
for (; begin_enabled_index != end_enabled_index; begin_enabled_index++) {
GLboolean found = false;
// 查找属性索引对应的格式(规范)
begin_vaf = vao->vafs->begin();
for (; begin_vaf != end_vaf; begin_vaf++) {
if ((*begin_vaf)->index == *begin_enabled_index) {
found = true;
break;
}
}
// 如果找不到启用的 attribute,则报错(OpenGL 这里会使用 静态的顶点属性,这里就不实现了,直接报错)
if (!found) {
GLchar str[512];
sprintf_s(str, "glDrawArrays mode is triangle, but not found attribute loation : %d", *begin_enabled_index);
throw str;
}
// 取格式(规范)中绑定的 VBO
BufferObject* vbo = (BufferObject*)((*begin_vaf)->setupToVBO);
// 取 VBO 中的数据
_ubuffer_T buffer = (vbo->buf->data);
// 取顶点的偏移,按 stride 来偏移
// 这里为了测试 VAO 整体应用,只打印一下数据即可
// 其实也可以再写个 C++ 版本的软渲染器来替换这里的打印也是可以的
GLuint offset = ((GLuint)((*begin_vaf)->stride)) * i;
PrintVertex(i, *begin_vaf, buffer + offset);
}
}
}
else {
GLchar str[512];
sprintf_s(str, "unhandle glDrawArrays mode : %d", mode);
throw str;
}
} // end glDrawArrays
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
if (mode == GL_TRIANGLES) { // 目前只支持三角形
std::cout vafs_enabled->end();
end_vaf = vao->vafs->end();
if (type != GL_UNSIGNED_INT) {
GLchar str[512];
sprintf_s(str, "glDrawElements unhandle type : %d", type);
throw str;
}
GLuint* indexBuffer = (GLuint*)(ebo->buf->data + (GLuint)indices); // 真正的索引缓存数据
// 根据索引遍历顶点数
for (size_t i = 0; i vafs_enabled->begin();
for (; begin_enabled_index != end_enabled_index; begin_enabled_index++) {
GLboolean found = false;
// 查找属性索引对应的格式(规范)
begin_vaf = vao->vafs->begin();
for (; begin_vaf != end_vaf; begin_vaf++) {
if ((*begin_vaf)->index == *begin_enabled_index) {
found = true;
break;
}
}
if (!found) {
GLchar str[512];
sprintf_s(str, "glDrawElements mode is triangle, but not found attribute loation : %d", *begin_enabled_index);
throw str;
}
// 取格式(规范)中绑定的 VBO
BufferObject* vbo = (BufferObject*)((*begin_vaf)->setupToVBO);
// 取 VBO 中的数据
_ubuffer_T buffer = (vbo->buf->data);
// 取顶点的偏移,按 stride 来偏移
// 这里为了测试 VAO 整体应用,只打印一下数据即可
// 其实也可以再写个 C++ 版本的软渲染器来替换这里的打印也是可以的
GLuint offset = ((GLuint)((*begin_vaf)->stride)) * vertexIndex; // 使用索引值来偏移 VBO 数据
PrintVertex(i, *begin_vaf, buffer + offset);
}
}
}
else {
GLchar str[512];
sprintf_s(str, "unhandle glDrawElements mode : %d", mode);
throw str;
}
} // end glDrawElements
Main.cpp
// Main.cpp
// jave.lin - 测试自己写的 gllike,目前为了测试 VAO/VBO/EBO 等接口的使用
#include
#include
#include"gl_like.h"
extern Context* currentContext;
extern GLint errorCode;
extern GLchar* errorMsg;
void PrintGlobalError() {
GLchar errorCodeMeans[512] = { 0 };
switch (errorCode)
{
case GL_INVALIDATED_CONTEXT: strcpy_s(errorCodeMeans, 512, "Invalidated context"); break;
case GL_OUT_BO_MAX_COUNT: strcpy_s(errorCodeMeans, 512, "Out of buffer object max count"); break;
case GL_CREATE_FAILURE: strcpy_s(errorCodeMeans, 512, "Create / alloc failure"); break;
case GL_CONTEXT_NOT_NULL: strcpy_s(errorCodeMeans, 512, "Context not null"); break;
case GL_BINDING_UN_HANDLE_BUFF_TYPE: strcpy_s(errorCodeMeans, 512, "Binding unhandle buffer type"); break;
case GL_SET_BUFF_VALUES_BUFF_NULL: strcpy_s(errorCodeMeans, 512, "Buffer is NULL, while Setting buffer"); break;
default:
break;
}
if (errorCode != 0) {
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?