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)

你可能感兴趣的:(OpenGL)