gpt4 book ai didi

python - 如何使用 PyOpenGL 渲染文本?

转载 作者:行者123 更新时间:2023-12-02 19:14:38 28 4
gpt4 key购买 nike

我正在学习现代 openGL,目前我在渲染文本方面遇到了麻烦。我正在关注这个tutorial这是用 C++ 编写的,但我正在尝试用 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, 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()

到目前为止,我在两个部分遇到了麻烦,我可以理解。 initliaze() 中的第一个问题,以下部分引发错误。

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

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

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

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

最佳答案

您错过了 glUseProgram 安装着色器程序:

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

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

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

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

glBufferSubData(GL_ARRAY_BUFFER, 0, len(顶点), 顶点)

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

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

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)

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

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

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

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

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

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

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

glDrawArrays(GL_TRIANGLES, 0, 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):

投影 = glm.ortho(0.0,640,0.0,640)

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

文本的引用点位于底部。因此,您必须设置大于文本高度的 x 坐标:

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

render_text(window,'hello', 20, 50, 1, (255, 100, 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, 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()

另请参阅FreeType / OpenGL text rendering

关于python - 如何使用 PyOpenGL 渲染文本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63836707/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com