Python之OpenGL筆記(3):用OpenGL畫第一個(gè)三角形程序解析

一、OpenGL的目的

??在OpenGL中速缨,任何事物都在3D空間中,而屏幕和窗口卻是2D像素?cái)?shù)組,這導(dǎo)致OpenGL的大部分工作都是關(guān)于把3D坐標(biāo)轉(zhuǎn)變?yōu)檫m應(yīng)你屏幕的2D像素荤傲。3D坐標(biāo)轉(zhuǎn)為2D坐標(biāo)的處理過程是由OpenGL的圖形渲染管線(Graphics Pipeline,大多譯為管線颈渊,實(shí)際上指的是一堆原始圖形數(shù)據(jù)途經(jīng)一個(gè)輸送管道遂黍,期間經(jīng)過各種變化處理最終出現(xiàn)在屏幕的過程)管理的。圖形渲染管線可以被劃分為兩個(gè)主要部分:第一部分把你的3D坐標(biāo)轉(zhuǎn)換為2D坐標(biāo)俊嗽,第二部分是把2D坐標(biāo)轉(zhuǎn)變?yōu)閷?shí)際的有顏色的像素雾家。
??圖形渲染管線接受一組3D坐標(biāo),然后把它們轉(zhuǎn)變?yōu)槟闫聊簧系挠猩?D像素輸出绍豁。圖形渲染管線可以被劃分為幾個(gè)階段芯咧,每個(gè)階段將會(huì)把前一個(gè)階段的輸出作為輸入。所有這些階段都是高度專門化的(它們都有一個(gè)特定的函數(shù))竹揍,并且很容易并行執(zhí)行敬飒。正是由于它們具有并行執(zhí)行的特性,當(dāng)今大多數(shù)顯卡都有成千上萬的小處理核心芬位,它們?cè)贕PU上為每一個(gè)(渲染管線)階段運(yùn)行各自的小程序无拗,從而在圖形渲染管線中快速處理你的數(shù)據(jù)。這些小程序叫做著色器(Shader)昧碉。

二蓝纲、頂點(diǎn)坐標(biāo)值在-1.0到1.0之間

??在涉及到光柵器之前我們使用屏幕坐標(biāo)系繪制點(diǎn)、線晌纫、和三角形税迷,點(diǎn)的坐標(biāo)用X,Y锹漱,Z表示并且他們的值都在[-1.0, 1.0]之間箭养。光柵器將這些坐標(biāo)映射到屏幕空間(比如,如果屏幕的寬度是1024哥牍,X 的坐標(biāo)為-1.0毕泌,則 X 將映射到0喝检;若 X 坐標(biāo)為1.0,則將會(huì)被將映射到1023)撼泛。最后挠说,光柵器根據(jù)指定繪圖命令的拓?fù)浣Y(jié)構(gòu)畫出圖元。
??事實(shí)上愿题,我們選取在屏幕中的零點(diǎn)作為x损俭、y兩條軸的中心點(diǎn),換句話說潘酗,零點(diǎn)是在屏幕的正中心杆兵。

三、用OpenGL畫第一個(gè)三角形程序的處理過程

??用OpenGL繪圖的實(shí)例程序主要建立了一個(gè)FirstTriangle類,有初始化init()和渲染Render()兩個(gè)方法仔夺。

(一)init()方法負(fù)責(zé)程序中所需的數(shù)據(jù)琐脏,主要完成:

1、初始化頂點(diǎn)數(shù)組對(duì)象
2缸兔、分配頂點(diǎn)緩存對(duì)象
3日裙、將數(shù)據(jù)載入緩存對(duì)象
4、初始化頂點(diǎn)與片元著色器
5惰蜜、啟用頂點(diǎn)屬性數(shù)組阅签。

(二)Render()方法負(fù)責(zé)使用OpenGL進(jìn)行渲染

四、init()方法的解析

1蝎抽、初始化頂點(diǎn)數(shù)組對(duì)象
??VAO(vertex Array Object):頂點(diǎn)數(shù)組對(duì)象政钟,是包含一個(gè)或者多個(gè)VBOs的對(duì)象,被設(shè)計(jì)用來存儲(chǔ)被完整渲染對(duì)象的相關(guān)數(shù)據(jù)信息樟结,如渲染對(duì)象的頂點(diǎn)信息和每一個(gè)頂點(diǎn)的顏色信息养交。
??在C++中 函數(shù)形式是glGenVertexArrays(GLsizei n, GLuint *arrays);
??返回n個(gè)未使用的對(duì)象名到數(shù)組arrays中,用作頂點(diǎn)數(shù)組對(duì)象瓢宦。返回的名字可以用來分配更多的緩存對(duì)象碎连,并且它們已經(jīng)使用未初始化的頂點(diǎn)數(shù)組集合的默認(rèn)狀態(tài)進(jìn) 行了數(shù)值的初始化。
??在Python中驮履,函數(shù)形式是arrays= glGenVertexArrays((GLsizei n)

self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)

