【问题标题】:OpenGL: How can I move a 2d object without displacing the whole scene?OpenGL:如何在不移动整个场景的情况下移动 2d 对象?
【发布时间】:2011-04-13 03:03:16
【问题描述】:

好的,我正在尝试使用 C++ 中的 OpenGL 重新创建经典的 Missile Command。这是我第一次涉足 OpenGL,尽管此时我对 C++ 感觉相当熟悉。

我认为我的第一个任务是弄清楚如何在屏幕上移动 2d 对象,这似乎相当简单。我创建了两个快速方法调用来制作三角形或四边形:

void makeTriangle(color3f theColor, vertex2f &p1, vertex2f &p2, vertex2f &p3,
                  int &xOffset, int &yOffset)
{
    //a triangle
    glBegin(GL_POLYGON);
        glColor3f(theColor.red, theColor.green, theColor.blue);
        glVertex2f(p1.x, p1.y);
        glVertex2f(p2.x, p2.y);
        glVertex2f(p3.x, p3.y);
    glEnd();
}

void makeQuad(color3f theColor, vertex2f &p1, vertex2f &p2, vertex2f &p3,
              vertex2f &p4, int &xOffset, int &yOffset)
{
    //a rectangle
    glBegin(GL_POLYGON);
        glColor3f(theColor.red, theColor.green, theColor.blue);
        glVertex2f(p1.x, p1.y);
        glVertex2f(p2.x, p2.y);
        glVertex2f(p3.x, p3.y);
        glVertex2f(p4.x, p4.y);
    glEnd();
}

color3fvertex2f 是简单的类:

class vertex2f
{
public:
    float x, y;

    vertex2f(float a, float b){x=a; y=b;}
};

class color3f
{
public:
    float red, green, blue;

    color3f(float a, float b, float c){red=a; green=b; blue=c;}
};

这是我的主文件:

#include <iostream>
#include "Shapes.hpp"

using namespace std;

int xOffset = 0, yOffset = 0;
bool done = false;

void keyboard(unsigned char key, int x, int y)
{
    if( key == 'q' || key == 'Q')
        {
            exit(0);
            done = true;
        }

        if( key == 'a' )
            xOffset = -10;

        if( key == 'd' )
            xOffset = 10;

        if( key == 's' )
            yOffset = -10;

        if( key == 'w' )
            yOffset = 10;

}


void init(void)
{
    //Set color of display window to white
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    //Set parameters for world-coordiante clipping window
    glMatrixMode(GL_PROJECTION);
    gluOrtho2D(-400.0,400.0,-300.0,300.0);

}


void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    color3f aGreen(0.0, 1.0, 0.0);
    vertex2f pa(-400,-200);
    vertex2f pb(-400,-300);
    vertex2f pc(400,-300);
    vertex2f pd(400,-200);

    makeQuad(aGreen,pa,pb,pc,pd,xOffset,yOffset);

    color3f aRed(1.0, 0.0, 0.0);
    vertex2f p1(-50.0,-25.0);
    vertex2f p2(50.0,-25.0);
    vertex2f p3(0.0,50.0);

    makeTriangle(aRed,p1,p2,p3,xOffset,yOffset);

    glFlush();
}


int main(int argc, char** argv)
{
    // Create Window.
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutCreateWindow("test");

    // Some initialization.
    init();

    while(!done)
    {
    //display functions
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);

    // Start event loop.
    glutMainLoop();
    }

    return 0;
}

四边形暂时被定义为“背景”,它仅由屏幕底部的一个绿色矩形组成。红色三角形是我想要移动的“对象”。按下按键时,会按指示的方向保存偏移量。

我尝试过使用glTranslatef(xOffset,yOffset,0);,但问题在于它会移动屏幕上的两个元素,而不仅仅是红色三角形。我试图将整个调用用于在 push 和 pop 矩阵操作之间绘制三角形:

PushMatrix();
glTranslatef(xOffset,yOffset,0);
glBegin(GL_POLYGON);
    glColor3f(theColor.red, theColor.green, theColor.blue);
    glVertex2f(p1.x, p1.y);
    glVertex2f(p2.x, p2.y);
    glVertex2f(p3.x, p3.y);
glEnd();
PopMatrix();

据我所知,这会破坏翻译之前所做的任何更改。 我还尝试在调用绘图之前更改 x 和 y 坐标的值,但这只会在将三角形留在其原始位置之前导致短暂的闪烁:

p1.x += xOffset;
p2.x += xOffset;
p3.x += xOffset;

p1.y += yOffset;
p2.y += yOffset;
p3.y += yOffset;

必须有一个很好的简单方法来做到这一点,而我只是忽略了它。有人可以提供建议吗?

编辑:

我的实际问题是在初始绘制后我从未刷新屏幕。我需要的是在我的主循环中指定一个空闲函数:

glutIdleFunc(IdleFunc);

实际 IdleFunc 的样子:

GLvoid IdleFunc(GLvoid)
{
    glutPostRedisplay();
}

我应该使用glutSwapBuffers(),而不是在我的绘图函数中使用glFlush()。通过这样做,我首先想出的代码:

p1.x += xOffset;
p2.x += xOffset;
p3.x += xOffset;

p1.y += yOffset;
p2.y += yOffset;
p3.y += yOffset;

适合我的目的。我不需要转换矩阵,我只需要将元素绘制在从一个场景到下一个场景的不同位置。

【问题讨论】:

    标签: c++ opengl 2d


    【解决方案1】:

    GL_MODELVIEW 是您所需要的。

    来自 OpenGL 常见问题解答,2.1:http://www.opengl.org/resources/faq/technical/gettingstarted.htm

    program_entrypoint
    {
        // Determine which depth or pixel format should be used.
        // Create a window with the desired format.
        // Create a rendering context and make it current with the window.
        // Set up initial OpenGL state.
        // Set up callback routines for window resize and window refresh.
    }
    
    handle_resize
    {
        glViewport(...);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        // Set projection transform with glOrtho, glFrustum, gluOrtho2D, gluPerspective, etc.
    }
    
    handle_refresh
    {
        glClear(...);
    
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        // Set view transform with gluLookAt or equivalent
    
        // For each object (i) in the scene that needs to be rendered:
            // Push relevant stacks, e.g., glPushMatrix, glPushAttrib.
            // Set OpenGL state specific to object (i).
            // Set model transform for object (i) using glTranslatef, glScalef, glRotatef, and/or equivalent.
            // Issue rendering commands for object (i).
            // Pop relevant stacks, (e.g., glPopMatrix, glPopAttrib.)
        // End for loop.
    
        // Swap buffers.
    }
    

    【讨论】:

    • +1 没错。这大概就是问题所在。我在代码中没有看到任何 Modelview 矩阵初始化。另外,我看到 holtavolt 已将矩阵初始化放在 openGL 循环中,因此每帧都会对其进行初始化。
    • 我没有发现 opengl 网站上的文档很有帮助。我不明白这些 cmets 中的大多数是什么意思,谷歌搜索它们中的大多数并不是很有启发性。我知道我的问题的答案在于 Modelview Matrix 以及 Push 和 Pop 矩阵操作,但我似乎无法解决它们在我的原始解决方案中的位置,或者我是否需要将整个事情扔掉并从头开始.
    • 好的 - 你可能想从这个 NeHe OpenGL 教程开始,它可以独立旋转两个对象以降低表单:nehe.gamedev.net/data/lessons/lesson.asp?lesson=04
    【解决方案2】:

    您回答自己的问题,这就是解决方案:

    glPushMatrix();
    glTranslatef(xOffset,yOffset,0);
    glBegin(GL_POLYGON);
        glColor3f(theColor.red, theColor.green, theColor.blue);
        glVertex2f(p1.x, p1.y);
        glVertex2f(p2.x, p2.y);
        glVertex2f(p3.x, p3.y);
    glEnd();
    glPopMatrix();
    

    这将在绘制矩形时更改模型视图矩阵,然后将模型视图矩阵恢复为之前的状态。你真的试过吗?什么时候出错了?

    【讨论】:

    • +1,我的想法完全正确。您能否详细说明一下上面的代码不起作用的地方?
    • 按照我的方式执行此操作导致没有发生任何转换。在查看了下面 joe_coolish 和 holtavolt 的答案后,我相信我的错误在于没有初始化 Modelview 矩阵。但是,我仍在努力寻找正确的方法以及将其插入代码的位置。
    【解决方案3】:

    如果我没看错你的代码,你只想旋转一个元素吗?如果是这样,请执行以下操作:

    • 致电glPushMatrix();
    • 然后进行轮换
    • 存储您旋转了多少
    • 然后绘制您旋转的项目
    • 然后拨打glPopMatrix();

    这只会旋转一个对象。

    编辑:

    我看到这样做会“破坏”之前的轮换。你能详细说明一下吗?这是平移/旋转一个对象的正确方法。

    我还注意到您没有初始化模型视图矩阵。设置投影矩阵后,您应该初始化模型视图矩阵。您还需要确保将两个矩阵都初始化为标识。最后,确保每次屏幕刷新时都初始化这两个矩阵。要对此进行测试,请在矩阵初始化时设置一个断点,并查看它是否只被击中一次或每一帧。

    【讨论】:

      猜你喜欢
      • 2016-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-31
      相关资源
      最近更新 更多