opengl:调整大小时如何将对象保留在窗口中

Posted

技术标签:

【中文标题】opengl:调整大小时如何将对象保留在窗口中【英文标题】:opengl: how to keep objects in window when it's resized 【发布时间】:2010-10-15 02:22:30 【问题描述】:

我正在使用 Bresenham 的中点算法作为作业在 OpenGL 上开发一个类似于 MS 绘制的应用程序。到目前为止,我可以画线和椭圆。调整窗口大小时,我将它们全部丢失。我怎样才能让它们保持绘制状态?

完整代码:

#include "GL/glut.h"

#include <stdio.h>
#include <math.h>
int i;

//int mainWindow, subWindow;
int X1, Y1, X2, Y2;

int modoDeDibujo; 

int W = 1000, H = 1000;
/*void menuApp (int value)

    if (value == 1) printf("Linea\n");
    if (value == 2) printf("Circulo\n");
    if (value == 3) printf("Elipsis\n");
    if (value == 4) exit(0);



void crearMenu()

    //inicio Creando el menu
    int submenu;
    submenu = glutCreateMenu(menuApp);
    glutAddMenuEntry("Linea", 1);

    glutAddMenuEntry("Elipse",3);
    glutAddMenuEntry("Salir",4);
    glutCreateMenu(menuApp);
    glutAddSubMenu("SubMenu", submenu);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

    //fin Creando el menu
*/

void renderPoint(void) /*REVISAR ESTO*/

    glClear (GL_COLOR_BUFFER_BIT);
    glBegin (GL_POINTS);
        glVertex2f  (-0.98, 0.98);
    glEnd ();
    glFlush ();






void renderPoint(double x, double y)


    //printf("BEFORE TRANSFORM %f\t%f\t# renderPoint\n", x, y);

    W = glutGet(GLUT_WINDOW_WIDTH);
    H = glutGet(GLUT_WINDOW_HEIGHT);

    float X;
    float Y;
    glBegin (GL_POINTS);
        X = (2*x/W) - 1;
        Y = (-2*y/H) + 1;
        glVertex2f (X, Y);
        //printf("TRANSFORMED POINT %f\t%f\t# renderPoint\n", X, Y);

    glEnd ();
    glFlush ();



/*wiki pseudo:

function line(x0, x1, y0, y1) //x1
     boolean steep := abs(y1 - y0) > abs(x1 - x0)//x2
     if steep then//x3
         swap(x0, y0) //x4
         swap(x1, y1) //x5 
     if x0 > x1 then //x6
         swap(x0, x1) //x7
         swap(y0, y1) //x8
     int deltax := x1 - x0 //x9
     int deltay := abs(y1 - y0) //x10
     int error := deltax / 2 //x11
     int ystep //x12
     int y := y0 //x13
     if y0 < y1 then ystep := 1 else ystep := -1 //x14
     for x from x0 to x1 //x15
         if steep then plot(y,x) else plot(x,y) //x16
         error := error - deltay //x17
         if error < 0 then //x18
             y := y + ystep //x19
             error := error + deltax //x20
*/


void bresenham1(GLint x0, GLint x1, GLint y0, GLint y1) //function line(x0, x1, y0, y1)



    //double result1 = fabs((double)y1 - y0); //abs(y1 - y0)
    //double result2 = fabs((double)x1 - x0); //abs(x1 - x0)


    int result1 = abs(y1-y0);
    int result2 = abs(x1-x0);

bool steep = (result1 > result2); //boolean steep := abs(y1 - y0) > abs(x1 - x0)

if (steep) //if steep then

        GLint aux1 = x0; //swap(x0, y0)
        x0=y0;
        y0 = aux1;

        GLint aux2 = x1; // swap (x1,y1)
        x1=y1;
        y1=aux2;



if(x0>x1) // if (x0>x1)
        GLint aux3=x0; //swap(x0,x1)
        x0=x1;
        x1=aux3;

        GLint aux4=y0;//swap(y0,y1)
        y0=y1;
        y1=aux4;



int deltax = x1-x0; // deltax = x1-x0
int deltay = abs(y1-y0); //  int deltay := abs(y1 - y0) - revisar 
int error = (deltax / 2); //int error := deltax / 2 
int ystep; // int ystep

int y = y0;  //int y := y0

    if (y0<y1)  //if y0 < y1 then ystep := 1 else ystep := -1 

        ystep=1;

    

    else ystep=-1;

for (int x=x0; x<=x1; x++) //for x from x0 to x1
  if (steep)  // if steep then plot(y,x) else plot(x,y)

       renderPoint(y,x);
   
   else 

    renderPoint(x,y);
   

error = error - deltay; //error := error - deltay

if (error<0)   //if error < 0 then 
  y = y + ystep; // y := y + ystep
  error = error + deltax; //error := error + deltax

 // end if (error<0)


// end for from x0 to x1


// end bresenham


