如何使用 PyOpenGL 渲染文本? [英] How to render text with PyOpenGL?

查看:35
本文介绍了如何使用 PyOpenGL 渲染文本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习现代 openGL,目前我在渲染文本方面遇到了麻烦.我正在关注 C++ 中的这个

from OpenGL.GL import *从 OpenGL.GLU 导入 *从 OpenGL.GL 导入着色器进口glfw导入自由类型进口glm将 numpy 导入为 np从 PIL 导入图像导入数学导入时间fontfile = "Vera.ttf";#fontfile = r'C:source
esourcefontsgnu-freefont_freesansfreesans.ttf'类 CharacterSlot:def __init__(self, texture, glyph):self.texture = 纹理self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)if isinstance(glyph, freetype.GlyphSlot):self.bearing = (glyph.bitmap_left, glyph.bitmap_top)self.advance = glyph.advance.xelif isinstance(glyph, freetype.BitmapGlyph):self.bearing = (glyph.left, glyph.top)self.advance = 无别的:raise RuntimeError('未知字形类型')def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):返回 np.asarray([xpos, ypos - h, 0, 0,xpos, ypos, 0, 1,xpos + w, ypos, 1, 1,xpos, ypos - h, 0, 0,xpos + w, ypos, 1, 1,xpos + w, ypos - h, 1, 0], np.float32)VERTEX_SHADER = """#版本330核心vec4 顶点中的布局(位置 = 0);//<vec2 pos, vec2 tex>输出 vec2 TexCoords;均匀的 mat4 投影;无效主(){gl_Position = 投影 * vec4(vertex.xy, 0.0, 1.0);TexCoords = vertex.zw;}"FRAGMENT_SHADER = """#版本330核心在 vec2 TexCoords 中;出 vec4 颜色;统一的 sampler2D 文本;统一的 vec3 textColor;无效主(){vec4 采样 = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);颜色 = vec4(textColor, 1.0) * 采样;}"着色器程序 = 无字符 = dict()VBO = 无VAO = 无定义初始化():全局 VERTEXT_SHADER全局 FRAGMENT_SHADER全局着色程序全局字符全球 VBO全球VAO#编译着色器vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)#创建着色器程序shaderProgram = shaders.compileProgram(顶点着色器,片段着色器)glUseProgram(着色器程序)#获取投影#问题shader_projection = glGetUniformLocation(shaderProgram,投影")投影 = glm.ortho(0, 640, 640, 0)glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))#禁用字节对齐限制glPixelStorei(GL_UNPACK_ALIGNMENT,1)face = freetype.Face(字体文件)face.set_char_size( 48*64 )#加载 ASCII 集的前 128 个字符对于范围内的 i (0,128):face.load_char(chr(i))字形 = face.glyph#生成纹理纹理 = glGenTextures(1)glBindTexture(GL_TEXTURE_2D,纹理)glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,GL_RED、GL_UNSIGNED_BYTE、glyph.bitmap.buffer)#纹理选项glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)#现在存储字符以备后用Characters[chr(i)] = CharacterSlot(texture,glyph)glBindTexture(GL_TEXTURE_2D,0)#为纹理四边形配置 VAO/VBOVAO = glGenVertexArrays(1)glBindVertexArray(VAO)VBO = glGenBuffers(1)glBindBuffer(GL_ARRAY_BUFFER,VBO)glBufferData(GL_ARRAY_BUFFER,6 * 4 * 4,无,GL_DYNAMIC_DRAW)glEnableVertexAttribArray(0)glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 无)glBindBuffer(GL_ARRAY_BUFFER, 0)glBindVertexArray(0)def render_text(window,text,x,y,scale,color):全局着色程序全局字符全球 VBO全球VAOface = freetype.Face(字体文件)face.set_char_size(48*64)glUniform3f(glGetUniformLocation(shaderProgram, textColor"),颜色[0]/255,颜色[1]/255,颜色[2]/255)glActiveTexture(GL_TEXTURE0)启用(GL_BLEND)glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)glBindVertexArray(VAO)对于文本中的 c:ch = 字符[c]w, h = ch.textureSizew = w*比例h = h*比例顶点 = _get_rendering_buffer(x,y,w,h)#在四边形上渲染字形纹理glBindTexture(GL_TEXTURE_2D,ch.texture)#更新VBO内存的内容glBindBuffer(GL_ARRAY_BUFFER,VBO)glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)glBindBuffer(GL_ARRAY_BUFFER, 0)#渲染四边形glDrawArrays(GL_TRIANGLES, 0, 6)#now 为下一个字形前进光标(请注意,前进是 1/64 像素的数量)x += (ch.advance>>6)*scaleglBindVertexArray(0)glBindTexture(GL_TEXTURE_2D,0)glfw.swap_buffers(窗口)glfw.poll_events()定义主():glfw.init()window = glfw.create_window(640, 640,示例程序",无,无)glfw.make_context_current(window)初始化()而不是 glfw.window_should_close(window):glfw.poll_events()glClearColor(0,0,0,1)glClear(GL_COLOR_BUFFER_BIT)render_text(window,'hello', 20, 50, 1, (255, 100, 100))glfw.terminate()如果 __name__ == '__main__':主要的()


