有这个简单的代码:
#include <iostream>
#include <array>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <test/main/shader.hpp>
#include <test/main/texture.hpp>
struct QuadVertex {
glm::vec3 position;
glm::vec4 color;
glm::vec2 texture_coordinates;
float texture_index;
};
struct Data {
static const int max_quads = 10000;
static const int max_vertices = max_quads * 4;
static const int max_indices = max_quads * 6;
static const int max_texture_slots = 32;
Shader *shader = nullptr;
int index_count = 0;
QuadVertex *quad_vb_ptr = nullptr;
std::array<const Texture *, max_texture_slots> textures;
int texture_slot_index = 1.0;
};
static Data data;
void
DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color) {
float texture_index = 0.0f; // white texture
data.quad_vb_ptr->position = position;
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {0.0f, 0.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x + size.x, position.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {1.0f, 0.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x + size.x, position.y + size.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {1.0f, 1.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x, position.y + size.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {0.0f, 1.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.index_count += 6;
}
void DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color, const Texture *texture) {
float texture_index = 0.0f;
for (int i = 1; i < data.textures.size(); i++) {
if (data.textures[i] && *data.textures[i] == *texture) {
texture_index = (float) i;
break;
}
}
if (texture_index == 0.0f) {
texture_index = (float) data.texture_slot_index;
data.textures[data.texture_slot_index] = texture;
data.texture_slot_index++;
}
data.quad_vb_ptr->position = position;
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {0.0f, 0.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x + size.x, position.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {1.0f, 0.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x + size.x, position.y + size.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {1.0f, 1.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.quad_vb_ptr->position = {position.x, position.y + size.y, 0.0f};
data.quad_vb_ptr->color = color;
data.quad_vb_ptr->texture_coordinates = {0.0f, 1.0f};
data.quad_vb_ptr->texture_index = texture_index;
data.quad_vb_ptr++;
data.index_count += 6;
}
int main() {
glfwInit();
GLFWwindow *window = glfwCreateWindow(1280, 720, "test", nullptr, nullptr);
if (window == nullptr) {
std::cerr << "failed to create GLFW window\n";
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *_, int width, int height) {
glViewport(0, 0, width, height);
});
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cerr << "failed to initialize GLAD\n";
}
uint32_t quad_va, quad_vb, quad_ib;
glGenVertexArrays(1, &quad_va);
glBindVertexArray(quad_va);
glGenBuffers(1, &quad_vb);
glBindBuffer(GL_ARRAY_BUFFER, quad_vb);
glBufferData(GL_ARRAY_BUFFER, data.max_vertices * sizeof(QuadVertex), nullptr, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 10 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 10 * sizeof(float),
reinterpret_cast<const void *>(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 10 * sizeof(float),
reinterpret_cast<const void *>(7 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 10 * sizeof(float),
reinterpret_cast<const void *>(9 * sizeof(float)));
glEnableVertexAttribArray(3);
QuadVertex *quad_vb_base = new QuadVertex[data.max_vertices];
data.quad_vb_ptr = quad_vb_base;
int *quad_indices = new int[data.max_indices];
int offset = 0;
for (int i = 0; i < data.max_indices; i += 6) {
quad_indices[i + 0] = offset + 0;
quad_indices[i + 1] = offset + 1;
quad_indices[i + 2] = offset + 2;
quad_indices[i + 3] = offset + 2;
quad_indices[i + 4] = offset + 3;
quad_indices[i + 5] = offset + 0;
offset += 4;
}
glGenBuffers(1, &quad_ib);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ib);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, data.max_indices * sizeof(int), quad_indices, GL_STATIC_DRAW);
delete[] quad_indices;
Texture white_texture(1, 1);
uint32_t white_texture_data = 0xffffffff;
white_texture.SetData(&white_texture_data);
data.textures[0] = &white_texture;
Texture texture("assets/textures/checkerboard.png");
data.shader = new Shader("assets/GLSL/test3.glsl");
data.shader->Bind();
data.shader->SetInt("u_texture", 0);
while (!glfwWindowShouldClose(window)) {
glClearColor(0.1f, 0.1f, 0.1f, 1);
glClear(GL_COLOR_BUFFER_BIT);
data.shader->Bind();
DrawQuad({-0.5f, 0.0f, 0.0f}, {0.5f, 0.8f}, {0.8f, 0.2f, 0.3f, 1.0f});
DrawQuad({0.5f, -0.5f, 0.0f}, {0.5f, 0.75f}, {0.2f, 0.3f, 0.8f, 1.0f});
// DrawQuad({-0.5f, 0.0f, 0.0f}, {0.5f, 0.8f}, glm::vec4(1.0f), &texture); // THIS WORKS, BECAUSE THE TEXTURE IS SET EXPLICITLY (AND THUS NO "WHITE TEXTURE" IS USED)
for (int i = 0; i < data.texture_slot_index; i++) {
data.textures[i]->BindToArray(i);
}
int size = (uint8_t *) data.quad_vb_ptr - (uint8_t *) quad_vb_base;
glBindBuffer(GL_ARRAY_BUFFER, quad_vb);
glBufferSubData(GL_ARRAY_BUFFER, 0, size, quad_vb_base);
glDrawElements(GL_TRIANGLES, data.index_count, GL_UNSIGNED_INT, nullptr);
data.quad_vb_ptr = quad_vb_base;
data.index_count = 0;
glfwSwapBuffers(window);
glfwPollEvents();
}
}
着色器:
#type vertex
#version 330 core
layout(location = 0) in vec3 a_pos;
layout(location = 1) in vec4 a_color;
layout(location = 2) in vec2 a_texcoord;
layout(location = 3) in float a_texindex;
out vec4 v_color;
out vec2 v_texcoord;
out float v_texindex;
void main()
{
v_color = a_color;
v_texcoord = a_texcoord;
v_texindex = a_texindex;
gl_Position = vec4(a_pos, 1.0);
}
#type fragment
#version 330 core
in vec4 v_color;
in vec2 v_texcoord;
in float v_texindex;
out vec4 color;
uniform sampler2DArray u_textures;
void main()
{
color = texture(u_textures, vec3(v_texcoord.xy, v_texindex)) * v_color;
}
未显示的 Shader 类简单地解析包含顶点和片段部分的着色器文件。
DrawQuad(position, size, color, texture)
函数运行正常(即绘制指定纹理)。但它的同胞没有纹理DrawQuad(position, size, color)
,其中未指定纹理,但选择为白色,因为texture_index
设置为0.0f
(其中white_texture
也绑定为0),绘制黑色矩形(所以不工作)。我不确定原因是否是实际数据是0xffffffff
,但这应该不是问题。但作为白色纹理(如 rgba 中的白色全为 1),它不应影响实际颜色(在 DrawQuad 中指定为 3 参数)。那么出了什么问题?
为了完整性,这里是纹理类:
#include <test/main/texture.hpp>
#include <iostream>
#include <glad/glad.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
Texture::Texture(int width, int height) : width_(width), height_(height) {
glGenTextures(1, &renderer_id_);
glBindTexture(GL_TEXTURE_2D, renderer_id_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
InitArray();
}
Texture::Texture(const std::string& filepath) {
stbi_set_flip_vertically_on_load(1);
data_ = stbi_load(filepath.c_str(), &width_, &height_, nullptr, 4);
if (data_ == nullptr) {
std::cerr << "Failed to load image";
}
glGenTextures(1, &renderer_id_);
glBindTexture(GL_TEXTURE_2D, renderer_id_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
InitArray();
}
Texture::~Texture() {
glDeleteTextures(1, &renderer_id_);
}
void Texture::Bind(int slot) const {
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, renderer_id_);
}
void Texture::BindToArray(int slot) const {
if(data_ == nullptr){
std::cerr << "no texture data loaded";
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, slot, width_, height_, 1, GL_RGBA, GL_UNSIGNED_BYTE, data_);
}
void Texture::SetData(void *data) {
data_ = data;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
void *Texture::GetData() const {
return data_;
}
int Texture::GetWidth() const {
return width_;
}
int Texture::GetHeight() const {
return height_;
}
void Texture::InitArray() {
if(array_renderer_id_ == 0){
glGenTextures(1, &array_renderer_id_);
glBindTexture(GL_TEXTURE_2D_ARRAY, array_renderer_id_);
int max_textures;
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_textures);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, width_, height_, max_textures, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
bool Texture::operator==(const Texture& rhs) const {
return renderer_id_ == rhs.renderer_id_;
}
bool Texture::operator!=(const Texture& rhs) const {
return !(rhs == *this);
}