void Plot4EllipsePoints(int X, int Y,int CX,int CY)

      renderPoint(CX+X, CY+Y); //point in quadrant 1
      renderPoint(CX-X, CY+Y); // point in quadrant 2
      renderPoint(CX-X, CY-Y); // point in quadrant 3
      renderPoint(CX+X, CY-Y); // point in quadrant 4





void PlotEllipse (int CX, int CY, int XRadius, int YRadius) 


        int X, Y;
        int XChange, YChange;
        int EllipseError;
        int  TwoASquare, TwoBSquare;
        int  StoppingX, StoppingY;


    TwoASquare = 2 * XRadius * XRadius;
    TwoBSquare = 2 * YRadius * YRadius;

    X = XRadius;
    Y =0;

    XChange = YRadius*YRadius*(1-(2*XRadius));
    YChange = XRadius * XRadius;

    EllipseError =0;
    StoppingX = TwoBSquare*XRadius;
    StoppingY = 0;

    while(StoppingX >= StoppingY)

       Plot4EllipsePoints(X,Y,CX,CY);
       Y++;
       StoppingY=StoppingY + TwoASquare; 

       EllipseError= EllipseError+ YChange;

       YChange= YChange+ TwoASquare;

       if( ((2*EllipseError)  + XChange)>0)
       
            X--;
            StoppingX = StoppingX - TwoBSquare;
                EllipseError= EllipseError + XChange;
                XChange = XChange + TwoBSquare;

    


          

      //1st set of points done, start second set


     X=0;
     Y= YRadius;

     XChange= YRadius*YRadius;
     YChange = XRadius*XRadius*(1-2*YRadius);

     EllipseError=0;
     StoppingX =0;
     StoppingY= TwoASquare * YRadius;
     while(StoppingX <= StoppingY) // 2nd set of points, y'<-1

    Plot4EllipsePoints(X,Y, CX,CY);
        X++;
        StoppingX = StoppingX + TwoBSquare;
        EllipseError = EllipseError + XChange;
        XChange = XChange + TwoBSquare;

       if (((2*EllipseError) + YChange)>0)

           Y--;
           StoppingY = StoppingY - TwoASquare;
           EllipseError = EllipseError + YChange;
           YChange = YChange + TwoASquare;


    

      













void renderAll (void)

    /*glutSetWindow(mainWindow);
    glutPostRedisplay();
    glutSetWindow(subWindow);
    glutPostRedisplay();*/


void movimiento(int boton, int estado, int x, int y)

    if((estado == GLUT_DOWN) && (boton == GLUT_LEFT_BUTTON))//mouse down
    
        X1 = x; Y1 = y;


        PlotEllipse (x, y, 200, 100);
        renderPoint(x,y);
    
    if((estado == GLUT_UP) && (boton == GLUT_LEFT_BUTTON))//mouse up
    
        //printf("  Up|x:%d, y:%d\n",x,y);
        X2 = x; Y2 = y;
        //renderLine();
        bresenham1(X1,X2,Y1,Y2);


       //PRUEBA USANDO LA PRIMITIVA DE OPENGL

         glBegin( GL_LINES );



         glEnd();


        //renderPoint(x, y);
    


void MouseMove(int x, int y)

    //printf("x:%d  | y:%d\n", x,y);
    X2 = x; Y2 = y;
    //renderLine();
    //bresenham1(X1, Y1, X2, Y2);


void teclado(unsigned char key, int x, int y)

    if(key==1)
      modoDeDibujo=1; // dibuja lineas  
      printf("Modo de dibujo: linea");
    

    if (key==2)
       modoDeDibujo=2; //dibuja elipses 
    


    if(key == 27)exit(0);


void especiales(int key, int x, int y)

    if(key == GLUT_KEY_F1) exit(0);


static void
key(unsigned char k, int x, int y)

  switch (k) 
  case 27:  /* Escape */
    exit(0);
    break;
  default:
    return;
  
  glutPostRedisplay();


int main (int argc, char *argv []) 

    i = 0;
    //inicializa las operaciones de OpenGL/GLUT, db cr antes de usar funciones GLUT
    glutInit (&argc, argv);

    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA);
    glutInitWindowPosition (100, 100);
    glutInitWindowSize (W, H);
    //Crea una ventana  de Opengl
    glutCreateWindow ("tarea");
    glutDisplayFunc (renderPoint);
    glutMouseFunc(movimiento);
    glutKeyboardFunc(teclado);//teclas ASCII
    glutSpecialFunc(especiales);//captura las teclas [f1..f12]

    //glutPassiveMotionFunc(pasivo);
    glutKeyboardFunc(key);
    glutMotionFunc(MouseMove);
    //crearMenu();
    glutMainLoop ();



【问题讨论】:

【参考方案1】:

首先,你需要安排代码。你必须有一个且只有一个显示函数,它清除缓冲区,调用其他绘制函数并将它们刷新到屏幕上(或者如果你使用双缓冲区,则交换缓冲区)。

在调整窗口大小时,GLUT 将调用显示函数,如您所知,它是 renderPoint():

glutDisplayFunc (renderPoint);