另见 FreeType/OpenGL 文本渲染

I'm learning modern openGL, and at this moment I'm facing trouble with rendering text. I'm following this tutorial which is in C++, but I'm trying to implement in python.

Here is my code:

from OpenGL.GL import *
from OpenGL.GLU import *

from OpenGL.GL import shaders

import glfw
import freetype
import glm

import numpy as np
from PIL import Image
import math
import time


class CharacterSlot:
    def __init__(self, texture, glyph):
        self.texture = texture
        self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)

        if isinstance(glyph, freetype.GlyphSlot):
            self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
            self.advance = glyph.advance.x
        elif isinstance(glyph, freetype.BitmapGlyph):
            self.bearing = (glyph.left, glyph.top)
            self.advance = None
        else:
            raise RuntimeError('unknown glyph type')

def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
    return np.asarray([
        xpos, ypos - h, zfix, 0.0, 1.0,
        xpos, ypos, zfix, 0.0, 0.0,
        xpos + w, ypos, zfix, 1.0, 0.0,
        xpos, ypos - h, zfix, 0.0, 1.0,
        xpos + w, ypos, zfix, 1.0, 0.0,
        xpos + w, ypos - h, zfix, 1.0, 1.0
    ], np.float32)


VERTEX_SHADER = """
        #version 330 core
        layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
        out vec2 TexCoords;

        uniform mat4 projection;

        void main()
        {
            gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
            TexCoords = vertex.zw;
        }


       """

FRAGMENT_SHADER = """
        #version 330 core
        in vec2 TexCoords;
        out vec4 color;

        uniform sampler2D text;
        uniform vec3 textColor;

        void main()
        {    
            vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
            color = vec4(textColor, 1.0) * sampled;
        }

        """

shaderProgram = None
Characters = dict()
VBO = None
VAO = None

def initliaze():
    global VERTEXT_SHADER
    global FRAGMENT_SHADER
    global shaderProgram
    global Characters
    global VBO
    global VAO

    #compiling shaders
    vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
    fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)

    #creating shaderProgram
    shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)

    #get projection
    #problem
    
    shader_projection = glGetUniformLocation(shaderProgram, "projection")
    projection = glm.ortho(0.0,640,0.0,640)
    glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));
    
    #disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    face = freetype.Face("Vera.ttf")
    face.set_char_size( 48*64 )

    #load first 128 characters of ASCII set
    for i in range(0,128):
        face.load_char(chr(i))
        glyph = face.glyph
        
        #generate texture
        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexImage2D(GL_TEXTURE_2D,
                     0,
                     GL_RGB,
                     glyph.bitmap.width, glyph.bitmap.rows,
                     0,
                     GL_RGB,
                     GL_UNSIGNED_BYTE,
                     glyph.bitmap.buffer)

        #texture options
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        #now store character for later use
        Characters[chr(i)] = CharacterSlot(texture,glyph)
        
    glBindTexture(GL_TEXTURE_2D, 0);

    #configure VAO/VBO for texture quads
    VAO = glGenVertexArrays(1)
    glBindVertexArray(VAO)
    
    VBO = glGenBuffers(1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    
def render_text(window,text,x,y,scale,color):
    global shaderProgram
    global Characters
    global VBO
    global VAO
    
    face = freetype.Face("Vera.ttf")
    face.set_char_size(48*64)
    glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
                color[0]/255,color[1]/255,color[2]/255)
               
    glActiveTexture(GL_TEXTURE0);
    

    for c in text:
        ch = Characters[c]
        w,h = ch.textureSize
        w = w*scale
        h = w*scale
        vertices = _get_rendering_buffer(x,y,w,h)

        #render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.texture);
        #update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        #render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);
        #now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.advance+6)*scale;

    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);

    glfwSwapBuffers(window);
    glfwPollEvents();
    
