关于opengl:将透视投影转换为正交投影

Converting perspective projection to orthogonal projection

我创建了这个程序,用户单击鼠标右键,他会得到一个菜单,他可以在其中选择在透视投影中绘制立方体或球体

我希望他能够将透视投影更改为正交投影。我希望仅使用正交投影将形状放置在同一位置。

我希望他能够通过单击键盘上的"O"来执行此操作,然后通过单击"p"返回透视图。

如何在这段代码中混合正交投影?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include <windows.h>
#include <stdio.h>
#include<math.h>

 #include <gl\\glut.h>  // glut.h must come before gl.h and glu.h
#include <gl\\gl.h>
#include <gl\\glu.h>

void menu(int value);
static int win;
static int menid;
static int submenid;
static int  left_click = GLUT_UP;
static int  right_click = GLUT_UP;
static int  xold;
static int  yold;
static int  width;
static int  height;
static float    rotate_x = 30;
static float    rotate_y = 15;
static float    alpha = 0;
static float    beta = 0;
static int primitive = 0;
 static int orth =0;
static int flag =0;

void        sphere (void)
{
  glBegin (GL_LINES);
  glColor3f (1, 0, 0); glVertex3f (-1, -1, -1); glVertex3f ( 1, -1, -1);
  glColor3f (0, 1, 0); glVertex3f (-1, -1, -1); glVertex3f (-1,  1, -1);
  glColor3f (0, 0, 1); glVertex3f (-1, -1, -1); glVertex3f (-1, -1,  1);

  glEnd ();
  glRotatef (beta, 1, 0, 0);
  glRotatef (alpha, 0, 1, 0);
  glColor3f (0, 1, 0);
  glutWireSphere(1.0, 20, 16);  
}

void        cube (void)
{
  glBegin (GL_LINES);
  glColor3f (1, 0, 0); glVertex3f (-1, -1, -1); glVertex3f ( 1, -1, -1);
  glColor3f (0, 1, 0); glVertex3f (-1, -1, -1); glVertex3f (-1,  1, -1);
  glColor3f (0, 0, 1); glVertex3f (-1, -1, -1); glVertex3f (-1, -1,  1);

  glEnd ();
  glRotatef (beta, 1, 0, 0);
  glRotatef (alpha, 0, 1, 0);
  glColor3f (1, 0, 0);
 glutWireCube(1);

}

void        KeyboardFunc (unsigned char key, int x, int y)
{
  xold = x; /* Has no effect: just to avoid a warning */
  yold = y;
  if ('q' == key || 'Q' == key || 27 == key)
      exit (0);
  if(key=='O' || key =='o')
 //does something to change the perspective to orthogonal...  
  // you would want to redraw now
  glutPostRedisplay();

}
void createMenu(void){

  // MENU //

  submenid = glutCreateMenu(menu);

  glutAddMenuEntry("cube", 2);
  glutAddMenuEntry("sphere", 3);

  menid = glutCreateMenu(menu);

  // Create an entry
  glutAddMenuEntry("Clear", 1);

  glutAddSubMenu("Draw", submenid);

  glutAddMenuEntry("Quit", 0);

  glutAttachMenu(GLUT_RIGHT_BUTTON);


}

void menu(int value)
{
  if(value == 0)
  {
    glutDestroyWindow(win);
    exit(0);
  }
  else{
    primitive=value;
  }
  glutPostRedisplay();
}

void        DisplayFunc (void)
{
  const float a = height / (float) width;
  const float b = width / (float) height;

  glClear (GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);

 /* Perspective projection */
     glLoadIdentity();
    gluPerspective (20 * sqrt (1 + a * a), b, 8, 12);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();


/* Perspective view */
 glViewport (0, 0, width , height);
 glPushMatrix ();
glTranslatef (0, 0, -10);
glRotatef (rotate_y, 1, 0, 0);
glRotatef (rotate_x, 0, 1, 0);


  switch(primitive)
  {

case 2:
    cube();
    break;

case 3:
    sphere();
    break;
  }

  glPopMatrix ();

  glFlush ();
  glutSwapBuffers ();
}

void        ReshapeFunc (int new_width, int new_height)
{
  width = new_width;
  height = new_height;
  glutPostRedisplay();
}


void        MouseFunc (int button, int state, int x, int y)
{
  if (GLUT_LEFT_BUTTON == button)
    left_click = state;
  if (GLUT_RIGHT_BUTTON == button)
    right_click = state;
  xold = x;
  yold = y;
}

void        MotionFunc (int x, int y)
{
  if (GLUT_DOWN == left_click)
    {
      rotate_y = rotate_y + (y - yold) / 5.f;
      rotate_x = rotate_x + (x - xold) / 5.f;
      if (rotate_y > 90)
    rotate_y = 90;
      if (rotate_y < -90)
    rotate_y = -90;
      glutPostRedisplay ();
    }
  if (GLUT_DOWN == right_click)
    {
      beta = beta + (y - yold) / 2.f;
      alpha = alpha + (x - xold) / 2.f;
      glutPostRedisplay ();
    }
  xold = x;
  yold = y;
}


int     main (int argc, char **argv)
{
  /* Creation of the window */
  glutInit (&argc, argv);
  glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE);
  glutInitWindowSize (500, 500);
  glutCreateWindow ("perspective");

  /* OpenGL settings */
  glClearColor (0, 0, 0, 0);
  glEnable (GL_CULL_FACE);
  glCullFace (GL_BACK);
  glEnable (GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  /* Declaration of the callbacks */
  glutDisplayFunc (&DisplayFunc);
  glutReshapeFunc (&ReshapeFunc);
  glutKeyboardFunc (&KeyboardFunc);
  glutMouseFunc (&MouseFunc);
  glutMotionFunc (&MotionFunc);

    // put all the menu functions in one nice procedure
  createMenu();

  /* Loop */
  glutMainLoop ();
  return 0;
}


当然,使用正交投影,您无法获得与使用透视投影相同的图像。但是,您可以定义一个平面(我们称其为焦平面),该平面在两个变换中都投影到相同的位置。

Visualization

你有一个 20 * sqrt (1 + a * a) 的垂直视野(顺便说一句,我怀疑这是一个合理的计算;fovy 应该以度为单位)。为了计算正交的边缘,我们需要半角。然后:

1
2
3
4
float halfY = 20 * sqrt (1 + a * a) / 2.0f * 3.1415926f / 180.0f;
float top = focus_plane * tan(halfY); //focus_plane is the distance from the camera
float right = top * aspectRatio;
glOrtho(-right, right, -top, top, 8, 12);

您可以将 focus_plane 设置为 zNearzFar 之间的中间位置,在本例中设置为 10