Python之OpenGL笔记(32):正交投影画六角星

一、目的

1、摄像机应用,正交投影画六角星;

二、程序运行结果

六角星

三、摄像机的设置

   吴亚峰《OpenGL ES 3.x游戏开发》(上卷)内容
   从日常生活的经验中可以很容易地了解到,随着摄像机位置、姿态的不同,就算是对同一个场景进行拍摄,得到的画面也是迥然不同的。
   摄像机的设置需要给出 3 方面的信息,包括摄像机的位置、观察的方向以及 up 方向,具体情况如图5-1 所示。


图5-1 摄像机观察物体
  • 摄像机的位置很容易理解,用其在 3D 空间中的坐标来表示。
  • 摄像机观察的方向可以理解为摄像机镜头的指向,用一个观察目标点来表示(通过摄像机位置与观察目标点可以确定一个向量,此向量即代表了摄像机观察的方向)。
  • 摄像机的 up 方向可以理解为摄像机顶端的指向,用一个向量来表示。

  可以看出,摄像机的位置、朝向、 up 方向可以有很多不同的组合。
  程序通过调用pyrr.matrix44.create_look_at()方法来完成对摄像机的设置

mvMatrix = pyrr.matrix44.create_look_at(cameraPos, cameraFront, cameraUp,None)

四、两种投影方式

  在图元装配之后的光栅化阶段前,首先需要把虚拟 3D 世界中的物体投影到二维平面上。 OpenGL中常用的投影模式有两种,分别为正交投影与透视投影

1、 正交投影

  OpenGL中,根据应用程序中ᨀ供的投影矩阵,管线会确定一个可视空间区域,称为
视景体。视景体是由 6 个平面确定的,这 6 个平面分别为:上平面(up)、下平面(down)、左平面(left)、右平面(right)、远平面(far)、近平面(near)。
  场景中处于视景体内的物体会被投影到近平面上(视景体外面的物体将被裁剪掉),然后再将近平面上投影出的内容映射到屏幕上的视口中。对于正交投影而言,视景体及近平面的情况如图 5-3所示。


图 5-3 正交投影示意图

  从图 5-3 中可以看出,由于正交投影是平行投影的一种,其投影线(物体的顶点与近平面上投影点的连线)是平行的。故其视景体为长方体,投影到近平面上的图形不会产生真实世界中“近大远小”的效果,图 5-4 更清楚地说明了这个问题


图 5-4 正交投影不产生“近大远小”效果的原理图

五、源代码

"""
程序名称:GL_orthoM.py
编程: dalong10
功能: 正交投影的实现
参考资料: 《OpenGL ES 3.x游戏开发》(上卷)吴亚峰
"""
import myGL_Funcs        #Common OpenGL utilities,see myGL_Funcs.py
import glfw
from OpenGL.GL import *
import numpy 
import numpy as np
import pyrr
from PIL import Image

StarVS = """
# version 330
layout(location = 0) in vec3 a_position; //顶点位置
layout(location = 1) in vec3 a_color;   //顶点颜色
uniform mat4 rotation;                  //总变换矩阵
out vec3 v_color;                       //用于传递给片元着色器的变量
void main()
{
    gl_Position = rotation * vec4(a_position, 1.0);  //根据总变换矩阵计算此次绘制此顶点位置 
    v_color = a_color;          //将接收的颜色传递给片元着色器
}
"""

StarFS = """
# version 330
in vec3 v_color;      //接收从顶点着色器过来的参数
out vec4 out_color;   //输出到的片元颜色
void main()
{
    out_color = vec4(v_color, 1.0f); //给此片元颜色值
}
"""