2鱼辙、分配頂點(diǎn)緩存對(duì)象
??VBO(vertex Array Object):頂點(diǎn)緩沖對(duì)象,是被用來儲(chǔ)存頂點(diǎn)數(shù)據(jù)的玫镐。加載頂點(diǎn)進(jìn)入 GPU 最有效率的方法是 VBOs倒戏。它們是可以存儲(chǔ)在顯存中的緩沖區(qū),使得 GPU 訪問數(shù)據(jù)的速度最快恐似。VBO是頂點(diǎn)數(shù)組數(shù)據(jù)真正所在的地方杜跷。
??為了指定一個(gè)屬性數(shù)據(jù)的格式和來源,我們需要告訴OpenGL,編號(hào)為0的屬性使用哪個(gè)VBO葛闷,編號(hào)為1的屬性使用哪個(gè)VBO等等憋槐。
??在Python中我們使用numpy.array(vertices, numpy.float32)創(chuàng)建多維數(shù)組。
??在C++中 函數(shù)形式是glGenBuffers(GLsizei n, GLuint *buffers);返回n個(gè)當(dāng)前未使用的緩存對(duì)象名稱淑趾,并保存到buffers數(shù)組中阳仔。
??在Python中,函數(shù)形式是 buffers= glGenBuffers(GLsizei n);

vertexData = numpy.array(vertices, numpy.float32)
self.vertexBuffer = glGenBuffers(1) #創(chuàng)建VBOs
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
'''綁定VBOs目標(biāo) GL_ARRAY_BUFFER 表示緩沖區(qū)用于存儲(chǔ)頂點(diǎn)數(shù)組扣泊。'''

3近范、將數(shù)據(jù)載入緩存對(duì)象

glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData, GL_STATIC_DRAW)

??當(dāng)我們將緩沖區(qū)綁定到目標(biāo)上之后,我們需要用數(shù)據(jù)進(jìn)行填充旷赖。上面函數(shù)的參數(shù)包括:目標(biāo)點(diǎn)名稱(和上面緩沖區(qū)綁定的目標(biāo)點(diǎn)一樣)顺又,數(shù)據(jù)的字節(jié)數(shù)更卒,頂點(diǎn)數(shù)組的地址等孵,以及一個(gè)表示數(shù)據(jù)使用方式的枚舉量。因?yàn)槲覀儾恍枰淖兙彌_區(qū)中的內(nèi)容蹂空,所以我們將數(shù)據(jù)指定為 GL_STATIC_DRAW俯萌。
4、初始化頂點(diǎn)與片元著色器(暫略)

5上枕、啟用頂點(diǎn)屬性數(shù)組咐熙。
??那就是啟用頂點(diǎn)屬性數(shù)組。我們通過調(diào)用glEnableVertexAttribArray()來完成這項(xiàng)工作辨萍。

self.vertIndex = 0
glEnableVertexAttribArray(self.vertIndex)
# 由于將要調(diào)用繪圖函數(shù)棋恼,所以這里我們?cè)僖淮谓壎司彌_區(qū)。
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)

??下面這個(gè)函數(shù)調(diào)用告訴管線如何在緩沖區(qū)內(nèi)部解釋數(shù)據(jù)锈玉。第一個(gè)參數(shù)指定了屬性的索引爪飘。我們都知道0是默認(rèn)的,但是當(dāng)我們開始使用 Shaders 的時(shí)候拉背,我們?cè)?Shader 中需要明確的設(shè)置索引师崎。第二個(gè)參數(shù)是構(gòu)成屬性的分量的個(gè)數(shù)(X,Y,Z共三個(gè)分量)。第三個(gè)參數(shù)是每個(gè)分量的數(shù)據(jù)類型椅棺。第四個(gè)參數(shù)表明屬性在管線中使用之前是否需要被規(guī)范化犁罩。第五個(gè)參數(shù)是在緩沖區(qū)中兩個(gè)相同屬性值之間的間隔的字節(jié)數(shù),當(dāng)只有一個(gè)屬性時(shí)(比如緩沖區(qū)只包含頂點(diǎn)位置)并且數(shù)據(jù)是緊挨著的两疚,那么我們?cè)O(shè)置這個(gè)值為0床估。如果我們有一個(gè)包含位置屬性和法線屬性的數(shù)組(每個(gè)屬性都是一個(gè)float類型的三維向量),我們將這個(gè)值設(shè)置為6*4=24诱渤。最后一個(gè)參數(shù)在多個(gè)屬性時(shí)是有用的顷窒。我們需要指定在緩沖區(qū)中存儲(chǔ)數(shù)據(jù)的偏移值,這樣管線才會(huì)找到數(shù)據(jù)。當(dāng)頂點(diǎn)的位置和法線相鄰存儲(chǔ)時(shí)時(shí)鞋吉,我們?cè)O(shè)置頂點(diǎn)位置的偏移值為0而頂點(diǎn)法線的偏移值為12鸦做。

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
# unbind VAO
glBindVertexArray(0)

五、Render()方法的解析