def main():
    glfw.init()
    window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)
    
    glfw.make_context_current(window)

    
    initliaze()
    while not glfw.window_should_close(window):
        glfw.poll_events()
        glClearColor(0,0,0,1);
        glClear(GL_COLOR_BUFFER_BIT);
        render_text(window,'hello',1,1,1,(100,100,100))

        
    glfw.terminate()


if __name__ == '__main__':
    main()

I'm facing trouble in two portion so far I can understand. The first problem in initliaze(), error raised for the following portion.

    shader_projection = glGetUniformLocation(shaderProgram, "projection")
    projection = glm.ortho(0.0,640,0.0,640)
    glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection));

I've commented out the above portion to ignore. The second problem is in render_text() function, error raised for the following portion.

glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
                color[0]/255,color[1]/255,color[2]/255)

There could be problems in many more places. I don't understand that why text rendering will be so difficult. What am I missing here?

解决方案

You missed to install the shader program by glUseProgram:

shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
glUseProgram(shaderProgram) # <---


The 2nd argument to glBufferData and the 3rd argument of glBufferSubData is the size in bytes:

glBufferData(GL_ARRAY_BUFFER, 6 * 4, None, GL_DYNAMIC_DRAW)

glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)

glBufferSubData(GL_ARRAY_BUFFER, 0, len(vertices), vertices)

glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)

The vertex attribute consist of a 2 dimension vertex coordinate (x, y) and a 2 dimensional texture coordinate. Remove the wird zfix from the array of vertex attribute data. Furthermore you have to flip the 2nd component of the texture coordinates (otherwise the text is upside down)

def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
    return np.asarray([
        xpos,     ypos - h, 0, 0,
        xpos,     ypos,     0, 1,
        xpos + w, ypos,     1, 1,
        xpos,     ypos - h, 0, 0,
        xpos + w, ypos,     1, 1,
        xpos + w, ypos - h, 1, 0
    ], np.float32)

The stride argument of glVertexAttribIPointer has to be specified in bytes. If stride is 0, the generic vertex attributes are understood to be tightly packed in the array. Hence in your case stride has to be 16 or 0:

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4, 0)

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)

face.load_char(chr(i)) generates a image with on color channel (1 byte per pixel). Use the internal format and format GL_RED rather than GL_RGB for generating the 2 dimensional texture image:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
             GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer) 

You have to bind the vertex array, before drawing the text:

glBindVertexArray(VAO)
for c in text:
    # [...]

    glDrawArrays(GL_TRIANGLES, 0, 6)

There is typo when you increment x, you have to use the >>-operator rather than the +-operator:

x += (ch.advance+6)*scale

x += (ch.advance>>6)*scale

and another typo when you compute h:

h = w*scale

h = h*scale

You have to enable alpha blending:

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

In NDC (normalized device coordinates) the left bottom is (-1, -1) and the right top is (1, 1). Set the orthographic projection in that way, that the top left of the window is at (0, 0):

projection = glm.ortho(0.0,640,0.0,640)

projection = glm.ortho(0, 640, 640, 0)

The reference point of the text is at the bottom. Hence you have to set a x coordinate greater than the text height:

render_text(window,'hello',1,1,1,(100,100,100))

render_text(window,'hello', 20, 50, 1, (255, 100, 100))


