使用OpenGL和SDL处理窗口调整大小

6

我有一个绘制和移动正方形的程序代码,但是在调整应用程序窗口大小时遇到了问题。当我处理调整大小并相应地更改状态时,屏幕上所有应该绘制且在调整大小之前已经绘制的内容都消失了。我不知道为什么,因为在调整窗口大小期间没有更改任何对象的内部坐标。

我的问题是,有没有人能指点我解决这个问题的正确方向。(代码可以编译通过)

void ResizeWindow()
{
    screen_width = event.resize.w;
    screen_height = event.resize.h;

    SDL_SetVideoMode(screen_width, screen_height, bpp, SDL_OPENGL | SDL_RESIZABLE | SDL_DOUBLEBUF);

    glViewport(0, 0, screen_width, screen_height);
    glMatrixMode(GL_PROJECTION);
    glOrtho(0, screen_width, 0, screen_height, -1, 1);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
}

主循环:


while (running == true)

{

    while(SDL_PollEvent(&event))

    {

        switch(event.type)

        {

            case SDL_VIDEORESIZE: ResizeWindow(); break; // resizing called here
            case SDL_QUIT: running = false; break;
            case SDL_KEYDOWN: square.Handle_Input(down); break;
            case SDL_KEYUP: square.Handle_Input(up); break;

        }

    }

    square.Move();
    square.Check_Collision();

    glClear(GL_COLOR_BUFFER_BIT);

    square.Draw();

    SDL_GL_SwapBuffers();

}

一切都在完美地运行,直到窗口被调整大小。

我看到了你之前的问题,但当时无法指出所有问题。我相信你还没有解决之前的问题。你应该将使其工作的更改发布为自己的答案并关闭问题。 - batbrat
我的建议解决了问题吗? - batbrat
自从我发布这个问题以来,我一直在睡觉,你的答案很有道理,特别是关于SDL_SetVideoMode的部分,回过头看看,似乎是我犯了一个愚蠢的错误。我之前发布的问题实际上只有一个问题,那就是我又犯了一个大错误;我是根据记忆工作的,没有查找glOrtho参数。我把它设置为从左到右为0-0。我现在会尝试你的建议。 - user969416
在SDL 2.0中,调整大小事件被称为SDL_WINDOWEVENT_RESIZED - lubosz
2个回答

14

我已经复制粘贴了你的代码,逐行讲解正在发生的事情。这将帮助你不仅看到为什么在调整大小后屏幕会变空白,还将帮助你删除你实际上不需要的东西。

void ResizeWindow()
{

下面这几行很好!你已经从调整大小事件中获取了新的屏幕宽度和高度,我假设在你的代码中此时可以访问该事件。

    screen_width = event.resize.w;
    screen_height = event.resize.h;

我怀疑你不需要调用SDL_SetViedoMode。我认为它仅在设置OpenGL窗口时使用。目前我没有使用SDL的经验,所以我不能确定。但我快速查阅了文档,似乎支持按我期望的方式使用它。

SDL_SetVideoMode(screen_width, screen_height, bpp, SDL_OPENGL | SDL_RESIZABLE | SDL_DOUBLEBUF);

现在我们开始进入有趣的OpenGL部分。你已经调整了视口大小,这是必要的。

    glViewport(0, 0, screen_width, screen_height);

现在,您正在创建一个新的投影矩阵来保持宽高比(除非我搞错了)。您已经切换了矩阵模式并设置了一个正交投影矩阵,这是很明智的做法。

    glMatrixMode(GL_PROJECTION);
    glOrtho(0, screen_width, 0, screen_height, -1, 1);

现在,你将投影矩阵设置为单位矩阵,覆盖了旧的OpenGL投影矩阵并撤销了你所做的所有好工作。这就是屏幕变空白的原因。

    glLoadIdentity();

现在,您切换到模型视图矩阵并将其设置为恒等矩阵,这并不是必要的,只要您在其他地方正确设置它。

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

我怀疑你并不需要接下来的这一行。毕竟,为什么在重设大小后要清除屏幕呢?你只需要重绘它,但我相信你的绘画函数会自动在刷新完成后清除屏幕并绘制对象。

    glClear(GL_COLOR_BUFFER_BIT);

你绝对不需要再次将当前的模型视图矩阵重置为单位矩阵。OpenGL仍处于模型视图模式,并且该矩阵已经设置为单位矩阵!请记住,OpenGL是一个状态机!

    glLoadIdentity();
}

2

你可能遇到的一个问题是,使用SDL调整窗口大小会创建一个新的OpenGL上下文,这意味着你之前上传的所有东西(纹理、顶点缓冲对象)和设置的状态(如顶点数组指针)都将丢失。如果要使用SDL,则需要重新初始化它们。如果想保留它们,请不要使用SDL进行窗口管理。我建议使用GLFW。

这个

void ResizeWindow()

{

screen_width = event.resize.w;
screen_height = event.resize.h;

SDL_SetVideoMode(screen_width, screen_height, bpp, SDL_OPENGL | SDL_RESIZABLE | SDL_DOUBLEBUF);

glViewport(0, 0, screen_width, screen_height);
glMatrixMode(GL_PROJECTION);
glOrtho(0, screen_width, 0, screen_height, -1, 1);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();

}

过度绘制是初学者OpenGL代码中最常见的反模式。影响绘图的绘图命令和状态管理应该放在绘图函数中,即如下:

glViewport(0, 0, screen_width, screen_height);
glMatrixMode(GL_PROJECTION);
glOrtho(0, screen_width, 0, screen_height, -1, 1);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();

如果你想要一个强大的OpenGL程序,不要只把它们放在调整大小处理程序中。它们应该与其他绘图命令一起。


我想我明白你的意思。真的没有一种轻松的方法在SDL中调整大小时保持程序正常运行吗? - user969416
那么他确实需要调用SDL_SetVideoMode吗?另外,重新调整视口并在开始和调整期间设置投影是否明智?实际上,如果我想保持纵横比,我认为这是必要的。至于模型视图矩阵,我会根据需要在绘制时设置它。当然,这并没有考虑现代OpenGL实践,仅考虑了即时模式。 - batbrat
调用SDL_SetVideoMode必须在调整大小的代码中的某个位置。我现在想知道的是如何完全重置矩阵(如果可能的话),以便在窗口调整大小时可以从头开始。 - user969416
1
好的,我明白了。我的目标是学习OpenGL。由于我刚开始编程时它易于使用,所以我的亲戚建议我使用SDL。我会尝试你提供的GLFW建议。谢谢你的帮助。 - user969416
@batbrat:Qt和FLTK会过度设计。GLFW与SDL非常相似,实际上大多数SDL调用都可以用相应的GLFW函数1:1替换。这就是我首先建议使用GLFW的原因。转换工作量很小。 - datenwolf
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接