本文转自:http://eyehere.net/2013/learn-opengl-3d-by-pyopengl-5/
我不得不演示几个例子来加深一下之前学习的东西(时隔这么久了,有点难以为继的感觉啊)~
据说这个世界上最深沉的感情不是爱而是恨,或许一开始就亮出一个数学函数能让你有动力进行下去?
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
。
再来一个抽象艺术,第一次看到结果,我自己都被震惊了:)
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里有如下几种矩阵:
我不想搬出一堆数字和大括号来说明矩阵的基本运算和应用(好吧其实真实原因是我也不会:),也不会告诉你最后的ModelView矩阵是View矩阵与Model矩阵的乘积,更不会告诉你有glRotate和glTranslate之流的函数来改变矩阵!暂时这么理解就好了,矩阵就是我们走路的方向,我们现在朝南走,看到的南边的风景,然后说“向右拐”,现在看到西边的风景了,再说“向后转”,现在看到东边的风景了。就是通过这样可以累积的变换,我们把我们最初的一些数据变成了更复杂的东西表达了出来,转了几圈后也许有点糊涂了,用glLoadIdentity将当前指定的矩阵还原为最初的状态。