出于性能原因,我正在尝试将游戏中的图形迁移到 OpenGL。
Here's a picture.
到目前为止,我设法完成了前两个任务:
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(120, 160, 0); // move rotation point
gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate
gl.glTranslatef(-120, -160, 0); // restore rotation point
mesh.draw(gl); // draws 100x100 px rectangle with the following coordinates: (70, 110, 170, 210)
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0f, (float)width, (float)height, 0f, -1f, 1f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
但是当我试图围绕 x 或 y 旋转我的盒子时,我的盒子发生了令人讨厌的事情并且没有透视效果。我尝试使用其他函数而不是 glRotate(glFrustum、glPerspective、gluLookAt、应用“倾斜”矩阵),但我无法使它们正常工作。
最佳答案
对于透视,您还需要一些镜头长度,这决定了 FOV。 FOV 是视平面距离与可见范围之比。在近平面的情况下,它因此变为 {left,right,top,bottom}/near
。为简单起见,我们假设水平 FOV 和对称投影,即
FOV = 2*|left|/near = 2*|right|/near = extent/distance
或者如果你更喜欢角度
FOV = 2*tan(angular FOV / 2)
对于 90° FOV,镜头的长度是焦平面宽度的一半。您的焦平面是 240x320 像素,所以左右各 120 度,上下各 160 度。 OpenGL 并没有真正的焦点,但我们可以说近和远之间的中间平面是“焦点”。
因此,假设对象的平均范围约为可见平面限制的数量级,即对于 240x360 的可见平面,对象的平均大小约为 200 像素。因此,从近到远剪辑的距离为 200,因此在焦平面上为 +- 100。所以对于 90° 的 FOV 焦平面有距离
2*tan(90°/2) = extent/distance
2*tan(45°) = 2 = 240/distance
2*distance = 240
distance = 120
120,因此远近剪裁距离分别为 20 和 220。
最后但并非最不重要的近剪裁平面限制必须按near_distance/focal_distance = 20/120 缩放
所以
left = -120 * 20/120 = -20
right = 120 * 20/120 = 20
bottom = -180 * 20/120 = -30
top = 180 * 20/120 = 30
所以这给了我们 glFrustum 参数:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-20, 20, -30, 30, 20, 220);
最后但并非最不重要的是,我们必须将世界原点移动到“焦点”平面
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -120);
完毕。
透视不保留大小。这就是使它成为一个视角的原因。您可以使用非常长的镜头,即小 FOV。
代码更新
作为一般专业提示:在绘图处理程序中执行所有 OpenGL 操作。不要在重塑处理程序中设置投影。它很丑,一旦你想要一些 HUD 或其他类型的覆盖,你无论如何都必须丢弃它。所以这里是如何改变它:
public void onDrawFrame(GL10 gl) {
// fov, extents are parameters set somewhere else
// 2*tan(fov/2.) = width/distance =>
float distance = width/(2.*tan(fov));
float near = distance - extent/2;
float far = distance + extent/2;
if(near < 1.) {
near = 1.;
}
float left = (-width/2) * near/distance;
float right = ( width/2) * near/distance;
float bottom = (-height/2) * near/distance;
float top = ( height/2) * near/distance;
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustum(left, right, bottom, top, near, far);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glTranslatef(0, 0, -focal);
gl.glTranslatef(120, 160, 0); // move rotation point
gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate
gl.glTranslatef(-120, -160, 0); // restore rotation point
mesh.draw(gl); // draws 100x100 px rectangle with the following coordinates: (70, 110, 170, 210)
}
public void onSurfaceChanged(GL10 gl, int new_width, int new_height) {
width = new_width;
height = new_height;
}
关于Android OpenGL - 简单 2D 转换的困难,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8700768/