OpenGL:如何在不移动整个场景的情况下移动 2d 对象?

发布于 2024-11-01 10:19:35 字数 4100 浏览 0 评论 0原文

好吧,我正在尝试使用 C++ 中的 OpenGL 重新创建古老的经典导弹命令。这是我第一次涉足 OpenGL,尽管此时我对 C++ 感到相当满意。

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

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); ,但问题是它会移动屏幕上的两个元素,而不仅仅是红色三角形。我尝试将整个调用放在推入和弹出矩阵操作之间绘制三角形:

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;

可以很好地满足我的目的。我不需要翻译矩阵,我只需要在从一个场景到下一个场景的不同位置绘制元素。

Alright, I'm trying to recreate the old classic, Missile Command, using OpenGL in C++. This is my first foray into OpenGL, although I feel fairly comfortable with C++ at this point.

I figured my first task was to figure out how to move 2d objects around the screen, seemed like it would be fairly simple. I created two quick method calls to make either triangles or quads:

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();
}

color3f and vertex2f are simple classes:

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;}
};

And here is my main file:

#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;
}

A quad is defined as the "background" for the time being and consists of just a green rectangle along the bottom of the screen. The red triangle is the "object" that I wish to move. On a keypress, an offset is saved in the direction indicated.

I've tried using glTranslatef(xOffset,yOffset,0); but the problem with that is that it moves both elements on the screen and not just the red triangle. I attempted to put the whole call to draw the triangle between a push and pop matrix operation:

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();

As far as I can tell, that destroys any changes that the translation was doing beforehand.
I've also tried just changing the values of the x and y coordinates before calling the draw, but that just causes a brief flicker before leaving the triangle in its original position:

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

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

There has to be a nice simple way of doing this, and I'm just overlooking it. Could someone offer a suggestion please?

EDIT:

My actual problem was that I was never refreshing the screen after an initial draw. What I needed was to specify an idle function inside my main loop:

glutIdleFunc(IdleFunc);

Where the actual IdleFunc looks like:

GLvoid IdleFunc(GLvoid)
{
    glutPostRedisplay();
}

Instead of using glFlush() inside my draw function, I should have been using glutSwapBuffers(). By doing that, the code I had first come up with:

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

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

Works fine for my purposes. I didn't have a need to translate the matrix, I just needed to draw the element in a different position from one scene to the next.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城°AllureLove 2024-11-08 10:19:35

GL_MODELVIEW 就是您所需要的。

来自 OpenGL 常见问题解答,2.1: http://www.opengl.org/resources/常见问题解答/技术/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.
}

GL_MODELVIEW is what you need.

From the OpenGL FAQ, 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.
}
思念满溢 2024-11-08 10:19:35

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

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();

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

You answer your own question, that is the solution:

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();

That will change the modelview matrix while the rectangle is drawn, then it will revert the modelview matrix back to what it were before. Did you actualy tried that? What whent wrong?

喜爱纠缠 2024-11-08 10:19:35

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

  • 调用 glPushMatrix();
  • 然后进行旋转
  • 存储旋转了多少
  • ,然后绘制旋转的项目
  • ,然后调用 glPopMatrix();

这只会旋转一个对象。

编辑:

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

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

If I'm reading your code right, you want to only rotate one element right? If so, do this:

  • Call glPushMatrix();
  • then do your rotation
  • Store how much you've rotated
  • then draw your rotated item
  • then call glPopMatrix();

That will only rotate the one object.

EDIT:

I see that doing that "destroys" the previous rotation. Could you elaborate? That is the correct way to translate/rotate one object.

I also notice that you aren't initializing the Modelview Matrix. You should initialize the Modelview Matrix after you setup your PROJECTION matrix. You also need to make sure that you are initializing both matrices to the identity. And finally, make sure that you are initializing both matrices EVERY time the screen refreshes. To test this, set a breakpoint on your matrix initialization and see if it gets hit only once or every frame.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文