Python之OpenGL笔记(2):现代OpenGL编程常用的几个通用函数

一、现代OpenGL:三维图形管线

  OpenGL 应用编程接口(API)从固定功能的图形管线转为可编程的图形管线。
  简化三维图形管线分为6步:

1、三维几何图形定义(VBO等)

  在第一步,通过定义在三维空间中的三角形的顶点,并指定每个顶点相关联的颜色,我们定义了三维几何图形。

2、顶点着色器

  接下来,变换这些顶点:第一次变换将这些顶点放在三维空间中,第二次变换将三维坐标投影到二维空间。根据照明等因素,对应顶点的颜色值也在这一步中计算,这在代码中通常称为“顶点着色器”。

3、光栅化

  接着,将几何图形“光栅化”(从几何物体转换为像素),

4、片段着色器

  针对每个像素,执行另一个名为“片段着色器”的代码块。正如顶点着色器作用于三维顶点,片段着色器作用于光栅化后的二维像素。

5、帧缓冲区操作(深度测试、混合等)

  最后,像素经过一系列帧缓冲区操作,其中,它经过“深度缓冲区检验”(检查一个片段是否遮挡另一个)、“混合”(用透明度混合两个片段)以及其他操作,其当前的颜色与帧缓冲区中该位置已有的颜色结合。

6、帧缓冲区

  这些变化最终体现在最后的帧缓冲区上,通常显示在屏幕上。

二、几个常用的通用函数和类

  这几个常用的通用函数和类是《Python极客项目编程》作者,大虾Mahesh Venkitachalam写的。使用时在程序中使用import glutils导入即可。Git地址:https://github.com/electronut/pp/blob/master/common/glutils.py

1、加载图像作纹理的函数loadTexture(filename)

  loadTexture()函数用 Python 图像库(PIL)的 Image 模块读取图像文件。
  然后获取 Image 对象的数据,放入 8 位的 numpy 数组,创建一个 OpenGL 纹理对象,这是在 OpenGL 中利用纹理做任何事的先决条件。执行现在你比较熟悉的绑定 texture 对象,这样所有后来纹理相关的设置都应用于该对象。将数据的拆包对齐设置为 1,这意味着该图像数据被硬件认为是 1 字节(或 8 位)的数据。
  告诉 OpenGL 如何处理边缘的纹理。在这个例子中,在几何图形的边缘截取纹理颜色(指定纹理坐标时,惯例是使用字母 S 和 T 表示轴,而不是 x 和 y)。
  指定插值类型,在拉伸或压缩纹理来覆盖多边形时采用。在这个例子中,指定为“线性滤波”。
  设置绑定纹理中的图像数据。此时,图像数据传送到显存,纹理准备好使用了。

2、生成透视矩阵的函数perspective()

  fov:角度;aspect:长宽比;zNear:近点;zFar:远点
  math.radians(45.0) :将45度转换成弧度。
  numpy.array([ ],numpy.float32):建立浮点型数组。

3、生成正交矩阵的函数ortho(l, r, b, t, n, f)

  正透视

4、生成视点矩阵的函数lookAt(eye, center, up)

  视点

5、生成平移矩阵的函数translate(tx, ty, tz)

  生成带点(tx, ty, tz)的4×4矩阵;

6、读取链接顶点着色器和片元着色器的源程序字串的函数;

  compileShader() 编译;
  glCreateProgram() 创建程序;
  glAttachShader() 关联
  glLinkProgram()链接

7、着色器的编译函数;

  glShaderSource(shader, source)替换着色器对象中的源代码

8、相机对象类。

  摄像机/观察空间(Camera/View Space)
  以摄像机的透视图作为场景原点时场景中所有可见顶点坐标。观察矩阵把所有的世界坐标变换到观察坐标,这些新坐标是相对于摄像机的位置和方向的。
  设置了 OpenGL 的观看参数三维立体图的特征通常在于 3 个参数:眼睛位置、向上向量和方向向量。Camera类包含这些参数,并提供了一种便捷的方式,在每一个时间步骤旋转。
  现在我们已经有了x轴向量和z轴向量,获取摄像机的正y轴相对简单;我们把右向量和方向向量(Direction Vector)进行叉乘。
  在构造函数设置了 camera 对象的初始值。
  调用 rotate()方法时,增加旋转角度,并计算旋转后新的眼睛位置和方向。
  其中点(r cos(θ), r sin(θ))表示一个点,它在以原点为中心、半径为 r 的圆上,θ 是该点到原点的连线与 x 轴的夹角。转换中使用了 center,确保即使旋转中心不在原点,也能工作。

三、源代码

"""
glutils.py
Author: Mahesh Venkitachalam
Some OpenGL utilities.
"""

import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *

import numpy, math
import numpy as np

from PIL import Image

def loadTexture(filename):
    """load OpenGL 2D texture from given image file"""
    img = Image.open(filename) 
    imgData = numpy.array(list(img.getdata()), np.int8)
    texture = glGenTextures(1)
    glPixelStorei(GL_UNPACK_ALIGNMENT,1)
    glBindTexture(GL_TEXTURE_2D, texture)
    glPixelStorei(GL_UNPACK_ALIGNMENT,1)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1], 
                 0, GL_RGBA, GL_UNSIGNED_BYTE, imgData)
    glBindTexture(GL_TEXTURE_2D, 0)
    return texture