??渲染工作是選擇我們準(zhǔn)備繪制的頂點(diǎn)數(shù)據(jù)谓着,然后請(qǐng)求進(jìn)行繪制泼诱。首先調(diào)用glBindVertexArray()來選擇作為頂點(diǎn)數(shù)據(jù)使用的頂點(diǎn)數(shù)組。正如前文中提到的赊锚,我們可以用這個(gè)函數(shù)來切換程序中保存的多個(gè)頂點(diǎn)數(shù)據(jù)對(duì)象集合治筒。
其次調(diào)用glDrawArrays()來實(shí)現(xiàn)頂點(diǎn)數(shù)據(jù)向OpenGL管線的傳輸。

glBindVertexArray(self.vao)
# 最后舷蒲,調(diào)用glDrawArrays函數(shù)來畫幾何體耸袜。這里就是 GPU 真正開始工作的地方。它現(xiàn)在將結(jié)合繪圖命令的參數(shù)牲平,然后創(chuàng)建一個(gè)三角形并將結(jié)果渲染到屏幕堤框。
glDrawArrays(GL_TRIANGLES, 0, 3)
# unbind VAO
glBindVertexArray(0)

六、運(yùn)行結(jié)果

11.png

七纵柿、代碼

"""
glfw_FirstTriangle.py
Author: dalong10
Description: Draw a Triagle, learning OPENGL 
"""
import glutils    #Common OpenGL utilities,see glutils.py
import sys, random, math
import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *
import numpy 
import numpy as np
import glfw

strVS = """
#version 330 core
layout(location = 0) in vec3 vertexPosition_modelspace;
void main(){
    gl_Position.xyz = vertexPosition_modelspace;
    gl_Position.w = 1.0;
    }
"""

strFS = """
#version 330 core
out vec3 color;
void main(){
    color = vec3(1,0,0);
    }
"""

class FirstTriangle:
    def __init__(self, side):
        self.side = side

        # load shaders
        self.program = glutils.loadShaders(strVS, strFS)
        glUseProgram(self.program)
        
        s = side/1.0
        vertices = [
            -s, -s, 0, 
             s, -s, 0,
             0, s, 0
             ]
                
        # set up vertex array object (VAO)
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        # set up VBOs
        vertexData = numpy.array(vertices, 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
        self.vertIndex = 0
        glEnableVertexAttribArray(self.vertIndex)
        # set buffers 
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
        # unbind VAO
        glBindVertexArray(0)

    def render(self):
        # use shader
        glUseProgram(self.program)
        # bind VAO
        glBindVertexArray(self.vao)
        # draw
        glDrawArrays(GL_TRIANGLES, 0, 3)
        # unbind VAO
        glBindVertexArray(0)

if __name__ == '__main__':
    import sys
    import glfw
    import OpenGL.GL as gl
    def on_key(window, key, scancode, action, mods):
        if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
            glfw.set_window_should_close(window,1)

    # Initialize the library
    if not glfw.init():
        sys.exit()

    # Create a windowed mode window and its OpenGL context
    window = glfw.create_window(640, 480, "Hello World", None, None)
    if not window:
        glfw.terminate()
        sys.exit()

    # Make the window's context current
    glfw.make_context_current(window)
    # Install a key handler
    glfw.set_key_callback(window, on_key)

    # Loop until the user closes the window
    while not glfw.window_should_close(window):
        # Render here
        width, height = glfw.get_framebuffer_size(window)
        ratio = width / float(height)
        gl.glViewport(0, 0, width, height)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT)

        gl.glClearColor(0.0,0.0,4.0,0.0)
        firstTriangle0 = FirstTriangle(1.0)
        # render
        firstTriangle0.render()
        # Swap front and back buffers
        glfw.swap_buffers(window)
        # Poll for and process events
        glfw.poll_events()

    glfw.terminate()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蜈抓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子昂儒,更是在濱河造成了極大的恐慌沟使,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渊跋,死亡現(xiàn)場(chǎng)離奇詭異腊嗡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拾酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門燕少,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人微宝,你說我怎么就攤上這事棺亭。” “怎么了蟋软?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵镶摘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我岳守,道長(zhǎng)凄敢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任湿痢,我火速辦了婚禮涝缝,結(jié)果婚禮上扑庞,老公的妹妹穿的比我還像新娘。我一直安慰自己拒逮,他們只是感情好罐氨,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滩援,像睡著了一般栅隐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玩徊,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天租悄,我揣著相機(jī)與錄音,去河邊找鬼恩袱。 笑死泣棋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畔塔。 我是一名探鬼主播潭辈,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼俩檬!你這毒婦竟也來了萎胰?” 一聲冷哼從身側(cè)響起碾盟,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤棚辽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冰肴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屈藐,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年熙尉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了联逻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡检痰,死狀恐怖包归,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铅歼,我是刑警寧澤公壤,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站椎椰,受9級(jí)特大地震影響厦幅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慨飘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一确憨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦休弃、人聲如沸吞歼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浆熔。三九已至,卻和暖如春桥帆,著一層夾襖步出監(jiān)牢的瞬間医增,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工老虫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叶骨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓祈匙,卻偏偏與公主長(zhǎng)得像忽刽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夺欲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容