OpenGL抽象了VBO、VAO、EBO、UBO以及shader等,都是Object概念,在项目里面,对于这些Object都进行再一次封装,原因有两个
- 为了OO的编程范式
- 便于以后更换OpenGL为其他的渲染引擎
1 VBO抽象定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
class VertexBuffer { public: virtual ~VertexBuffer() = default;
virtual void Bind() const = 0; virtual void Unbind() const = 0;
virtual const BufferLayout& GetLayout() const = 0; virtual void SetLayout(const BufferLayout& layout) = 0;
virtual void SetData(const void* data, uint32_t size) = 0;
static X::Ref<VertexBuffer> Create(float* vertices, uint32_t size);
static X::Ref<VertexBuffer> Create(uint32_t size); };
|
2 显存空间的开辟
VBO的本质是显存的一片连续空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
OpenGLVertexBuffer::OpenGLVertexBuffer(float *vertices, uint32_t size) { X_PROFILE_FUNCTION(); glGenBuffers(1, &m_rendererID); glBindBuffer(GL_ARRAY_BUFFER, m_rendererID);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); }
|
3 内存拷贝到显存
VBO的作用是把内存数据传输到显存
1 2 3 4 5
| void OpenGLVertexBuffer::Bind() const { X_PROFILE_FUNCTION(); glBindBuffer(GL_ARRAY_BUFFER, m_rendererID); }
|
4 顶点属性
为了渲染,还得告诉OpenGL这些顶点信息的布局情况,映射shader的attribute
4.1 Attribute的封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
struct BufferElement {
std::string name;
ShaderDataType type; uint32_t size;
size_t offset; bool normalized;
BufferElement() = default;
BufferElement(ShaderDataType type, const std::string& name, bool normalized = false) : name(name), type(type), size(ShaderDataTypeSize(type)), offset(0), normalized(normalized) {}
uint32_t GetComponentCount() const { switch (type) { case ShaderDataType::kNone: { return 0; } case ShaderDataType::kFloat: { return 1; } case ShaderDataType::kFloat2: { return 2; } case ShaderDataType::kFloat3: { return 3; } case ShaderDataType::kFloat4: { return 4; } case ShaderDataType::kMat3: { return 3; } case ShaderDataType::kMat4: { return 4; } case ShaderDataType::kInt: { return 1; } case ShaderDataType::kInt2: { return 2; } case ShaderDataType::kInt3: { return 3; } case ShaderDataType::kInt4: { return 4; } case ShaderDataType::kBool: { return 1; } } X_CORE_ASSERT(false, "Unknown ShaderDataType!"); return 0; } };
|
4.2 多个attribute怎么放的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
class BufferLayout { public: BufferLayout() {}
BufferLayout(const std::initializer_list<BufferElement>& elements) : m_elements(elements) { calculateOffsetsAndStride(); }
uint32_t GetStride() const { return m_stride; }
const std::vector<BufferElement>& GetElements() const { return m_elements; }
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
std::vector<BufferElement>::iterator end() { return m_elements.end(); }
std::vector<BufferElement>::const_iterator begin() const { return m_elements.begin(); }
std::vector<BufferElement>::const_iterator end() const { return m_elements.end(); }
private: void calculateOffsetsAndStride() { size_t offset = 0; m_stride = 0; for (auto& element : m_elements) { element.offset = offset; offset += element.size; m_stride += element.size; } }
private: std::vector<BufferElement> m_elements; uint32_t m_stride = 0; };
|
4.3 VBO得知道attribute的布局
1 2 3
| void SetLayout(const BufferLayout& layout) override { m_bufferLayout = layout; }
|