我在 OpenGL 中绘制立方体。正交投影效果很好,但透视投影会出现黑屏。
所以我尝试了两种投影类型之间的插值,以查看透视失败的地方。
proj = alpha*persp + (1-alpha)*ortho.
对于 alpha=0(纯正交),它工作正常,但在 alpha=.48 附近,立方体的远端开始被 zFar 处的墙剪裁。这会随着 alpha 的增加而继续,到 alpha = .508 时,整个立方体将被剪裁并且根本不会出现。
我预计增加 zFar = frustum[5] 会解决这个问题,但它根本没有影响。尽管增加了 zFar,但后墙剪裁总是发生在相同的 alpha 处。这就是这里的大谜团。
这里是两个投影运算符,插值和“vba”(vbo 的 CPU 端)。
GLfloat frustum[] = {-0.01, 0.01, -0.009, 0.009, 0.02, 1000};
/*------------------------------------------------------------------------*/
static void interpolate_proj1_and_proj2(GLfloat *proj1, GLfloat *proj2, GLfloat alpha)
{
int i;
for (i=0; i<16; i++)
{proj[i] = alpha*proj1[i] + (1-alpha)*proj2[i];}
}
/*------------------------------------------------------------------------*/
//letters stand for: left, right, bottom, top, near, far, width, height, depth
static int get_perspective_from_frustum(GLfloat *proj, GLfloat *frustum)
{
float l=frustum[0], r=frustum[1], b=frustum[2], t=frustum[3], n=frustum[4];
float f=frustum[5], w=r-l, h=t-b, d=f-n;
memset(proj, 0, 16*sizeof(float));
proj[ 0]= 2*n/w; proj[ 1]= 0 ; proj[ 2]= (r+l)/w; proj[ 3]= 0 ;
proj[ 4]= 0 ; proj[ 5]= 2*n/h; proj[ 6]= (t+b)/h; proj[ 7]= 0 ;
proj[ 8]= 0 ; proj[ 9]= 0 ; proj[10]=-(f+n)/d; proj[11]=-2*f*n/d;
proj[12]= 0 ; proj[13]= 0 ; proj[14]= -1 ; proj[15]= 0 ;
return 1;
}
/*------------------------------------------------------------------------*/
//letters stand for w=width, h=height, d=depth
static int get_orthography_from_frustum(GLfloat *ortho, GLfloat *frustum)
{
float w=frustum[1]-frustum[0], h=frustum[3]-frustum[2], d=frustum[5]-frustum[4];
memset(ortho, 0, 16*sizeof(GLfloat));
ortho[ 0] = .2/w;
ortho[ 5] = .2/h;
ortho[10] = .2/d;
ortho[15] = 1;
return 1;
}
/*------------------------------------------------------------------------*/
static float *cube_vba()
{
int i, j, k, l, m, ibase, ineigh[3], span=2*3, offset, ibw;
GLfloat *vba, size=.05, *r[2], color[]={0,.5,0}, corner[8][3], *corn;
GLfloat Re[] ={0.98039, 0.01960, -0.19607,
0.01960, 0.98039, 0.19607,
0.19607, -0.19607, 0.96078};//small rotation about x, y axes
for (k=0; k<2; k++)
for (j=0; j<2; j++)
for (i=0; i<2; i++) {
offset=4*k+2*j+i;
corner[offset][0] = 2*(i-.5)*size;
corner[offset][1] = 2*(j-.5)*size;
corner[offset][2] = 2*(k-.5)*size + .04;//z has to be positive
corn=corner[offset];
for (l=0; l<3; l++)//rotate to show all sides with ortho
corn[l] = Re[3*l+0]*corn[0] + Re[3*l+1]*corn[1] + Re[3*l+2]*corn[2];
}
vba=(GLfloat *)malloc(2*12*span*sizeof(GLfloat));
memset(vba, 0, 2*12*span*sizeof(GLfloat));
for (m=ibase=0; m<4; m++) {
//Indices of base corners. Neighbors of corners 0,3,5,6 cover all sides
i=(m==1||m==2); j=(m==1||m==3); k=(m==2||m==3);
ibase = 4* k + 2* j + i;
ineigh[2] = 4*!k + 2* j + i; //
ineigh[1] = 4* k + 2*!j + i; //neighboring corners, ie, one different index
ineigh[0] = 4* k + 2* j + !i; //
for (l=0; l<3; l++) {
offset=3*m*2*span + l*2*span;
r[0] = corner[ibase ];
r[1] = corner[ineigh[l]];
memcpy(vba+offset , r[0] , 3*sizeof(GLfloat));
memcpy(vba+offset+3, color , 3*sizeof(GLfloat));
memcpy(vba+offset+6, r[1] , 3*sizeof(GLfloat));
memcpy(vba+offset+9, color , 3*sizeof(GLfloat));
}
}
return vba;
}
我在这里做错了两件事。
我用的是透视投影的转置,不是正确的形式。那是因为 OpenGL 按列而不是行对其矩阵进行排序。这对我来说似乎很奇怪,但没关系。
我天真地假设正交投影和透视投影之间的线性插值会在直线框和平截头体之间提供平滑过渡。显然它比那更复杂。
我修复了问题 1,但无论我使用什么 zFar,仍然会出现后墙剪裁。这里仍然没有真正的答案。