It's not the most optimal way to draw many quads, but it works for one textured quad. You might have to change the vertex order in vertex_data, from 021 to 012 and 032 to 023.
void draw(GLuint shader_program, size_t x, size_t y, size_t win_width, size_t win_height)
{
y = win_height - y;
glBindTexture(GL_TEXTURE_2D, tex_handle);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
float hp_ratio = static_cast<float>(this->hit_points) / static_cast<float>(this->max_hit_points);
RGB_uchar rgb = HSBtoRGB(static_cast<unsigned short int>(100.0f*hp_ratio),
static_cast<unsigned char>(100),
static_cast<unsigned char>(100));
for (size_t i = 1; i < width - 1; i++)
{
for (size_t j = 1; j < height - 1; j++)
{
size_t index = 4 * (j * width + i);
if (i == 1 || i == 2 || j == 1 || j == 2 || i == width - 2 || j == height - 2 || i == width - 3 || j == height - 3)
{
texture_data[index + 0] = rgb.r;
texture_data[index + 1] = rgb.g;
texture_data[index + 2] = rgb.b;
texture_data[index + 3] = 255;
}
}
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, static_cast<GLsizei>(width), static_cast<GLsizei>(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &amp;texture_data[0]);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
complex<float> v0w(static_cast<float>(x), static_cast<float>(y));
complex<float> v1w(static_cast<float>(x), static_cast<float>(y + this->height));
complex<float> v2w(static_cast<float>(x + this->width), static_cast<float>(y + this->height));
complex<float> v3w(static_cast<float>(x + this->width), static_cast<float>(y));
complex<float> v0ndc = get_ndc_coords_from_window_coords(win_width, win_height, v0w);
complex<float> v1ndc = get_ndc_coords_from_window_coords(win_width, win_height, v1w);
complex<float> v2ndc = get_ndc_coords_from_window_coords(win_width, win_height, v2w);
complex<float> v3ndc = get_ndc_coords_from_window_coords(win_width, win_height, v3w);
vector<GLfloat> vertex_data = {
// 3D position, 2D texture coordinate
v0ndc.real(), v0ndc.imag(), 0, 0, 1, // vertex 0
v2ndc.real(), v2ndc.imag(), 0, 1, 0, // vertex 2
v1ndc.real(), v1ndc.imag(), 0, 0, 0, // vertex 1
v0ndc.real(), v0ndc.imag(), 0, 0, 1, // vertex 0
v3ndc.real(), v3ndc.imag(), 0, 1, 1, // vertex 3
v2ndc.real(), v2ndc.imag(), 0, 1, 0 // vertex 2
};
GLuint components_per_vertex = 5;
GLuint components_per_position = 3;
GLuint components_per_tex_coord = 2;
GLuint num_vertices = static_cast<GLuint>(vertex_data.size()) / components_per_vertex;
glBindBuffer(GL_ARRAY_BUFFER, vbo_handle);
glBufferData(GL_ARRAY_BUFFER, vertex_data.size() * sizeof(GLfloat), &amp;vertex_data[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(glGetAttribLocation(shader_program, "position"));
glVertexAttribPointer(glGetAttribLocation(shader_program, "position"),
components_per_position,
GL_FLOAT,
GL_FALSE,
components_per_vertex * sizeof(GLfloat),
NULL);
glEnableVertexAttribArray(glGetAttribLocation(shader_program, "texcoord"));
glVertexAttribPointer(glGetAttribLocation(shader_program, "texcoord"),
components_per_tex_coord,
GL_FLOAT,
GL_TRUE,
components_per_vertex * sizeof(GLfloat),
(const GLvoid*)(components_per_position * sizeof(GLfloat)));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_handle);
glUniform1i(glGetUniformLocation(shader_program, "tex"), 0);
glDrawArrays(GL_TRIANGLES, 0, num_vertices);
}