大家好!
本期丹丹將給大家解析第一個OpenGL程序
本期視頻的連結地址是:
https://www.bilibili.com/video/av21319715
大家也可以直接在bi站首頁搜索:New程序媛 ,即可看到相應視頻
本期的資源和代碼下載連結是:
連結:https://pan.baidu.com/s/1w4PiW8gNwiIu9gIOxUW_VA 密碼:mpfz
視頻和文章一起搭配效果更好哦
首先咱們來看看今天代碼的完整版:
代碼運行起來的效果如下圖:
接下來的工作就是逐句來做解析啦!
第一個模塊我們要來看的就是main 函數,因為main函數是我們的程序入口函數。
glutInit:該函數的調用是,glut提供的負責初始化的函數,它必須是應用程式調用的第一個glut函數。
glutInitDisplayMode:設置了程序所使用的窗口類型,GLUT_DOUBIE表示使用了雙緩衝,GLUT_RGBA表示設置窗口使用RGBA顏色。
glutInitWindowSize:設置窗口的大小。
glutCreateWindow:創建窗口,如果當前的系統環境可以滿足glutDisplayMode的顯示模式要求。
glewInit: glew庫的初始化
glutDisplayFunc:它設置了顯示會調函數,即glut在每次更新窗口內容的時候會自動執行的函數。
Init:自定義的一個初始化函數。
glutMainLoop:這是一個無限之行的循環,他會負責一直處理窗口和作業系統的用戶輸入等操作,比如它會判斷窗口是否需要進行重繪,如果需要它就會自動調用glutDisplayFunc註冊的函數。
接下來看看繪製的頂點數據初始化:
vertices數組:頂點數據的定義,我們一共定義了四個頂點,每個頂點有xyz 三個分量
indices數組 :索引數據的定義,索引號是從0開始,索引0表示的是加載到程序中頂點數據中的第一個頂點,而數組0 1 3則表示繪製順序是分別取到0號1號3號頂點依次繪製
VAO: 表示vertex array object 的ID
VBO: 表示vertex buffer object 的ID
EBO: 表示elements buffer object的ID
知識點:
很多 OpenGL命令都是 glGen*的形式,它們負責分配不同類型的 OpenGL
對象的名稱。這裡的名稱就是類似C語言中的一個指針變量,我們必須分配內存並且用名稱引用它之後,名稱才有意義。在 OpenGL中,這個分配的機制叫做綁定對象( bind an object),它是通過一系列 glBind*形式的函數集合去實現的。如果綁定到一個已經創建的頂點數組對象中,那麼會激活這個頂點數組對象,並且直接影響對象中所保存的頂點數組狀態。
有了這些知識點,我們就來一起看看InitData函數:
glGenVertexArrays(1, &VAO); 分配一個頂點數組對象,ID保存在VAO
glGenBuffers(1, &VBO);分配一個頂點緩存對象,ID保存在VBO
glGenBuffers(1, &EBO);分配一個索引緩存對象,ID保存在EBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);綁定了一個頂點數組對象。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);指定上句綁定buffer對象的具體數據
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);綁定了一個索引數組對象。
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);指定上句綁定buffer對象的具體數據
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);該函數的調用是告訴OpenGL從內存中如何取數據。
接下來的三條語句就是將數據內容解綁,也就是重置為零ID,即為不綁定和操作任何數據。
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
有了數據,我們要來看的就是對應的著色器代碼啦。
知識點:
對於每一個 OPENGL程序,當它所使用的 OPENGL版本高於或等於3.1時,都需要指定至少兩個著色器:頂點著色器和片元著色器。
對於 OPENGL程式設計師而言,著色器就是使用 OPENGL著色語言( OPENGL Shading Language,GLSL)編寫的一個小型函數。GLSL是構成所有 OPENGL著色器的語言,它與C++語言非常類似,儘管GLSL中的所有特性並不能用於 OPENGL的每個著色階段。我們可以以字符串的形式傳輸GLSL著色器到 OPENGL。今天我們的例子中也正是這樣使用的。著色器的詳細內容我們會在接下來的推送中給大家詳細講解。
首先看到我們的vertexShaderSource(頂點著色器)和fragmentShaderSource(片元著色器)定義:
#version 330core 指定了我們所用的 Opengl著色語言的版本。330表示430版本。可能大家會疑惑,我們不是講4.3版本麼?沒關係,此處改成430即可。
layout(location=0),是布局限定,目的是為變量提供元數據。我們可以使用布局限定符來設置很多不同的屬性,其中有些是與不同的著色階段相關的。
在這裡,設置 position的位置屬性 location為0。
in欄位,它指定了數據進入著色器的流向,還可以聲明變量為out。片元著色器中我們就使用的是out,表示輸出數據。
position和color都是我們自己聲明的變量名,顧名思義就是位置和顏色。
而gl_Positon則是頂點著色器的指定輸出位置。
我們在著色器的main()函數中實現它的主體部分。 Opengl的所有著色器,無論是
處於哪個著色階段,都會有一個main()函數。對於頂點著色器而言,它所實現的就是將輸入的頂點位置複製到頂點著色器的指定輸出位置gl_Position中。而片元著色器的作用則是將輸出的顏色設置為指定的顏色,顏色分量的四個值分別是RGBA。
知識點:
Opengl著色器程序的編寫與C語言等基於編譯器的語言非常類似。我們使用編譯器來解析程序,檢查是否存在錯誤,然後將它翻譯為目標代碼。然後,在連結過程中將一系列目標文件合併,並產生最終的可執行程序。在程序中使用GLSL著色器的過程與之類似,只不過編譯器和連結器都是 OPENGL API的一部分而已。
程序中通過下面的步驟我們對每個著色器程序進行設置。
對於每個著色器對象:
1)創建一個著色器對象。
2)將著色器原始碼編譯為對象。
3)驗證著色器的編譯是否成功。
然後需要將多個著色器對象連結為一個著色器程序,包括:
1)創建一個著色器程序。
2)將著色器對象關聯到著色器程序。
3)連結著色器程序。
4)判斷著色器的連結過程是否成功完成。
5)使用著色器來處理頂點和片元。
好了,那咱們再來看看著色器代碼部分:
glCreateShader:根據給定的參數創建著色器對象。
glShaderSource:指定著色器對象的原始碼內容。
glCompileShader:編譯。
glGetShaderiv:根據我們給定的參數,判斷編譯是否成功。
glCreateProgram:創建一個著色器程序。
glAttachShader:將著色器對象關聯到著色器程序。
glLinkProgram:連結著色器程序。
glGetProgramiv:判斷著色器的連結過程是否成功完成。
glDeleteShader:刪除釋放指定的著色器對象。
好啦,數據初始化完成,著色器也初始化完成啦
接著就是咱們的Init函數進行統一調用了
glClearColor:指定了背景色
RenderScene函數逐句解析:
glClear:指定每次渲染清空掉的是顏色緩衝區的內容。
glUseProgram:使用指定的著色器程序。
glBindVertexArray:綁定對應的頂點數組,參數為0是解綁。
glDrawElements:按照指定圖元和指定索引數組繪製對應的圖形。
glutSwapBuffers:從後臺緩衝區刷新到前臺緩衝區。
好啦~咱們的第一個程序就解析工作完畢!碼起來~~~~
希望小夥伴們多多捧場,添加關注並介紹給身邊的小夥伴哦丹丹也期待大家的意見和建議,歡迎小夥伴們積極留言