通过单位球体中心的平面在OpenGL ES 2.0中出现偏移

时间:2015-09-11 10:34:41

标签: debugging opengl-es-2.0 vispy

我使用OpenGL ES 2.0的vispy界面在球体上绘制一些海岸线数据。我使用纬度和经度值来计算球体上数据的3d坐标,并绘制这些坐标。我能够成功绘制数据但我想只看到球体一侧可以从视口中看到的那些数据点。

我尝试了两种截然不同的方法来创造这种效果,但两者都导致了同样的问题。首先,我计算了视图方向和数据位置的点积,只绘制了具有负结果的点(即只有那些面向视口的点),其次,我只是画了一个穿过球体中心的平面,垂直于查看方向。

在这两种情况下我都观察到了相同的情况 - 这个平面似乎略微偏离了视口,在球体的中心后面。换句话说,您可以在球体被球体遮盖之前稍微看到球体背面的数据。

我已经检查过我绘制的点实际上是在单位球上,我相信,从三维世界的角度来看,一切都是合理的。作为3D图形的相对初学者,我不太自信的是,我是否误解了投影矩阵的某些东西。我做了一些阅读 - 但我的理解让我认为投影不应该改变" Z方向上的点的顺序" (视口面向的方向)。

我确信这不是深度测试问题,因为我的第一种方法没有启用深度测试,并且在顶点着色器中完成了屏蔽(通过设置片段颜色alpha为0.0)。除此之外,我还没有找到任何其他解释。

这是平面方法的代码:

import numpy as np
import cartopy
from vispy import app
from vispy import gloo
import time
from vispy.util.transforms import perspective, translate, rotate

xpts = []
ypts = []

#getting coastlines data

for string in cartopy.feature.NaturalEarthFeature('physical', 'coastline', '10m').geometries():
    for line in string:
        points = list(line.coords)
        for point in points:
            xpts.append(point[0])
            ypts.append(point[1])

coasts = np.array(zip(xpts,ypts), dtype=np.float32)

theta = (np.pi/180)*np.array(xpts, dtype=np.float32)
phi = (np.pi/180)*np.array(ypts, dtype=np.float32)

x3d = np.cos(phi)*np.cos(theta)
y3d = np.sin(theta)*np.cos(phi)
z3d = np.sin(phi)



vertex = """
// Uniforms
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform vec3 u_color;

attribute vec3 a_position;
void main (void)
{
    gl_Position = u_projection*u_view*u_model*vec4(a_position, 1.0);
}
"""

fragment = """
// Uniforms
uniform vec3 u_color;

void main()
{
    gl_FragColor = vec4(u_color, 1.0);
}
"""

class Canvas(app.Canvas):
    def __init__(self):
        app.Canvas.__init__(self, keys='interactive')

        gloo.set_state(clear_color = 'red', depth_test=True, blend=True, blend_func=('src_alpha', 'one_minus_src_alpha'))

        self.x = 0

        self.plane = 5*np.array([(0.,-1., -1.,1), (0, -1., +1.,1), (0, +1., -1.,1), (0, +1., +1.,1)], dtype=np.float32)

        self._timer = app.Timer(connect=self.on_timer, start=True)
        self.program = gloo.Program(vertex, fragment)

        self.view = np.dot(rotate(-90, (1, 0, 0)), np.dot(translate((-3, 0, 0)), rotate(-90.0, (0.0,1.0,0.0))))
        self.model = np.eye(4, dtype=np.float32)
        self.projection = perspective(45.0, self.size[0]/float(self.size[1]), 2.0, 10.0)

        self.program['u_projection'] = self.projection
        self.program['u_view'] = self.view
        self.program['u_model'] = self.model
        self.program['u_color'] = np.array([0.0, 0.0, 0.0], dtype=np.float32)

        self.program2 = gloo.Program(vertex, fragment)

        self.program2['u_projection'] = self.projection
        self.program2['u_view'] = self.view
        self.program2['u_model'] = self.model
        self.program2['u_color'] = np.array([1.0, 1.0, 1.0], dtype=np.float32)

        self.program2['a_position'] =  self.plane[:,:3].astype(np.float32)

    def on_timer(self, event):
        self.x += 0.05
        self.model = rotate(self.x, (0.0,0.0,1.0))
        pointys = np.concatenate((x3d,y3d,z3d)).reshape((3, -1)).T
        self.program['a_position'] = pointys
        self.program['u_model'] = self.model

        self.update()


    def on_resize(self, event):
        gloo.set_viewport(0, 0, *event.size)
        self.projection = perspective(45.0, event.size[0]/float(event.size[1]), 2.0, 10.0)
        self.program['u_projection'] = self.projection
        self.program2['u_projection'] = self.projection


    def on_draw(self, event):
        gloo.clear((1,1,1,1))
        self.program2.draw('triangle_strip')
        self.program.draw('points')

Canvas().show()
app.run()

1 个答案:

答案 0 :(得分:0)

我理解您的描述的方式,您所看到的是透视投影的结果。我使用了所有的MS Paint技能来创建这个从侧面看的情况的详细图表:

Sphere with perspective

球体轮廓用黑色绘制。红线表示穿过球体中心的平面。

蓝线显示视点的两条视线,位于图的底部。如果在应用投影后对结果进行描绘,则在渲染图像中显示为球体前面部分的内容就是绿线下方的所有内容。绿线上方的球体部分在生成的渲染中形成球体的背面部分。

或者换句话说,绿线表示与生成的渲染中球体轮廓相对应的平面。

从中可以看出,穿过球体中心的平面确实距离球体部分后面一定距离,该部分在渲染图像中显示为球体的正面部分。这只是透视投影的本质。红色平面和绿色平面之间的距离将随着较小的视角(即较弱的视角)而减小,并且当使用平行投影时两者是相同的。