(Python OpenGL)現在開始我們使用着色器來進行渲染。着色器是目前做3D圖形最流行的方式。
OpenGL的渲染管線流程:
數據傳輸到OpenGL—>頂點處理器—>細分着色—>幾何處理器—>圖元裝配—>裁剪器—>光柵器(片段處理器)
詳細信息可以參考《OpenGL編程指南》 原書第8版 王銳譯 中的P8 -P10
一些Shader的注意:
Shader着色器的使用跟C/C++程序的創建過程類似。
1、寫一個shader着色器文本並使其在你的程序中有效可用
2、Shader着色器文本全為字符串類型 例如 S = “ coding部分” 代碼部分為字符串
3、將字符串編譯為Shader對象
4、使用GLSL編譯器(編譯器的語法見《OpenGL編程指南》 原書第8版 王銳譯)
對於每一個着色器程序需要進行下面的步驟設置:
對於着色器對象:
1、創建Shader對象
2、編譯
3、驗證編譯成功?
然后將上述的着色器對象鏈接為一個着色器程序:
1、創建程序
2、將Shader對象關聯到着色器程序
3、鏈接程序
4、判斷鏈接是否成功
5、使用着色器處理數據
Shader程序:
1 __author__ = "WSX" 2 3 import numpy as np 4 from OpenGL.GLUT import * 5 from OpenGL.GL import * 6 import ctypes 7 #頂點着色器部分 8 VERTEX_SHADER = """ 9 #version 330 10 11 layout (location = 0) in vec3 Position; 12 13 void main() 14 { 15 gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0); 16 } 17 """ 18 #片段着色器部分,字符串類型 19 FRAGMENT_SHADER = """ 20 #version 330 21 out vec4 FragColor; 22 void main() 23 { 24 FragColor = vec4(1.0, 0.0, 0.0, 1.0); 25 } 26 """ 27 def Create_Shader( ShaderProgram, Shader_Type , Source): #創建並且添加着色器(相當於AddShader)Shader_Type為類型 28 ShaderObj = glCreateShader( Shader_Type ) #創建Shader對象 29 glShaderSource(ShaderObj , Source) 30 glCompileShader(ShaderObj) #進行編譯 31 glAttachShader(ShaderProgram, ShaderObj) #將着色器對象關聯到程序上 32 33 34 def Compile_Shader(): #編譯着色器 35 Shader_Program = glCreateProgram() #創建空的着色器程序 36 Create_Shader(Shader_Program , GL_VERTEX_SHADER , VERTEX_SHADER) 37 Create_Shader(Shader_Program , GL_FRAGMENT_SHADER , FRAGMENT_SHADER) 38 glLinkProgram(Shader_Program) 39 glUseProgram(Shader_Program) 40 41 def Draw(): 42 glClear(GL_COLOR_BUFFER_BIT) 43 glEnableVertexAttribArray(0) 44 glBindBuffer(GL_ARRAY_BUFFER, VBO) 45 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None) #這里的None不能寫為0 46 glDrawArrays(GL_TRIANGLES, 0, 3) 47 glDisableVertexAttribArray(0) #解析數據 例如一個矩陣里含有 位置 、顏色、多種信息 48 glutSwapBuffers() 49 50 51 def CreateBuffer(): #創建頂點緩存器 52 global VBO #設置為全局變量 53 vertex = np.array([[-1.0,-1.0,0.0], 54 [1.0,-1.0,0.0], 55 [0.0,1.0,0.0]],dtype="float32") #創建頂點數組 56 VBO = glGenBuffers(1) #創建緩存 57 glBindBuffer(GL_ARRAY_BUFFER , VBO) #綁定 58 glBufferData(GL_ARRAY_BUFFER , vertex.nbytes , vertex , GL_STATIC_DRAW) #輸入數據 59 60 61 def main(): 62 glutInit([]) 63 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA) # 顯示模式 雙緩存 64 glutInitWindowPosition(100, 100) # 窗口位置 65 glutInitWindowSize(500, 500) # 窗口大小 66 glutCreateWindow("sanjiao") # 創建窗口 67 glutInitContextVersion(4,3) #為了兼容 68 glutInitContextProfile(GLUT_CORE_PROFILE) #為了兼容 69 glutDisplayFunc(Draw) # 回調函數 70 glClearColor(0.0, 0.0, 0.0, 0.0) 71 CreateBuffer() 72 Compile_Shader() 73 glutMainLoop() 74 75 main()
結果:
關於着色器代碼部分(字符串)的解釋:
#version 330
這告訴編譯器我們的目標是3.3版的GLSL。如果編譯器不支持它,它會發出錯誤。
layout (location = 0) in vec3 Position;
該語句出現在頂點着色器中。它聲明了一個頂點特定屬性,它是3個浮點數的向量,在着色器中將被稱為'位置'。“特定於頂點”表示對於GPU中每個對着色器的調用,將提供緩沖區中新頂點的值。
layout (location = 0)創建緩沖區中屬性名稱和屬性之間的綁定。
您可以通過將多個着色器對象鏈接在一起來創建着色器。但是,每個着色器階段(VS,GS,FS)只能有一個主要函數,用作着色器的入口點。例如,您可以創建一個具有多個函數的光照庫,並將其與着色器鏈接,前提是這些函數中沒有任何一個函數名為“main”。
gl_Position = vec4(0.5 * Position.x, 0.5 * Position.y, Position.z, 1.0);
這里我們對輸入的頂點位置進行硬編碼轉換。我們將X和Y值減半,並保持Z不變。'gl_Position'是一個特殊的內置變量,它應該包含齊次(包含X,Y,Z和W分量)頂點位置。光柵化器將查找該變量並將其用作屏幕空間中的位置(在進行了幾次轉換之后)。將X和Y值減半意味着我們將看到一個三角形,它是前一教程中三角形大小的四分之一。請注意,我們將W設置為1.0。這對於正確顯示三角形非常重要。從3D到2D的投影實際上分兩個階段完成。首先,您需要將所有頂點乘以投影矩陣(我們將在一些教程中開發),然后GPU在位置屬性到達光柵化器之前自動執行所謂的“透視分割”。這意味着它將W組件的所有組件分割成W組件。在本教程中,我們尚未在頂點着色器中進行任何投影,但透視分割階段是我們無法禁用的。無論我們從頂點着色器輸出的gl_Position值是用HW分量還是HW分量。我們需要記住,否則我們不會得到我們期望的結果。為了規避視角分割的影響,我們將W設置為1.0。由1除。
如果一切工作正常,則具有值(-0.5,-0.5),(0.5,-0.5)和(0.0,0.5)的三個頂點到達光柵器。裁剪器不需要做任何事情,因為所有的頂點都在標准化的框內。這些值被映射到屏幕空間坐標,並且光柵化器開始運行三角形內的所有點。對於每個點,執行片段着色器。以下着色器代碼是從片段着色器中獲取的。
out vec4 FragColor;
通常片段着色器的工作是確定片段(像素)的顏色。此外,片段着色器可以完全丟棄像素或更改其Z值(這會影響后續Z測試的結果)。通過聲明上述變量來輸出顏色。四個分量表示R,G,B和A(對於alpha)。您設置到該變量中的值將由光柵化器接收並寫入幀緩沖區。
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
在之前的幾個教程中,沒有片段着色器,因此所有內容都以白色的默認顏色繪制。在這里,我們將FragColor設置為紅色。
更多關於着色器部分的解釋:http://ogldev.atspace.co.uk/www/tutorial04/tutorial04.html