OpenGL文本渲染不会在屏幕上显示任何输出的文本

sauutmhj  于 2023-08-04  发布在  其他
关注(0)|答案(1)|浏览(118)

我正在尝试将this OpenGL文本渲染类移植到Rust,这是我到目前为止所做的:

use elara_gfx::{gl_info, Shader, Program, VertexArray, Buffer, BufferType, Color, Uniform, WindowHandler, HandlerResult};
use freetype::Library;
use freetype::face::LoadFlag;
use std::collections::HashMap;
use std::error::Error;
use elara_gfx::GLWindow;
use elara_gfx::types::c_void;

const VERTEX_SHADER: &'static str = r#"
#version 330 core
in vec4 vertex;
out vec2 TexCoord;

void main() {
    gl_Position = vec4(vertex.xy, 0.0, 1.0);
    TexCoord = vertex.zw;
}
"#;

const FRAGMENT_SHADER: &'static str = r#"
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D text;
uniform vec3 textColor;

void main() {
    vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoord).r);
    FragColor = vec4(textColor, 1.0) * sampled;
}
"#;

#[derive(Debug)]
struct Character {
    pub texture_id: i32,
    pub size: (i32, i32),
    pub bearing: (i32, i32),
    pub advance: i32
}

impl Character {
    fn new(texture_id: i32, size: (i32, i32), bearing: (i32, i32), advance: i32) -> Character {
        Character { texture_id, size, bearing, advance }
    }
}

struct TextRenderer {
    program: Program,
    vao: VertexArray,
    vbo: Buffer,
    characters: HashMap<char, Character>
}

impl TextRenderer {
    fn new() -> Result<TextRenderer, String> {
        let vertex_shader = Shader::new(&VERTEX_SHADER, gl::VERTEX_SHADER)?;
        let fragment_shader = Shader::new(&FRAGMENT_SHADER, gl::FRAGMENT_SHADER)?;
        let program = Program::new(&[vertex_shader, fragment_shader])?;

        let vao = VertexArray::new()?;
        let vbo = Buffer::new()?;
        vao.bind();
        vbo.bind(BufferType::Array);
        vbo.data_empty::<f32>(BufferType::Array, 24, gl::DYNAMIC_DRAW);

        let vertex_attrib = vao.get_attrib_location(&program, "vertex");
        vao.enable_vertex_attrib(vertex_attrib as u32);
        vao.vertex_attrib_pointer::<f32>(vertex_attrib as u32, 4, gl::FLOAT, false, 4, 0);

        vbo.unbind(BufferType::Array);
        vao.unbind();

        let characters: HashMap<char, Character> = HashMap::new();

        Ok(TextRenderer { program, vao, vbo, characters })
    }

    fn load(&mut self, font: &str, size: u32) {
        if !self.characters.is_empty() {
            self.characters.clear();
        }

        let ft = Library::init().unwrap();
        let face = ft.new_face(font, 0).unwrap();
        face.set_pixel_sizes(0, size).unwrap();
        unsafe {
            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
        }
        // Enable blending
        unsafe {
            gl::Enable(gl::CULL_FACE);
            gl::Enable(gl::BLEND);
            gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
        }

        // Load first 128 characters of ASCII set
        for c in 0..128 as u8 {
            face.load_char(c as usize, LoadFlag::RENDER).unwrap();
            unsafe {
                let mut texture = 0;
                gl::GenTextures(1, &mut texture);
                gl::BindTexture(gl::TEXTURE_2D, texture);
                gl::TexImage2D(
                    gl::TEXTURE_2D,
                    0,
                    gl::RED as i32,
                    face.glyph().bitmap().width(),
                    face.glyph().bitmap().rows(),
                    0,
                    gl::RED,
                    gl::UNSIGNED_BYTE,
                    face.glyph().bitmap().buffer().as_ptr() as *const c_void
                );
                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);

                let character = Character::new(
                    texture as i32,
                    (face.glyph().bitmap().width(), face.glyph().bitmap().rows()),
                    (face.glyph().bitmap_left(), face.glyph().bitmap_top()),
                    face.glyph().advance().x as i32
                );

                self.characters.insert(c as char, character);
            }
        }
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
    }

    fn render_text(&self, text: &str, mut x: f32, y: f32, scale: f32, color: Color) -> Result<(), String> {
        self.program.use_program();
        let color_uniform = Uniform::new(&self.program, "textColor")?;
        color_uniform.uniform3f(color.0 as f32 / 255.0, color.1 as f32 / 255.0, color.2 as f32 / 255.0);
        unsafe {
            gl::ActiveTexture(gl::TEXTURE0);
        }
        self.vao.bind();
        for c in text.chars() {
            let ch = self.characters.get(&c).unwrap();
            let xpos = x + ch.bearing.0 as f32 * scale;
            let ypos = y - (ch.size.1 as f32 - ch.bearing.1 as f32) * scale;
            let w = ch.size.0 as f32 * scale;
            let h = ch.size.1 as f32 * scale;
            let vertices: [f32; 24] = [
                xpos,     ypos + h,   0.0_f32, 0.0,            
                xpos,     ypos,       0.0,     1.0,
                xpos + w, ypos,       1.0,     1.0,

                xpos,     ypos + h,   0.0,     0.0,
                xpos + w, ypos,       1.0,     1.0,
                xpos + w, ypos + h,   1.0,     0.0  
            ];
            unsafe {
                gl::BindTexture(gl::TEXTURE_2D, ch.texture_id as u32);
                self.vbo.bind(BufferType::Array);
                self.vbo.subdata(BufferType::Array, 0, &vertices);
                gl::DrawArrays(gl::TRIANGLES, 0, 6);
                x += (ch.advance >> 6) as f32 * scale;
                self.vbo.unbind(BufferType::Array);
            }
        }
        self.vao.unbind();
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, 0);
        }
        Ok(())
    }
}