def perspective(fov, aspect, zNear, zFar):
    """returns matrix equivalent for gluPerspective"""
    fovR = math.radians(fov)
    f = 1.0/math.tan(fovR/2.0)
    return numpy.array([f/float(aspect), 0.0,   0.0,                0.0, 
                        0.0,        f,   0.0,                0.0, 
                        0.0, 0.0, (zFar+zNear)/float(zNear-zFar),  -1.0, 
                        0.0, 0.0, 2.0*zFar*zNear/float(zNear-zFar), 0.0], 
                       numpy.float32)

def ortho(l, r, b, t, n, f):
    """returns matrix equivalent of glOrtho"""
    return numpy.array([2.0/float(r-l), 0.0, 0.0, 0.0,
                        0.0, 2.0/float(t-b), 0.0, 0.0,
                        0.0, 0.0, -2.0/float(f-n), 0.0,
                        -(r+l)/float(r-l), -(t+b)/float(t-b), 
                        -(f+n)/float(f-n), 1.0], 
                       numpy.float32)


def lookAt(eye, center, up):
    """returns matrix equivalent of gluLookAt - based on MESA implementation"""
    # create an identity matrix
    m = np.identity(4, np.float32)

    forward = np.array(center) - np.array(eye)
    norm = np.linalg.norm(forward)
    forward /= norm
    
    # normalize up vector
    norm = np.linalg.norm(up)
    up /= norm

    # Side = forward x up 
    side = np.cross(forward, up)
    # Recompute up as: up = side x forward 
    up = np.cross(side, forward)

    m[0][0] = side[0]
    m[1][0] = side[1]
    m[2][0] = side[2]
 
    m[0][1] = up[0]
    m[1][1] = up[1]
    m[2][1] = up[2]
 
    m[0][2] = -forward[0]
    m[1][2] = -forward[1]
    m[2][2] = -forward[2]

    # eye translation
    t = np.identity(4, np.float32)
    t[3][0] += -eye[0]
    t[3][1] += -eye[1]
    t[3][2] += -eye[2]
    
    return t.dot(m)

def translate(tx, ty, tz):
    """creates the matrix equivalent of glTranslate"""
    return np.array([1.0, 0.0, 0.0, 0.0, 
                     0.0, 1.0, 0.0, 0.0, 
                     0.0, 0.0, 1.0, 0.0, 
                     tx, ty, tz, 1.0], np.float32)

def loadShaders(strVS, strFS):
    """load vertex and fragment shaders from strings"""
    # compile vertex shader
    shaderV = compileShader([strVS], GL_VERTEX_SHADER)
    # compiler fragment shader
    shaderF = compileShader([strFS], GL_FRAGMENT_SHADER)
    
    # create the program object
    program = glCreateProgram()
    if not program:
        raise RunTimeError('glCreateProgram faled!')

    # attach shaders
    glAttachShader(program, shaderV)
    glAttachShader(program, shaderF)

    # Link the program
    glLinkProgram(program)

    # Check the link status
    linked = glGetProgramiv(program, GL_LINK_STATUS)
    if not linked:
        infoLen = glGetProgramiv(program, GL_INFO_LOG_LENGTH)
        infoLog = ""
        if infoLen > 1:
            infoLog = glGetProgramInfoLog(program, infoLen, None);
        glDeleteProgram(program)
        raise RunTimeError("Error linking program:\n%s\n", infoLog);
    
    return program

def compileShader2(source, shaderType):
    """Compile shader source of given type
    source -- GLSL source-code for the shader
    shaderType -- GLenum GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, etc,
    returns GLuint compiled shader reference
    raises RuntimeError when a compilation failure occurs
    """
    if isinstance(source, str):
        print('string shader')
        source = [source]
    elif isinstance(source, bytes):
        print('bytes shader')
        source = [source.decode('utf-8')]
        
    shader = glCreateShader(shaderType)
    glShaderSource(shader, source)
    glCompileShader(shader)
    result = glGetShaderiv(shader, GL_COMPILE_STATUS)
    
    if not(result):
        # TODO: this will be wrong if the user has
        # disabled traditional unpacking array support.
        raise RuntimeError(
            """Shader compile failure (%s): %s"""%(
                result,
                glGetShaderInfoLog( shader ),
                ),
            source,
            shaderType,
            )
    return shader

# a simple camera class
class Camera:
    """helper class for viewing"""
    def __init__(self, eye, center, up):
        self.r = 10.0
        self.theta = 0
        self.eye = numpy.array(eye, numpy.float32)
        self.center = numpy.array(center, numpy.float32)
        self.up = numpy.array(up, numpy.float32)

    def rotate(self):
        """rotate eye by one step"""
        self.theta = (self.theta + 1) % 360
        # recalculate eye
        self.eye = self.center + numpy.array([
                self.r*math.cos(math.radians(self.theta)),
                self.r*math.sin(math.radians(self.theta)), 
                0.0], numpy.float32)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容