I am attempting to render the following image in OpenGL:
(disregard the white line, that is not part of the image)
To render, I have a still texture of the text as a PNG, shown below:
At the moment, this is my Cargo.toml
:
[package]
name = "render-clock"
version = "0.1.0"
edition = "2021"
[dependencies]
elara-gfx = { git = "https://github.com/elaraproject/elara-gfx" }
This is my vertex shader code:
#version 100
attribute highp vec2 position;
attribute highp vec2 tex_coord;
attribute lowp vec4 vertex_color;
varying highp vec2 TexCoord;
varying lowp vec4 VertexColor;
void main() {
VertexColor = vertex_color;
TexCoord = tex_coord;
gl_Position = vec4(position.x * 0.66, position.y, 0.0, 1.0);
}
And my fragment shader:
#version 100
varying highp vec2 TexCoord;
varying lowp vec4 VertexColor;
uniform sampler2D uTexture;
void main() {
lowp vec4 col = texture2D(uTexture, TexCoord);
gl_FragColor = vec4(VertexColor.rgb, col.a);
}
And my program code:
use elara_gfx::{Buffer, BufferType, Program, Shader, VertexArray, PixelArray, Texture2D};
use elara_gfx::{GLWindow, HandlerResult, WindowHandler};
use std::error::Error;
const VERT_SHADER: &str = include_str!("shaders/clock.vert"); // code given above
const FRAG_SHADER: &str = include_str!("shaders/clock.frag"); // code given above
const IMG_PATH: &str = "clock_text.png"; // image shown above
struct Handler {
vao: VertexArray,
texture: Texture2D
}
impl Handler {
fn new() -> Result<Handler, String> {
let vertices: [f32; 42] = [
// positions // colors // texture coords
0.5, 0.5, 0.13, 0.15, 0.16, 1.0, 1.0, // top right
0.5, -0.5, 0.13, 0.15, 0.16, 1.0, 0.0, // bottom right
-0.5, 0.5, 0.13, 0.15, 0.16, 0.0, 1.0, // top left
0.5, -0.5, 0.13, 0.15, 0.16, 1.0, 0.0, // bottom right
-0.5, -0.5, 0.13, 0.15, 0.16, 0.0, 0.0, // bottom left
-0.5, 0.5, 0.13, 0.15, 0.16, 0.0, 1.0, // top left
];
let texture = Texture2D::new()?;
texture.bind();
texture.parameter_2d(gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
texture.parameter_2d(gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
let mut img = PixelArray::load_png(IMG_PATH).unwrap();
img.flipv();
texture.set_image_2d(img);
texture.generate_mipmap();
let vao = VertexArray::new()?;
vao.bind();
let vbo = Buffer::new()?;
vbo.bind(BufferType::Array);
vbo.data::<f32>(BufferType::Array, &vertices, gl::STATIC_DRAW);
let vertex_shader = Shader::new(&VERT_SHADER, gl::VERTEX_SHADER)?;
let fragment_shader = Shader::new(&FRAG_SHADER, gl::FRAGMENT_SHADER)?;
let program = Program::new(&[vertex_shader, fragment_shader])?;
program.use_program();
let pos_attrib = vao.get_attrib_location(&program, "position");
let col_attrib = vao.get_attrib_location(&program, "vertex_color");
let tex_coord_attrib = vao.get_attrib_location(&program, "tex_coord");
vao.enable_vertex_attrib(pos_attrib as u32);
vao.enable_vertex_attrib(col_attrib as u32);
vao.enable_vertex_attrib(tex_coord_attrib as u32);
vao.vertex_attrib_pointer::<f32>(pos_attrib as u32, 2, gl::FLOAT, false, 7, 0);
vao.vertex_attrib_pointer::<f32>(col_attrib as u32, 3, gl::FLOAT, false, 7, 2);
vao.vertex_attrib_pointer::<f32>(tex_coord_attrib as u32, 2, gl::FLOAT, false, 7, 5);
Ok(Handler {vao, texture})
}
}
impl WindowHandler for Handler {
fn on_draw(&mut self) -> HandlerResult<()> {
unsafe {
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
self.texture.bind();
self.vao.bind();
gl::DrawArrays(gl::TRIANGLES, 0, 6);
self.vao.unbind();
self.texture.unbind();
}
Ok(())
}
}
fn main() -> Result<(), Box<dyn Error>> {
let (app, window) = GLWindow::new_with_title("OpenGL clock")?;
window.get_context()?;
let render_handler = Handler::new()?;
app.run_loop(window, render_handler);
Ok(())
}
My dilemna is that to render my desired image would require rendering 2 quads, one of the text with transparency, and underneath, a rectangle quad filled with solid color. The text quad would need to have texture coordinates, but the rectangle quad shouldn't have (and doesn't need) texture coordinates. However, trying to render both with a single shader program/VAO/VBO seems like something OpenGL cannot do. Do I have to resort to using 2 VAOs/VBOs/shader programs to layer the text quad on the rectangle quad? Is there a better way?
There is nothing wrong with using 2 or more VAOs/VBOs/shader programs. Any realistic application will use dozens of them. But it is also fairly easy to render these two rectangles using a single pass: you just need to add more triangles with outside triangles having texture coordinates corresponding to transparent part of the texture (0, 0).