我正在尝试将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" }
型
1条答案
按热度按时间gywdnpxw1#
答案比我预想的要复杂得多。实际上是因为两个问题。
首先,顶点不在标准化的屏幕坐标中,这意味着任何渲染的内容都在视口之外。所以我需要手动转换它们:
字符串
第二,这一个更复杂,我使用的库有一个问题,它定义为:
型
正确的代码应该是:
型
这是我通过使用
c2rust
将源代码的工作C版本翻译到Rust才实现的。