struct Handler {
    renderer: TextRenderer   
}

impl Handler {
    fn new() -> Result<Handler, String> {
        let mut renderer = TextRenderer::new()?;
        renderer.load("resources/OpenSans-Regular.ttf", 48);
        Ok(Handler { renderer })
    }
}

impl WindowHandler for Handler {
    fn on_draw(&mut self) -> HandlerResult<()> {
        unsafe {
            gl::ClearColor(0.2, 0.3, 0.3, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
            self.renderer.render_text("This is some sample text", 0.0, 0.0, 1.0, Color(255, 255, 255, 1.0))?;
        }
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let (app, window) = GLWindow::new_with_title("Hi OpenGL!")?;
    window.get_context()?;
    gl_info();
    
    let render_handler = Handler::new()?;

    // Event handling
    app.run_loop(window, render_handler);
    Ok(())
}

字符串
我当前的代码不向屏幕输出任何文本。我检查了glGetError(),它显示没有OpenGL错误。我怀疑问题是空的预分配的VBO,我没有正确填写数据。任何与此有关的帮助都将不胜感激。
(另外,为了重现这个例子,这是我的cargo.toml):

[package]
name = "font-rendering"
version = "0.1.0"
edition = "2021"

[dependencies]
elara_gfx = { git = "https://github.com/elaraproject/elara-gfx.git" }

gywdnpxw

gywdnpxw1#

答案比我预想的要复杂得多。实际上是因为两个问题。
首先,顶点不在标准化的屏幕坐标中,这意味着任何渲染的内容都在视口之外。所以我需要手动转换它们:

let xpos = (x + ch.bearing.0 as f32 * scale) / width;
let ypos = (y - (ch.size.1 as f32 - ch.bearing.1 as f32) * scale) / height;
let w = (ch.size.0 as f32 * scale) / width;
let h = (ch.size.1 as f32 * scale) / height;

字符串
第二,这一个更复杂,我使用的库有一个问题,它定义为:

pub fn subdata<T>(&self, buffer_type: BufferType, offset: isize, data: &[T]) {
    unsafe {
        gl::BufferSubData(
            buffer_type as types::GLenum,
            offset,
            std::mem::size_of_val(&data) as isize,
            data.as_ptr() as *const types::c_void
        )
    }
}


正确的代码应该是:

pub fn subdata<T, const N: usize>(&self, buffer_type: BufferType, offset: isize, data: &[T; N]) 
{
    unsafe {
        gl::BufferSubData(
            buffer_type as types::GLenum,
            offset,
            std::mem::size_of::<[T; N]>() as isize,
            data.as_ptr() as *const types::c_void
        )
    }
}


这是我通过使用c2rust将源代码的工作C版本翻译到Rust才实现的。

相关问题