您当前的位置:首页 > 计算机 > 编程开发 > Python

用PyOpenGL叩开3D的心扉——OpenGL全解析(5)

时间:11-16来源:作者:点击数:

演示几个例子来加深一下之前学习的东西~

我恨数学

据说这个世界上最深沉的感情不是爱而是恨,或许一开始就亮出一个数学函数能让你有动力进行下去?

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
#from numpy import *
import sys

def init():
    glClearColor(1.0, 1.0, 1.0, 1.0)
    gluOrtho2D(-5.0, 5.0, -5.0, 5.0)

def plotfunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glPointSize(3.0)

    glColor3f(1.0, 1.0, 0.0)
    glBegin(GL_LINES)
    glVertex2f(-5.0, 0.0)
    glVertex2f(5.0, 0.0)
    glVertex2f(0.0, 5.0)
    glVertex2f(0.0, -5.0)
    glEnd()

    glColor3f(0.0, 0.0, 0.0)
    glBegin(GL_LINES)
    #for x in arange(-5.0, 5.0, 0.1):
    for x in (i * 0.1 for i in range(-50, 50)):
        y = x*x
        glVertex2f(x, y)
    glEnd()

    glFlush()

def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB)
    glutInitWindowPosition(50,50)
    glutInitWindowSize(400,400)
    glutCreateWindow("Function Plotter")
    glutDisplayFunc(plotfunc)
    init()
    glutMainLoop()

main()

这个程序也是很简单的,绘制y=x2的抛物线图像,应该是初中的知识,怎么样恨意上来了没?

这里有几个小地方要说明一下,两个注释,是使用numpy这个强大的数学库进行便捷的运算,为防止有人没装或者不知道怎么装,我注释了numpy而使用Python原生的方法做了。这里需要生产从-5.0到5.0的序列,间隔为0.1,然而python的range函数只接受整数,所以用表达式来代替了。但是记住numpy的速度是很快的,3D图像处理和展示需要大量的运算,Python孱弱的原生运算能力很快就会捉襟见肘,如果可能,请使用numpy库,以后的例子,看运算量我可能会混合使用两种方法,不一定给出替代方法,这点请谅解。

再看一下glPointSize(3.0)这个语句,它是在整个绘制函数最前面调用的,但是实际的结果,仅有点变粗了,虽然坐标系也是通过点画出来的,但是没有影响;如果你想把线也画粗一点,请使用glLineWidth

但我热爱艺术

再来一个抽象艺术,第一次看到结果,我自己都被震惊了:)

OpenGL 抽象艺术画
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from numpy import *
import sys

global W, H, R
(W, H, R) = (500, 500, 10.0)

def init():
    glClearColor(1.0, 1.0, 1.0, 1.0)

def drawfunc():
    glClear(GL_COLOR_BUFFER_BIT)
    glColor3f(0.0, 0.0, 0.0)
    glBegin(GL_POINTS)
    for x in arange(-R, R, 0.04):
        print '%.1f%%r' % ((R + x) / (R + R) * 100),
        for y in arange(-R, R, 0.04):
            r = cos(x) + sin(y)
            glColor3f(cos(y*r), cos(x*y*r), sin(x*r))
            glVertex2f(x, y)
    print '100%!!'
    glEnd()
    glFlush()

def reshape(w, h):
    if h <= 0: h = 1;
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    if w <= h:
        gluOrtho2D(-R, R, -R * h / w, R * h / w)
    else:
        gluOrtho2D(-R * w / h, R * w / h, -R, R)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

def keyboard(key, x, y):
    if key == chr(27) or key == "q": # Esc is 27
        sys.exit()

def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB)
    glutInitWindowPosition(20, 20)
    glutInitWindowSize(W, H)
    glutCreateWindow("Artist Drawer")
    glutReshapeFunc(reshape)
    glutDisplayFunc(drawfunc)
    glutKeyboardFunc(keyboard)
    init()
    glutMainLoop()

main()

这个结果很漂亮,但你问怎么出来的,这个还是要归结于万恶的数学啊,而且运算速度很慢,要慢慢等才能看到结果,美丽是有代价的啊:)

这个程序里面,就有比较多的东西了,drawfunc还是一如既往,画点而已,具体为什么要这么画,我也不知道……

还注册了一个glutReshapFunc的函数,这个是什么意思呢,就是当窗口大小改变的时候做的事情,如果你不注册这个函数,那么当改变窗口大小时,可能有一部分的图像就无法显示了。而reshape里做的事情,是我们这次学习的重点。

glViewport:指定了视口程序显示的范围,也就是OpenGL绘制的范围,这里使用(0, 0, w, h)便是说明整个窗口,一般情况下总是如此,但是我们也是可以指定小于这个范围的ViewPort的。事实上我隐瞒了很多细节,这个函数必须和下面要讲的gluOrtho2D函数一起用才能出现正确的结果。

gluOrtho2D:这个函数派生于OpenGL的glOrtho,它创建了一个正交的视景体(View Volume),我们所看到的物体,都处在这个体中,四个参数分别代表了(left, right, bottom, top),也就是竖直的左右边界和水平的下上边界;而近远则是默认的(-1, 1),glOrtho有六个参数可以设定。这个体越大,我们看到的东西就越小;反之看到的东西就越大。我知道这样很难理解,打个比方就是一个六边形的鱼缸,这个函数定出了一个鱼缸的大小,我们所看的东西呢,都在这个鱼缸里面。

上面说glViewport要和gluOrtho2D一起用才能正确显示是个什么意思呢?gluOrtho2D只管创建一个视体,而glViewport只管绘图的范围,如果视体是个正方体,而窗口是个长方体,直接绘制的结果会是什么呢?很明显,整个视体里的东西都被拉长了,而一般我们viewport都是指明了窗口大小,自然只能修改视体来适应各种不同的比例了。

修改代码,拉伸窗口,查看最终的结果会是怎样的。

glMatrixMode:这个函数非常难以理解,但是又极其重要!这关系到了OpenGL中的“矩阵”的概念。矩阵……你是说黑客帝国么?好像很有趣诶~~ 嗯嗯没错,矩阵是个伟大的东西,通过它,3D世界的所有维度都蜷曲到内存中的一维数据里去了。这是一个有点儿抽象的概念但其实也没什么特别的,OpenGL里有如下几种矩阵:

  • GL_MODELVIEW:模型观察矩阵,表示物体的位置变化和观察点的改变;
  • GL_PROJECTION:投影矩阵,描述如何将一个物体投影到平面上;
  • GL_TEXTURE:纹理矩阵,描述纹理坐标的动态变化
  • ...

我不想搬出一堆数字和大括号来说明矩阵的基本运算和应用(好吧其实真实原因是我也不会:),也不会告诉你最后的ModelView矩阵是View矩阵与Model矩阵的乘积,更不会告诉你有glRotate和glTranslate之流的函数来改变矩阵!暂时这么理解就好了,矩阵就是我们走路的方向,我们现在朝南走,看到的南边的风景,然后说“向右拐”,现在看到西边的风景了,再说“向后转”,现在看到东边的风景了。就是通过这样可以累积的变换,我们把我们最初的一些数据变成了更复杂的东西表达了出来,转了几圈后也许有点糊涂了,用glLoadIdentity将当前指定的矩阵还原为最初的状态。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门