class SixPointedStar:
    def initVertexData(self,R,r,z):  # 初始化顶点数据的initVertexData方法
        self.vertexs   = np.array([], np.float32)  # 位置FloatArray(numPoint * 3)
        self.colorArray = np.array([], np.float32)    # 颜色FloatArray(numPoint * 4)
        # 把矩形平铺在一个平面上
        PI = np.pi
        tempAngle=int(360/6)
        count=0
        for angle in range(0,360,tempAngle):  # 循环生成构成六角形各三角形的顶点坐标
            x1=0.0  #第一个三角形,三个点
            y1=0.0
            z1=z
            x2=R*np.cos(PI*angle/180) 
            y2=R*np.sin(PI*angle/180)
            z2=z
            x3=r*np.cos(PI*(angle+tempAngle/2)/180) 
            y3=r*np.sin(PI*(angle+tempAngle/2)/180) 
            z3=z

            x4=0
            y4=0
            z4=z            
            x5=r*np.cos(PI*(angle+tempAngle/2)/180) 
            y5=r*np.sin(PI*(angle+tempAngle/2)/180)
            z5=z
            x6=R*np.cos(PI*(angle+tempAngle)/180) 
            y6=R*np.sin(PI*(angle+tempAngle)/180)
            z6=z

            self.vertexs=np.hstack((self.vertexs, np.array([x1,y1,z1], np.float32) ))  #每个顶点xyz三个坐标,6个顶点 
            self.vertexs=np.hstack((self.vertexs, np.array([1,1,1], np.float32) ))    #中心点为白色
            self.vertexs=np.hstack((self.vertexs, np.array([x2,y2,z2], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([0.45,0.75,0.75], np.float32) ))    #边上的点为淡蓝色
            self.vertexs=np.hstack((self.vertexs, np.array([x3,y3,z3], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([0.45,0.75,0.75], np.float32) ))    #边上的点为淡蓝色
            
            self.vertexs=np.hstack((self.vertexs, np.array([x4,y4,z4], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([1,1,1], np.float32) ))    #中心点为白色
            self.vertexs=np.hstack((self.vertexs, np.array([x5,y5,z5], np.float32) ))   
            self.vertexs=np.hstack((self.vertexs, np.array([0.45,0.75,0.75], np.float32) ))    #边上的点为淡蓝色
            self.vertexs=np.hstack((self.vertexs, np.array([x6,y6,z6], np.float32) ))            
            self.vertexs=np.hstack((self.vertexs, np.array([0.45,0.75,0.75], np.float32) ))    #边上的点为淡蓝色

    def __init__(self,R,r,z):
        self.R= R
        self.r= r
        self.z = z  
        self.initVertexData(R,r,z)
        # load shaders
        self.program = myGL_Funcs.loadShaders(StarVS, StarFS)
        #print('ok1')
        glUseProgram(self.program)
        self.vertIndex = glGetAttribLocation(self.program, b"a_position")
        self.colorIndex = glGetAttribLocation(self.program, b"a_color")
                    # set up vertex array object (VAO)
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)            
        #  Step2: 创建并绑定VBO 对象 传送数据
        #self.vertexs= vertices
        vertexData = numpy.array(self.vertexs, numpy.float32)
        self.vertexBuffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData, GL_STATIC_DRAW)                 
                    # enable arrays
        # 顶点位置属性
        glEnableVertexAttribArray(self.vertIndex)
        glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, vertexData.itemsize * 6, ctypes.c_void_p(0))               
        # 顶点颜色属性
        glEnableVertexAttribArray(self.colorIndex)
        glVertexAttribPointer(self.colorIndex, 3, GL_FLOAT, GL_FALSE, vertexData.itemsize * 6, ctypes.c_void_p(12))               
        # unbind VAO
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)    
               
    def render(self, model):  
        # use shader
        glUseProgram(self.program)
        # set modelview matrix
        glUniformMatrix4fv(glGetUniformLocation(self.program, 'rotation'), 
                          1, GL_FALSE, model)       
        # bind VAO
        glBindVertexArray(self.vao)
        # draw
        #print(len(self.vertexs))
        glDrawArrays(GL_TRIANGLES,0,len(self.vertexs) )
        # unbind VAO
        glBindVertexArray(0)       

# glfw callback functions
def window_resize(window, width, height):
    glViewport(0, 0, width, height)


if __name__ == '__main__':
    import sys
    import glfw
    import OpenGL.GL as gl
    cameraPos=np.array([2.0, 0.0, 3])      # 眼睛的位置(默认z轴的正方向)
    cameraFront=np.array([0.0, 0.0, 0.0])  # 瞄准方向的参考点(默认在坐标原点)
    cameraUp=np.array([0.0, 1.0, 0.0])     # 定义对观察者而言的上方(默认y轴的正方向)

    # Initialize the library
    if not glfw.init():
        sys.exit()
    # Create a windowed mode window and its OpenGL context
    window = glfw.create_window(400, 300, "My OpenGL window", None, None)
    if not window:
        glfw.terminate()
        sys.exit()
    # set window's position
    glfw.set_window_pos(window, 100, 100)
    # set the callback function for window resize
    glfw.set_window_size_callback(window, window_resize)
    # make the context current
    glfw.make_context_current(window)

    glClearColor(0, 0.1, 0.1, 1)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
    scale1 = pyrr.matrix44.create_from_scale(pyrr.Vector3([1, 1, 1]))
    cube1 = pyrr.matrix44.create_from_translation(pyrr.Vector3([-0.2, 0, 0]))
    board= [None]*6     #创建对象数组
    for i in range(6):
        board[i]=SixPointedStar(0.2,0.5,-0.3*i)
    # the main application loop
    while not glfw.window_should_close(window):
        width, height = glfw.get_framebuffer_size(window)
        ratio = width / float(height)
        currentFrame =  1.0*glfw.get_time()
        glfw.poll_events()           
        gl.glViewport(0, 0, width, height)       
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glMatrixMode(gl.GL_PROJECTION)
        gl.glLoadIdentity()
        gl.glOrtho(-ratio, ratio, -1, 1, 1, -1)
        gl.glMatrixMode(gl.GL_MODELVIEW)
        gl.glLoadIdentity()
        gl.glClearColor(0.0,0.0,4.0,0.0)  
        pMatrix = pyrr.matrix44.create_orthogonal_projection_matrix(-1, 1,-1,1,  1, 10.0,None)
        # modelview matrix
        mvMatrix = pyrr.matrix44.create_look_at(cameraPos, cameraFront, cameraUp,None)
        for i in range(6):           
            model1 = pyrr.matrix44.multiply(scale1, cube1)       
            model2 = pyrr.matrix44.multiply(pMatrix,model1)       
            model3 = pyrr.matrix44.multiply(mvMatrix,model2)        
            board[i].render( model3)

        glfw.swap_buffers(window)

    # terminate glfw, free up allocated resources
    glfw.terminate()

六、参考资料

1、大龙10的简书:https://www.jianshu.com/p/49dec482a291
2、吴亚峰《OpenGL ES 3.x游戏开发》(上卷)

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

推荐阅读更多精彩内容