renderPoint 清除在重绘“点”之前的缓冲区,例如:

void renderPoint(void) /*REVISAR ESTO*/

    glClear (GL_COLOR_BUFFER_BIT);
    glBegin (GL_POINTS);
        glVertex2f  (-0.98, 0.98);
    glEnd ();
    glFlush ();

由于缓冲区已被清除,所有在函数 renderPoint 之外绘制的点(圆点、线点..)都没有任何意义,因为您没有从“主显示函数”调用它们是渲染点。

如何在屏幕上保持点数?

您必须将点(您要绘制的任何点)存储在缓冲区中,例如数组、动态数组、std::vector 或其他任何东西。在显示函数中,编写一个循环语句访问每个点并提取x和y..然后绘制它们。

例如,代替上面的函数,将其替换为:

class MyPoint 
    public:
    float x;
    float y;
    MyPoint(float x, float y)
    
        this->x = x;
        this->y = y;
     ;

#include <vector> 
std::vector<MyPoint> testPoints;

void renderPoint(void) /*REVISAR ESTO*/ 
    testPoints.push_back(MyPoint(-0.58,0.58));
    testPoints.push_back(MyPoint(0.58,0.58));
    testPoints.push_back(MyPoint(0.58,-0.58));

    glClear (GL_COLOR_BUFFER_BIT);
    glPointSize(2);

    glBegin (GL_POINTS);
        for(int i=0;i<testPoints.size();i++)
        
          glVertex2f  (testPoints[i].x, testPoints[i].y);
        
    glEnd ();

    glFlush (); 

如您所见,通过使用动态数组(如 (std::vector) 来存储点并使用 for 循环语句,我们能够保持三个点可见。

还有什么?

对其他形状执行相同的方法,这样对于每个“mouse click”事件,您可以add or push_back 的两个点代表一个名为lineArray 的数组或 std::vector 中的线端点。在display function中,提取两个线点后,做一个for循环语句来绘制每条线。

您应该使用glutReshapeFuncglViewport 确保视口在调整大小事件后与窗口具有相同的尺寸。我认为gluOrtho2d 比尝试从 Windows 坐标空间映射到 OpenGL 坐标空间更优雅

安排你的代码,让你只使用一个显示功能。

您的程序可能是这样的:

void drawTestPoints()

  for(int i=0;i<length;i++)
  
      renderPoint(pointsArray[i].x,pointsArray[i].y);// vector of points/(MyPoint)

  



void drawLines()

  for(int i=0;i<length;)
  

      MyPoint startPoint = linesArray[i];
      MyPoint endPoint = linesArray[i+1];
      bresenham1(startPoint.x,endPoint.x,startPoint.y,endPoint.y);
      i+=2;
  



void drawAll()

  glClear (GL_COLOR_BUFFER_BIT);

    drawTestPoints();
    drawLines();
    drawOtherShapes();

  glFlush();


.
.
.

// in the main func:
glutDisplayFunc (drawAll);

==

【讨论】:

谢谢,非常详细,虽然我不知道如何安排这段代码只使用一个显示功能。我目前使用的方法有什么问题? 对于任何 GLUT 应用程序,只有一个显示函数会在之后调用(调整大小/重新绘制事件发生)。所以缓冲区将被清除,OpenGL 将重绘缓冲区上的点。以您的方式,如果发生重绘事件,这些点将被删除,因此将显示函数用作主显示函数,该函数调用其他绘制其他形状的函数,这些函数有一个 for 循环语句来绘制存储在数组。 我在答案的最后一段中添加了一个示例(以明确我的意思)。【参考方案2】:

不要认为它是“让他们保持吸引力”。而是考虑事件和“我什么时候需要重绘我的对象”。

为此,您需要处理调整大小事件并重新运行绘图代码。我还猜测您的对象可能不会在另一个应用程序移到它们上之后重新绘制它们自己,或者如果它稍微移出屏幕然后又被带回来。如果是这种情况,您需要弄清楚要处理哪些事件也会导致重绘。

【讨论】:

如何重新运行绘图代码?我应该与我所做的操作保持堆栈吗?如果是这样,你能提供一个例子吗? @omgzor:我的回答非常高级和概念化。查看 BugKiller 和 VJo 的答案以了解详细信息。还有一个概念性的想法:不要将其视为一堆操作。想想需要经常重绘的基元(绘图对象)的集合(数组)。【参考方案3】:

下面是 windows 的一个简单示例: http://www.falloutsoftware.com/tutorials/gl/gl2.htm

【讨论】:

以上是关于opengl:调整大小时如何将对象保留在窗口中的主要内容,如果未能解决你的问题,请参考以下文章

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

窗口调整大小后将鼠标位置转换为 2D openGL 中的世界坐标

OpenGL 防止在调整窗口大小时拉伸

调整窗口大小时如何使用鼠标单击获取图像坐标?

如何在不缩放其内容的情况下调整 opengl 视口大小

调整 OpenGL 窗口的大小会导致它崩溃