easy-gl › Textured Quad
Textured Quad
Demonstrates loading a 2D texture and rendering it onto a quad with index buffer. Uses alpha blending for transparent textures.
Shaders
const char* vertSrc = R"(#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUV;
out vec2 vUV;
void main() {
gl_Position = vec4(aPos, 0.0, 1.0);
vUV = aUV;
})";
const char* fragSrc = R"(#version 330 core
in vec2 vUV;
uniform sampler2D uTex;
out vec4 FragColor;
void main() {
FragColor = texture(uTex, vUV);
})";
Quad geometry (indexed)
constexpr float quadVerts[] = {
// pos uv
-0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 1.0f,
};
constexpr unsigned int indices[] = { 0, 1, 2, 2, 3, 0 };
Setup
easygl::Program prog(vertSrc, fragSrc);
// VAO + VBO + IBO
easygl::VertexArray vao;
easygl::Buffer vbo, ibo;
vao.create(); vbo.create(); ibo.create();
vao.bind();
vbo.bind(easygl::BufferTarget::Array);
vbo.set_data(easygl::BufferTarget::Array, quadVerts, sizeof(quadVerts));
ibo.bind(easygl::BufferTarget::ElementArray);
ibo.set_data(easygl::BufferTarget::ElementArray, indices, sizeof(indices));
constexpr std::size_t stride = 4 * sizeof(float);
vao.set_attribute({0, 2, easygl::DataType::Float, false, stride, 0, true});
vao.set_attribute({1, 2, easygl::DataType::Float, false, stride, 2 * sizeof(float), true});
// Texture (assume stb_image loaded pixels into imageData)
easygl::Texture tex;
tex.create();
tex.bind(easygl::TextureTarget::Texture2D);
tex.set_parameter(easygl::TextureTarget::Texture2D,
easygl::TextureParameter::MinFilter,
static_cast<int>(easygl::TextureMinFilter::LinearMipmapLinear));
tex.set_parameter(easygl::TextureTarget::Texture2D,
easygl::TextureParameter::MagFilter,
static_cast<int>(easygl::TextureMagFilter::Linear));
tex.set_storage_2d(easygl::TextureTarget::Texture2D, 4,
easygl::InternalFormat::Rgba8, imgWidth, imgHeight);
tex.set_sub_image_2d(easygl::TextureTarget::Texture2D,
0, 0, 0, imgWidth, imgHeight,
easygl::PixelFormat::Rgba,
easygl::PixelType::UnsignedByte,
imageData);
tex.generate_mipmap(easygl::TextureTarget::Texture2D);
// Enable alpha blending
device.set_blend_enabled(true);
device.set_blend_func(easygl::BlendFactor::SrcAlpha,
easygl::BlendFactor::OneMinusSrcAlpha);
Render loop draw call
device.set_clear_color(0.1f, 0.1f, 0.1f, 1.0f);
device.clear(easygl::ClearFlags::Color);
prog.use();
tex.active_bind(easygl::TextureUnit::Texture0,
easygl::TextureTarget::Texture2D);
prog.set_uniform(prog.uniform_location("uTex"), 0); // sampler2D = unit 0
vao.bind();
device.draw_elements(easygl::PrimitiveType::Triangles,
6, easygl::DataType::UnsignedInt, nullptr);
Key points
- The IBO is bound while the VAO is bound — it becomes part of the VAO state.
set_storage_2d+set_sub_image_2dis preferred overset_image_2dbecause the storage is immutable and avoids later resizing.active_bindactivates the texture unit and binds in one call.- The sampler uniform receives the integer unit index (0), not the
TextureUnitenum.