如何使用PyOpenGL渲染文本?

问题描述

我正在学习现代的openGL,目前我在呈现文本时遇到了麻烦。我正在使用C ++中的tutorial,但是我正在尝试在python中实现。

这是我的代码

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,xpos + w,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,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,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,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,GL_RGB,glyph.bitmap.width,glyph.bitmap.rows,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_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,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,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,len(vertices),vertices)

        glBindBuffer(GL_ARRAY_BUFFER,0);
        #render quad
        glDrawArrays(GL_TRIANGLES,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,"EXAMPLE PROGRAM",None)
    
    glfw.make_context_current(window)

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

        
    glfw.terminate()


if __name__ == '__main__':
    main()

到目前为止,我可以分两部分来面对麻烦。 initliaze()中的第一个问题是以下部分的错误

    shader_projection = glGetUniformlocation(shaderProgram,glm.value_ptr(projection));

我已注释掉上面的部分以忽略。第二个问题是在 render_text()函数中,以下部分出现了错误

gluniform3f(glGetUniformlocation(shaderProgram,color[2]/255)

更多地方可能存在问题。我不明白为什么文本渲染会如此困难。我在这里想念什么?

解决方法

您没有通过glUseProgram安装着色器程序:

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

glBufferData的第二个参数和glBufferSubData的第三个参数的大小为以字节为单位

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

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

glBufferSubData(GL_ARRAY_BUFFER,len(vertices),vertices)

glBufferSubData(GL_ARRAY_BUFFER,vertices.nbytes,vertices)

顶点属性由一个二维顶点坐标(x,y)和一个二维纹理坐标组成。从顶点属性数据数组中删除zfix。此外,您必须翻转纹理坐标的第二部分(否则文本将上下颠倒)

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

glVertexAttribIPointer stride 参数必须以字节为单位。如果 stride 为0,则将通用顶点属性理解为紧密包装在数组中。因此,在您的情况下,步幅必须为16或0:

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

glVertexAttribPointer(0,None)

face.load_char(chr(i))生成具有彩色通道的图像(每个像素1字节)。使用内部格式和格式GL_RED而不是GL_RGB来生成二维纹理图像:

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

在绘制文本之前,必须绑定顶点数组:

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

    glDrawArrays(GL_TRIANGLES,6)

增加x时会出现错字,您必须使用>>运算符而不是+运算符:

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

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

和计算h时的另一种错字:

h = w*scale

h = h*scale

您必须启用Alpha blending

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)

在NDC(规范化设备坐标)中,左下角是(-1,-1),右上角是(1、1)。以这种方式设置正交投影,使窗口的左上角位于(0,0):

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

projection = glm.ortho(0,0)

文本的参考点在底部。因此,您必须将x坐标设置为大于文本高度:

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

render_text(window,20,50,(255,100))

查看完整的示例(我使用了其他字体):

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\resource\fonts\gnu-freefont_freesans\freesans.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,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,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,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,0)
    glUniformMatrix4fv(shader_projection,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,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_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,GL_DYNAMIC_DRAW)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(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,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,vertices)

        glBindBuffer(GL_ARRAY_BUFFER,0)
        #render quad
        glDrawArrays(GL_TRIANGLES,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,"EXAMPLE PROGRAM",None)    
    glfw.make_context_current(window)
    
    initliaze()
    while not glfw.window_should_close(window):
        glfw.poll_events()
        glClearColor(0,1)
        glClear(GL_COLOR_BUFFER_BIT)
        render_text(window,100))

    glfw.terminate()

if __name__ == '__main__':
    main()

另请参阅FreeType / OpenGL text rendering

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...