Pyglet HUD 文本定位/缩放

Pyglet HUD text location / scaling

背景

我有一个基于 this example

的 HUD 游戏

我有一个带有两个 gluOrtho2D 视图的 pyglet 应用程序。一个以 1/1 的比例映射到屏幕,用作我的 HUD。一个映射到游戏世界,所以它可以缩放。

问题

当游戏世界中的某个玩家在某个位置渲染时,我想在HUD中显示这个玩家的名字。但是 HUD 对世界对象的渲染位置一无所知,世界对屏幕的缩放一无所知。

问题

有没有办法找出在屏幕上呈现某些内容的位置,或者是否有可能找出或简单地抵消上下文中的当前缩放设置?

最小示例代码

进口

from pyglet import clock, window, font
from pyglet.gl import *

相机class 解决了 HUD 和 2D 世界之间的差异

class Camera(object):

    def __init__(self, win):
        self.win = win

    def project_world(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluOrtho2D(-500, 1000, -500, 500)

    def project_hud(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluOrtho2D(0, self.win.width, 0, self.win.height)

HUD 包含在没有视点的情况下呈现的所有内容。

class Hud(object):

    def __init__(self, win: window.Window):
        self.fps = clock.ClockDisplay()
        self.helv = font.load('Helvetica', 12.0)
        self.text = font.Text(self.helv, "This text does not scale", \
                              x=0, y=0, color=(1, 1, 1, 0.5))

    def draw(self):
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.text.draw()

包含从玩家视角呈现的所有内容的世界

class World(object):

    def __init__(self):
        self.helv = font.load('Helvetica', 12.0)
        self.text = font.Text(self.helv, "This text should not scale", \
                              x=0, y=0, color=(1, 1, 1, 0.5))

    def draw(self):
        glClear(GL_COLOR_BUFFER_BIT)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.text.draw()

运行整个交易的应用程序

class App(object):

    def __init__(self):
        self.win = window.Window(fullscreen=False, vsync=True, width=800, height=600)
        self.world = World()
        self.camera = Camera(self.win)
        self.hud = Hud(self.win)
        clock.set_fps_limit(60)

    def run(self):
        while not self.win.has_exit:
            self.win.dispatch_events()

            self.camera.project_world()
            self.world.draw()

            self.camera.project_hud()
            self.hud.draw()

            clock.tick()
            self.win.flip()

app = App()
app.run()

丑陋的黑客

现在我已经通过丑陋的黑客攻击解决了它。敏感读者请勿继续阅读

我现在计算相机在世界中的比例和原点,然后我 link 世界到 HUD,所以 HUD 可以读取属性并计算文本本身的位置。

这当然是我不想要的 Spaghetti 代码反模式。

我不是 python 程序员,但我认为我对问题的 opengl 部分了解得足够多,所以我会尝试回答。请原谅我的语法。

我相信您想要一个代表场景中 3D 点的 2D 位置。您想知道 2D 中的位置,以便可以在那里绘制一些文本。您拥有 3D 世界的模型视图和投影矩阵。获取二维位置是一个简单的矩阵乘法问题。

假设我的玩家位置是 p(1.0, 1.0, 1.0)

import numpy as np
import pyglet 

#transform point to camera or eye space
posV = np.array([1.0,1.0,1.0,1.0])
pyglet.gl.glMatrixMode(pyglet.gl.GL_MODELVIEW)
mvmat = (pyglet.gl.GLdouble * 16)()
pyglet.gl.glGetDoublev(pyglet.gl.GL_MODELVIEW_MATRIX, mvmat)
pyglet.gl.glLoadIdentity()
npmvmat = np.array( mvmat ).reshape((4,4))
eyeV = np.dot(npmvmat, posV)

#transform point to NDC
pyglet.gl.glMatrixMode(pyglet.gl.GL_PROJECTION)
projmat = (pyglet.gl.GLdouble * 16)()
pyglet.gl.glGetDoublev(pyglet.gl.GL_PROJECTION_MATRIX, projmat )
pyglet.gl.glLoadIdentity()
npprojmat = np.array( projmat ).reshape((4,4))
screenV = np.dot(npprojmat, eyeV)

#w-divide - convert from NDC
screenV[0] = screenV[0] / screenV[3]
screenV[1] = screenV[1] / screenV[3]

#[-1,1] shift to [0,1]
x = 0.5 + (0.5 * screenV[0])
y = 0.5 + (0.5 * screenV[1])    

#get a position on the viewport change to your viewport size
width = 1920
height = 1080
x = x * width
y = y * height

print (x,y)

最后,您的 xy 将包含 3D 点的 2D 投影。

注意:确保在设置矩阵后定位 glGetDoublev() 调用。将它们存储在一个变量中并用它来投影到 2D。

希望这对您有所帮助