dongpeihui1051 2015-12-24 17:09
浏览 212

使用OpenGL渲染Truetype字体

I am starting to learn OpenGL now, using the Go programming language (I just couldn't get working with C/C++ on my Windows machine), and so far I've managed to display some rotating cubes in the screen with textures mainly copying and pasting code from tutorials. I've learned a lot, though, but I just can't get some text on the screen with this code that I wrote on my own. I've looked up many tutorials and questions but nothing seems to work, and I suspect there is something wrong with the vertices because I'm pretty sure the textures coordinates are correct and still there's nothing showing up in the screen. Here's the code:

package game

import (
  "fmt"
  "io/ioutil"
  "image"
  "image/draw"
  "github.com/go-gl/gl/v3.3-core/gl"
  mgl "github.com/go-gl/mathgl/mgl32"
  "github.com/golang/freetype/truetype"
  "golang.org/x/image/font"
  "golang.org/x/image/math/fixed"
)

type GameFont struct {
  loaded bool
  vao uint32
  vbo VBOData
  pix float32
  Texture *Texture
  Shader ShaderProgram
}


// Load a TrueType font from a file and generate a texture 
// with all important characters.
func (f *GameFont) Load(path string, pix float32) {
  contents, err := ioutil.ReadFile(path)
  if err != nil {
    fmt.Println("Could not read font file: " + path)
    panic(err)
  }
  fontFace, err := truetype.Parse(contents)
  if err != nil {
    fmt.Println("Could not parse font file: " + path)
    panic(err)
  }

  // Create a texture for the characters
  // Find the next power of 2 for the texture size
  size := nextP2(int(pix * 16))
  fg, bg := image.White, image.Black
  rgba := image.NewRGBA(image.Rect(0, 0, size, size))

  draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
  d := &font.Drawer{
    Dst: rgba,
    Src: fg,
    Face: truetype.NewFace(fontFace, &truetype.Options{
      Size: float64(pix),
      DPI: 72,
      Hinting: font.HintingNone,
    }),
  }

  // Some GL preps
  gl.GenVertexArrays(1, &f.vao)
  gl.BindVertexArray(f.vao)
  f.vbo.Create()
  f.vbo.Bind()

  f.Shader = newShaderProgram("data/shaders/font.vert", "data/shaders/font.frag")
  f.Shader.Use()
  f.Shader.SetUniform("tex", 0)

  // Create vertex data (and coordinates in the texture) for each character
  // All characters below 32 are useless
  for i := 32; i < 128; i++ {
    c := string(rune(i))
    x, y := i % 16, i / 16

    // Draw the character on the texture
    d.Dot = fixed.P(x * int(pix), y * int(pix))
    d.DrawString(c)

    // Vertices
    quads := []float32{
      0,    0,
      0,    pix,
      pix,  0,
      pix,  pix,
    }

    norm := func(n int) float32 {
      return float32(n) / 16.0
    }

    // Texture coordinates (normalized)
    texQuads := []float32{
      norm(x),      1.0 - norm(y + 1),
      norm(x),      1.0 - norm(y),
      norm(x + 1),  1.0 - norm(y + 1),
      norm(x + 1),  1.0 - norm(y),
    }

    for v := 0; v < 8; v += 2 {
      vQuads, vTexQuads := quads[v:(v+2)], texQuads[v:(v+2)]

      // Data is like (X, Y, U, V)
      f.vbo.AppendData(vQuads, 2)
      f.vbo.AppendData(vTexQuads, 2)
    }
  }

  // Upload data to GPU and we're done
  f.Texture = newTextureFromRGBA(rgba)
  f.Texture.Bind()
  f.Texture.SetGLParam(gl.TEXTURE_MIN_FILTER, gl.LINEAR)
  f.Texture.SetGLParam(gl.TEXTURE_MAG_FILTER, gl.LINEAR)
  f.Texture.Upload()

  f.vbo.UploadData(gl.STATIC_DRAW)

  gl.EnableVertexAttribArray(0)
  gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(0))
  gl.EnableVertexAttribArray(1)
  gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(2*4))

  f.loaded = true
}

// Render a text using the font
func (f *GameFont) Render(text string, x, y int, pix float32, color mgl.Vec4) {
  if !f.loaded {
    return
  }

  gl.Disable(gl.DEPTH_TEST)
  gl.Enable(gl.BLEND)
  gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  gl.BindVertexArray(f.vao)

  f.Shader.Use()
  f.Shader.SetUniform("projection", mgl.Ortho2D(0, _screen.Width, 0, _screen.Height))
  f.Shader.SetUniform("color", color)

  f.Texture.Bind()

  scale := pix / f.pix
  for i := 0; i < len(text); i++ {
    index := rune(text[i])
    model := mgl.Ident4().Mul4(mgl.Scale3D(scale, scale, 0))
    model = model.Add(mgl.Translate3D(float32(x) + float32(i) * pix, float32(y), 0))
    f.Shader.SetUniform("model", model)
    gl.DrawArrays(gl.TRIANGLE_STRIP, (32-index)*4, 4)
  }

  gl.Enable(gl.DEPTH_TEST)
  gl.Disable(gl.BLEND)
}

Here's the shaders:

Vertex shader

#version 330
uniform mat4 projection;
uniform mat4 model;

layout (location = 0) in vec2 vert;
layout (location = 1) in vec2 vertTexCoord;

out vec2 fragTexCoord;

void main() {
  fragTexCoord = vertTexCoord;
  gl_Position = projection * model * vec4(vert, 0, 1);
}

Fragment shader

#version 330

uniform sampler2D tex;
uniform vec4 color;

in vec2 fragTexCoord;
out vec4 outputColor;

void main() {
  outputColor = color * texture(tex, fragTexCoord);
}

Every "component" of the GameFont struct is working properly (I've used them with the rotating cubes), so every function calls the GL corresponding one.

Also the texture is being drawed correctly, I've saved it to the disk and it looks like this:

enter image description here

And still, there's no text on the screen.

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序
    • ¥15 onvif+openssl,vs2022编译openssl64
    • ¥15 iOS 自定义输入法-第三方输入法
    • ¥15 很想要一个很好的答案或提示
    • ¥15 扫描项目中发现AndroidOS.Agent、Android/SmsThief.LI!tr
    • ¥15 怀疑手机被监控,请问怎么解决和防止
    • ¥15 Qt下使用tcp获取数据的详细操作
    • ¥15 idea右下角设置编码是灰色的
    • ¥15 全志H618ROM新增分区
    • ¥15 在grasshopper里DrawViewportWires更改预览后,禁用电池仍然显示