如何在OpenGL屏幕上同时显示形状和文本

How to display shape and text on OpenGL screen simultaneously?

本文关键字:显示 文本 OpenGL 屏幕      更新时间:2023-10-16

下面的代码工作得很好,没有致命错误,但是,当我使用参数"w","h"在"gluortho2d"作为gluortho2d(0,w,h,0)在重塑函数中,我得到屏幕上的文本,而如果我把这些参数"0,0"作为gluortho2d(0,0,0,0),我得到盒子的形状。我如何在屏幕上同时显示它们(框和文本)?

#include"glut.h"
void drawBitmapText(char *string, float x, float y, float z);
void reshape(int w, int h);
void display(void);
void drawBitmapText(char *string, float x, float y, float z)
{
    char *c;
    glRasterPos3f(x, y, z);//define position on the screen where to draw text.
    for (c = string; *c != ''; c++)
    {
        glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *c);
    }
}
void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();//Resets to identity Matrix.
    gluOrtho2D(0, w, h, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
void display(void)
{
    glBegin(GL_POLYGON);//1
    glVertex2f(-0.2, 0.6 - 0.3);
    glVertex2f(-0.1, 0.6 - 0.3);
    glVertex2f(-0.1, 0.5 - 0.3);
    glVertex2f(-0.2, 0.5 - 0.3);
    glEnd();

    glColor3f(0, 1, 0);
    drawBitmapText("Usama Ishfaq", 200, 400, 0);//drawBitmapText("Usama Ishfaq", x(how much right), y(how much down), z);
    glutSwapBuffers();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); 
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Usama OGL Window");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

通过不遵循糟糕的教程并在唯一有效的地方调用glViewport和投影矩阵设置:显示功能。在重塑处理程序中设置视口和投影矩阵是一个反模式。别这么做。

这样做

void display(void)
{
    int const w = glutGet(GLUT_WINDOW_WIDTH);
    int const h = glutGet(GLUT_WINDOW_HEIGHT);
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();//Resets to identity Matrix.
    gluOrtho2D(-1, 1, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();    
    glBegin(GL_POLYGON);//1
    glVertex2f(-0.2, 0.6 - 0.3);
    glVertex2f(-0.1, 0.6 - 0.3);
    glVertex2f(-0.1, 0.5 - 0.3);
    glVertex2f(-0.2, 0.5 - 0.3);
    glEnd();
    /* viewport doesn't change in this
     * application, but it's perfectly
     * valid to set a different
     * glViewport(...) here */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();//Resets to identity Matrix.
    gluOrtho2D(0, w, h, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glColor3f(0, 1, 0);
    drawBitmapText("Usama Ishfaq", 200, 400, 0);//drawBitmapText("Usama Ishfaq", x(how much right), y(how much down), z);
    glutSwapBuffers();
}

更新(由于评论中的请求):

为什么在重塑处理程序中设置视口和投影参数是错误的?好吧,你刚刚经历了自己的原因:它们不是"一刀切"的状态,并且在渲染过程中稍微复杂一些的帧,而不仅仅是一个网格绘制,你将希望在渲染过程中混合和匹配不同的视口和投影。这里有一个(不完整的)事情的列表,需要有不同的视口和投影,而渲染单一帧:

  • 渲染到纹理(FBO) -需要在纹理边界内的视口,通常也需要不同的投影(对于阴影映射,动态立方体映射和许多其他高级,多通道渲染技术很重要)
  • 小地图/概览帧或类似的在角落(viewport只覆盖角落)
  • 文本注释叠加(不同投影;通常是一个普通的恒等变换,以便直接在NDC空间中绘制文本矩形)
  • "放大镜"叠加

因为改变视口和投影状态在稍微复杂一点的OpenGL绘图中会发生多次,所以

a)在重塑处理程序中设置它没有意义:无论处理程序设置什么,都将仅在第一帧绘制开始时设置,此后帧绘制代码本身必须重置为重塑处理程序设置的内容。那么为什么还要在重塑处理程序中做呢?

b)在重塑处理程序中放置视口和投影设置代码从长远来看是一个负担,因为它可能导致程序的其他部分依赖于它。如果发生这种情况,一旦你意识到你的错误,并试图将视口和投影设置代码移动到它所属的地方,依赖于它被从重塑处理程序调用的程序的其他部分中断,你也必须修复这些。

总而言之,没有理由在重塑处理程序中放置任何与绘图相关的调用(并且glViewport和投影设置肯定与绘图相关)。当然,"一次性"初始化是完全可以的,例如,如果你想调整FBO渲染目标的大小以匹配窗口,或者如果你想准备一个覆盖图像,稍后会被重复应用。

你可以让它更简单。对于您正在做的事情,根本不需要设置转换。

看起来,对于方框,您试图在两个坐标方向上使用[-1.0,1.0]范围内的坐标。这对应于OpenGL NDC(归一化设备坐标)坐标系统,这是在应用模型视图和投影转换之后的坐标空间顶点。如果你保持它们的默认单位矩阵,你可以直接在NDC空间中指定坐标。换句话说,要使用[-1.0,1.0]范围内的坐标,请……什么都不用,保持默认值。

当你调用:

gluOrtho2D(0.0, 0.0, 0.0, 0.0);

表示此调用将导致错误,如手册页所述:

如果左=右,或底=上,或近=远,则生成GL_INVALID_VALUE。

,因此将保持默认值不变,这正是您所需要的。

现在,对于文本,看起来您想要以像素为单位指定位置。您遇到的问题是,glRasterPos*()通过转换管道运行指定的坐标,这意味着,使用默认的身份模型视图和投影转换,它期望输入坐标在[-1.0,1.0]范围内,就像您传递给glVertex2f()的坐标一样。

幸运的是,有一种非常简单的方法可以避免这种情况。有一个非常类似的glWindowPos*()调用,唯一的区别是传递给它的坐标是窗口坐标,以像素为单位。

总结一下:

  • 删除所有glMatrixMode()呼叫
  • 删除所有glLoadIdentity()呼叫
  • 删除所有gluOrtho2D()呼叫
  • drawBitmapText()中,将glRasterPos3f()替换为:

    glWindowPos2f(x, y);
    
唯一需要注意的是,窗口坐标的原点在底部左上角。因此,如果你的文本位置是相对于左上角给出的,你需要这样的:
glWindowPos2f(x, windowHeight - y);

在另一个答案中解决一些误导性信息:在reshape()函数中调用glViewport()是完全可以的,只要您对所有渲染使用相同的视口。在更复杂的应用程序中,你经常需要不同的视口来渲染不同的部分(例如,当你渲染到fbo时,或者只渲染到窗口的一部分),所以你需要在渲染期间在适当的地方调用glViewport()。但对于一个简单的例子,你做你所有的渲染整个窗口,没有错调用它在reshape()