See the complete example (I've used a different font):

from OpenGL.GL import *
from OpenGL.GLU import *

from OpenGL.GL import shaders

import glfw
import freetype
import glm

import numpy as np
from PIL import Image
import math
import time


fontfile = "Vera.ttf"
#fontfile = r'C:source
esourcefontsgnu-freefont_freesansfreesans.ttf'

class CharacterSlot:
    def __init__(self, texture, glyph):
        self.texture = texture
        self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)

        if isinstance(glyph, freetype.GlyphSlot):
            self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
            self.advance = glyph.advance.x
        elif isinstance(glyph, freetype.BitmapGlyph):
            self.bearing = (glyph.left, glyph.top)
            self.advance = None
        else:
            raise RuntimeError('unknown glyph type')

def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
    return np.asarray([
        xpos,     ypos - h, 0, 0,
        xpos,     ypos,     0, 1,
        xpos + w, ypos,     1, 1,
        xpos,     ypos - h, 0, 0,
        xpos + w, ypos,     1, 1,
        xpos + w, ypos - h, 1, 0
    ], np.float32)


VERTEX_SHADER = """
        #version 330 core
        layout (location = 0) in vec4 vertex; // <vec2 pos, vec2 tex>
        out vec2 TexCoords;

        uniform mat4 projection;

        void main()
        {
            gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
            TexCoords = vertex.zw;
        }
       """

FRAGMENT_SHADER = """
        #version 330 core
        in vec2 TexCoords;
        out vec4 color;

        uniform sampler2D text;
        uniform vec3 textColor;

        void main()
        {    
            vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);
            color = vec4(textColor, 1.0) * sampled;
        }
        """

shaderProgram = None
Characters = dict()
VBO = None
VAO = None

def initliaze():
    global VERTEXT_SHADER
    global FRAGMENT_SHADER
    global shaderProgram
    global Characters
    global VBO
    global VAO

    #compiling shaders
    vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
    fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)

    #creating shaderProgram
    shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
    glUseProgram(shaderProgram)

    #get projection
    #problem
    
    shader_projection = glGetUniformLocation(shaderProgram, "projection")
    projection = glm.ortho(0, 640, 640, 0)
    glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))
    
    #disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)

    face = freetype.Face(fontfile)
    face.set_char_size( 48*64 )

    #load first 128 characters of ASCII set
    for i in range(0,128):
        face.load_char(chr(i))
        glyph = face.glyph
        
        #generate texture
        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
                     GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)

        #texture options
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

        #now store character for later use
        Characters[chr(i)] = CharacterSlot(texture,glyph)
        
    glBindTexture(GL_TEXTURE_2D, 0)

    #configure VAO/VBO for texture quads
    VAO = glGenVertexArrays(1)
    glBindVertexArray(VAO)
    
    VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_DYNAMIC_DRAW)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    
def render_text(window,text,x,y,scale,color):
    global shaderProgram
    global Characters
    global VBO
    global VAO
    
    face = freetype.Face(fontfile)
    face.set_char_size(48*64)
    glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),
                color[0]/255,color[1]/255,color[2]/255)
               
    glActiveTexture(GL_TEXTURE0)
    
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

    glBindVertexArray(VAO)
    for c in text:
        ch = Characters[c]
        w, h = ch.textureSize
        w = w*scale
        h = h*scale
        vertices = _get_rendering_buffer(x,y,w,h)

        #render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.texture)
        #update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        #render quad
        glDrawArrays(GL_TRIANGLES, 0, 6)
        #now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.advance>>6)*scale

    glBindVertexArray(0)
    glBindTexture(GL_TEXTURE_2D, 0)

    glfw.swap_buffers(window)
    glfw.poll_events()
    
def main():
    glfw.init()
    window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)    
    glfw.make_context_current(window)
    
    initliaze()
    while not glfw.window_should_close(window):
        glfw.poll_events()
        glClearColor(0,0,0,1)
        glClear(GL_COLOR_BUFFER_BIT)
        render_text(window,'hello', 20, 50, 1, (255, 100, 100))

    glfw.terminate()

if __name__ == '__main__':
    main()


See also FreeType / OpenGL text rendering

这篇关于如何使用 PyOpenGL 渲染文本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