摘要:介紹Python和OpenGL的入門知識,包括安裝、語法、基本圖形繪制等,
本文分享自華為云社區《[Python影像處理] 二十七.OpenGL入門及繪制基本圖形(一)》,作者:eastmount,
一.OpenGL入門知識
1.什么是OpenGL
OpenGL(Open Graphics Library,譯為“開放式圖形庫”) 是用于渲染2D、3D矢量圖形的跨語言、跨平臺的應用程式編程介面(API),這個介面由近350個不同的函式呼叫組成,用來繪制從簡單的圖形元件到復雜的三維景象,OpenGL常用于CAD、虛擬現實、科學可視化程式和電子游戲開發,
OpenGL可用于設定所需的物件、影像和操作,以便開發互動式的3維計算機圖形應用程式,OpenGL被設計為一個現代化的、硬體無關的介面,因此我們可以在不考慮計算機作業系統或視窗系統的前提下,在多種不同的圖形硬體系統上,或者完全通過軟體的方式實作OpenGL的介面,OpenGL的高效實作(利用了圖形加速硬體)存在于Windows,部分UNIX平臺和Mac OS,這些實作一般由顯示設備廠商提供,而且非常依賴于該廠商提供的硬體,
OpenGL規范由1992年成立的OpenGL架構評審委員會(ARB)維護,ARB由一些對創建一個統一的、普遍可用的API特別感興趣的公司組成,到了今天已經發布了非常多的OpenGL版本,以及大量構建于OpenGL之上以簡化應用程式開發程序的軟體庫,這些軟體庫大量用于視頻游戲、科學可視化和醫學軟體的開發,或者只是用來顯示影像,
一個用來渲染影像的OpenGL程式需要執行的主要操作如下:
- 從OpenGL的幾何圖元中設定資料,用于構建形狀
- 使用不同的著色器(shader)對輸入的圖元資料執行計算操作,判斷它們的位置、顏色,以及其他渲染屬性
- 將輸入圖元的數學描述轉換為與螢屏位置對應的像素片元(fragment),這一步也稱作光柵化(rasterization)
- 最后,針對光柵化程序產生的每個片元,執行片元著色器(fragment shader),從而決定這個片元的最終顏色和位置
- 如果有必要,還需要對每個片元執行一些額外的操作,例如判斷片元對應的物件是否可見,或者將片元的顏色與當前螢屏位置的顏色進行融合
2.OpenGL安裝
作者的電腦環境為Win10+Python3.7,打開CMD呼叫pip工具進行安裝,如下圖所示,
cd C:\Software\Program Software\Python37\Scripts
pip install pyopengl
但通常安裝成功之后,運行代碼會報錯“OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling”,
據說是pip默認安裝的是32位版本的pyopengl,而作者的作業系統是64位,網上很多大牛會去 “https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl” 網站下載適合自己的版本,比如Python3.7且64位作業系統,
安裝流程如下所示:
pip install D:\PyOpenGL-3.1.5-cp37-cp37m-win_amd64.whl pip install D:\PyOpenGL-3.1.5-cp37-cp37m-win32.whl
寫到這里,我們Python的OpenGL庫就安裝成功了!
二.OpenGL入門程式
我們首先介紹兩個入門代碼,然后再進行深入的講解,
1.OpenGL繪制正方形
完整代碼如下:
# -*- coding: utf-8 -*- from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * # 繪制影像函式 def display(): # 清除螢屏及深度快取 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) # 設定紅色 glColor3f(1.0, 0.0, 0.0) # 開始繪制四邊形 glBegin(GL_QUADS) # 繪制四個頂點 glVertex3f(-0.5, -0.5, 0.0) glVertex3f(0.5, -0.5, 0.0) glVertex3f(0.5, 0.5, 0.0) glVertex3f(-0.5, 0.5, 0.0) # 結束繪制四邊形 glEnd() # 清慷訓沖區并將指令送往硬體執行 glFlush() # 主函式 if __name__ == "__main__": # 使用glut庫初始化OpenGL glutInit() # 顯示模式 GLUT_SINGLE無緩沖直接顯示|GLUT_RGBA采用RGB(A非alpha) glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA) # 設定視窗位置大小 glutInitWindowSize(400, 400) # 創建視窗 glutCreateWindow("eastmount") # 呼叫display()函式繪制影像 glutDisplayFunc(display) # 進入glut主回圈 glutMainLoop()
運行結果如下圖所示:
核心步驟如下:
- 主函式使用glut庫初始化OpenGL
glutInit() - 設定顯示模式并初始化glut視窗(畫布)
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA)
glutInitWindowSize(400, 400)
glutCreateWindow(“eastmount”) - 注冊繪制影像的回呼函式,如display()
glutDisplayFunc(display) - 繪制影像display函式,包括清除畫布、設定顏色、繪制圖元、設定定點、結束繪制、重繪執行
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_QUADS)
glVertex3f(-0.5, -0.5, 0.0)
glVertex3f(0.5, -0.5, 0.0)
glVertex3f(0.5, 0.5, 0.0)
glVertex3f(-0.5, 0.5, 0.0)
glEnd()
glFlush() - 進入glut主回圈
glutMainLoop()
2.OpenGL繪制水壺
接著補充一段經典的水壺代碼,所有計算機試卷、計算機圖形學、3D影像領域都會繪制它,
# -*- coding: utf-8 -*- from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * # 繪制影像函式 def drawFunc(): # 清除螢屏及深度快取 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) # 設定繞軸旋轉(角度,x,y,z) glRotatef(0.1, 5, 5, 0) # 繪制實心茶壺 # glutSolidTeapot(0.5) # 繪制線框茶壺 glutWireTeapot(0.5) # 重繪顯示影像 glFlush() # 主函式 if __name__ == "__main__": # 使用glut庫初始化OpenGL glutInit() # 顯示模式 GLUT_SINGLE無緩沖直接顯示|GLUT_RGBA采用RGB(A非alpha) glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA) # 設定視窗位置及大小 glutInitWindowPosition(0, 0) glutInitWindowSize(400, 400) # 創建視窗 glutCreateWindow("CSDN Eastmount") # 呼叫display()函式繪制影像 glutDisplayFunc(drawFunc) # 設定全域的回呼函式 # 當沒有視窗事件到達時,GLUT程式功能可以執行后臺處理任務或連續影片 glutIdleFunc(drawFunc) # 進入glut主回圈 glutMainLoop()
運行結果如下圖所示,它主要呼叫glutSolidTeapot(0.5)函式繪制實作實心茶壺,glutWireTeapot(0.5)函式繪制線框茶壺,

注意,glut提供了一些現成的繪制立體的API,如glutWireSphere繪制球、glutWireCone繪制椎體、glutWireCube繪制立體、glutWireTorus繪制甜圈、glutWireTeapot繪制茶壺、glutWireOctahedron繪制八面體,請讀者自行提升,
3.OpenGL繪制多個圖形
接下來繪制一個坐標系,并分別繪制四個圖形,設定不同顏色,代碼如下所示,
# -*- coding: utf-8 -*- from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * # 繪制影像函式 def display(): # 清除螢屏及深度快取 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) # 繪制線段 glBegin(GL_LINES) glVertex2f(-1.0, 0.0) # 左下角頂點 glVertex2f(1.0, 0.0) # 右下角頂點 glVertex2f(0.0, 1.0) # 右上角頂點 glVertex2f(0.0, -1.0) # 左上角頂點 glEnd() # 繪制頂點 glPointSize(10.0) glBegin(GL_POINTS) glColor3f(1.0, 0.0, 0.0) # 紅色 glVertex2f(0.3, 0.3) glColor3f(0.0, 1.0, 0.0) # 綠色 glVertex2f(0.5, 0.6) glColor3f(0.0, 0.0, 1.0) # 藍色 glVertex2f(0.9, 0.9) glEnd() # 繪制四邊形 glColor3f(1.0, 1.0, 0) glBegin(GL_QUADS) glVertex2f(-0.2, 0.2) glVertex2f(-0.2, 0.5) glVertex2f(-0.5, 0.5) glVertex2f(-0.5, 0.2) glEnd() # 繪制多邊形 glColor3f(0.0, 1.0, 1.0) glPolygonMode(GL_FRONT, GL_LINE) glPolygonMode(GL_BACK, GL_FILL) glBegin(GL_POLYGON) glVertex2f(-0.5, -0.1) glVertex2f(-0.8, -0.3) glVertex2f(-0.8, -0.6) glVertex2f(-0.5, -0.8) glVertex2f(-0.2, -0.6) glVertex2f(-0.2, -0.3) glEnd() # 繪制三角形 glColor3f(1.0, 1.0, 1.0) glPolygonMode(GL_FRONT, GL_FILL) glPolygonMode(GL_BACK, GL_LINE) glBegin(GL_TRIANGLES) glVertex2f(0.5, -0.5) glVertex2f(0.3, -0.3) glVertex2f(0.2, -0.6) # 結束繪制四邊形 glEnd() # 清慷訓沖區并將指令送往硬體執行 glFlush() # 主函式 if __name__ == "__main__": # 使用glut庫初始化OpenGL glutInit() # 顯示模式 GLUT_SINGLE無緩沖直接顯示|GLUT_RGBA采用RGB(A非alpha) glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA) # 設定視窗位置及大小 glutInitWindowSize(400, 400) glutInitWindowPosition(500, 300) # 創建視窗 glutCreateWindow("CSDN Eastmount") # 呼叫display()函式繪制影像 glutDisplayFunc(display) # 進入glut主回圈 glutMainLoop()
輸出結果如下圖所示:
4.OpenGL繪圖代碼及原理詳解
該部分將詳細講解上面三段代碼的核心知識,幫助大家鞏固基礎,作者讓大家先看代碼及其運行效果,從而提升OpenGL編程興趣,再深入分析其原理,這種倒敘的方式希望您們喜歡,
(1) 核心函式
上述代碼中,以glut開頭的函式都是GLUT工具包所提供的函式,
- glutInit():對GLUT進行初始化,該函式必須在其它的GLUT使用之前呼叫一次,其格式比較死板,一般glutInit()直接呼叫即可,
- glutInitDisplayMode():設定顯示方式,其中GLUT_RGB表示使用RGB顏色,與之對應的是GLUT_INDEX(表示使用索引顏色);GLUT_SINGLE表示使用單緩沖,與之對應的是GLUT_DOUBLE(表示使用雙緩沖),更多引數請讀者閱讀官方網站或Google,
- glutInitWindowPosition():設定視窗在螢屏中的位置,
- glutInitWindowSize():設定視窗的大小,兩個引數表示長度和寬度,
- glutCreateWindow():根據當前設定的資訊創建視窗,引數將作為視窗的標題,需要注意的是,當視窗被創建后,并不是立即顯示到螢屏上,需要呼叫glutMainLoop()才能看到視窗,
- glutDisplayFunc():設定一個函式,當需要進行畫圖時,這個函式就會被呼叫,通常用來呼叫繪制圖形函式,
- glutMainLoop():進行一個訊息回圈,大家需要知道這個函式可以顯示視窗,并且等待視窗關閉后才會回傳,
以gl開頭的函式都是OpenGL的標準函式,
- glClear():清除,其中引數GL_COLOR_BUFFER_BIT表示清除顏色,GL_DEPTH_BUFFER_BIT表示清除深度,
- glRectf():畫一個矩形,四個引數分別表示位于對角線上的兩個點的橫、縱坐標,
- glFlush():重繪顯示影像,保證前面的OpenGL命令立即執行,而不是讓它們在緩沖區中等待,
- OpenGL要求指定頂點的命令(glVertex2f)必須包含在glBegin()函式和glEnd()函式之間執行,
(2) 繪制頂點
頂點(vertex)是 OpengGL 中非常重要的概念,描述線段、多邊形都離不開頂點,它們都是以glVertex開頭,后面跟一個數字和1~2個字母,比如:
- glVertex2d
- glVertex2f
- glVertex3f
- glVertex3fv
數字表示引數的個數,2表示有2個引數(xy坐標),3表示三個(xyz坐標),4表示四個(齊次坐標 w),字母表示引數的型別,s表示16位整數(OpenGL中將這個型別定義為GLshort),i表示32位整數(OpenGL中將這個型別定義為GLint和GLsizei),f表示32為浮點數(OpenGL中將這個型別定義為GLfloat和GLclampf),d表示64位浮點數(OpenGL中將這個型別定義為GLdouble和GLclampd),例如:
- glVertex2i(1, 3)
- glVertex2f(1.0, 3.0)
- glVertex3f(1.0, 3.0, 1.0)
- glVertex4f(1.0, 3.0, 0.0, 1.0)
注意,OpenGL中很多函式都采用這種形式命名,
(3) 設定顏色
在OpenGL中,設定顏色函式以glColor開頭,后面跟著引數個數和引數型別,引數可以是0到255之間的無符號整數,也可以是0到1之間的浮點數,三個引數分別表示RGB分量,第四個引數表示透明度(其實叫不透明度更恰當),以下最常用的兩個設定顏色的方法:
- glColor3f(1.0,0.0,0.0) #紅色
- glColor3f(0.0,1.0,0.0) #綠色
- glColor3f(0.0,0.0,1.0) #藍色
- glColor3f(1.0,1.0,1.0) #白色
- glColor4f(0.0,1.0,0.0,0.0) #紅色且不透明度
- glColor3ub(255, 0, 0) #紅色
注意,OpenGL是使用狀態機模式,顏色是一個狀態變數,設定顏色就是改變這個狀態變數并一直生效,直到再次呼叫設定顏色的函式,除了顏色,OpenGL 還有很多的狀態變數或模式,
(4) 繪制基本圖形
前面我們介紹了各種影像,下表展示了常見的影像元件,
- GL_POINTS:繪制頂點
- GL_LINES:繪制線段
- GL_LINE_STRIP:繪制連續線段
- GL_LINE_LOOP:繪制閉合的線段
- GL_POLYGON:繪制多邊形
- GL_TRIANGLES:繪制三角形
- GL_TRIANGLE_STRIP:繪制連續三角形
- GL_TRIANGLE_FAN:繪制多個三角形組成的扇形
- GL_QUADS:繪制四邊形
- GL_QUAD_STRIP:繪制連續四邊形
詳見下圖所示,
三.OpenGL基礎知識
在深入學習OpenGL之前,我們有必要了解一些最常用的圖形學名詞、OpenGL原理和語法,
1.OpenGL語法
OpenGL程式的基本結構通常包括——初始化物體渲染所對應的狀態、設定需要渲染的物體,渲染(render)表示計算機從模型創建最終影像的程序,OpenGL只是其中一種渲染系統,模型(model)或者場景物件是通過幾何圖元,比如點、線和三角形來構建的,而圖元與模型的頂點(vertex)也存在著各種對應的關系,
OpenGL另一個最本質的概念叫著色器,它是圖形硬體設備所執行的一類特色函式,可以將著色器理解為專為圖形處理單元(GPU)編譯的一種小型程式,在OpenGL中,會用到始終不同的著色階段(shader stage),最常用的包括頂點著色器(vertex shader)以及片元著色器,前者用于處理頂點資料,后者用于處理光柵化后的片元資料,所有的OpenGL程式都需要用到這兩類著色器,最終生成的影像包含了螢屏上繪制的所有像素點,像素(pixel)是顯示幕上最小的可見單元,計算機系統將所有的像素保存到幀快取(framebuffer)當中,后者是由圖形硬體設備管理的一塊獨立記憶體區域,可以直接映射到最終的顯示設備上,
正如前面您看到的,OpenGL庫中所有的函式都會以字符“gl”作為前綴,然后是一個或者多個大寫字母開頭的詞組,以此來命令一個完整的函式(例如glBindVertexArray()),OpenGL的所有函式都是這種格式,上面看到的“glut”開頭的函式,它們來自第三方庫OpenGL Utility Toolkit(GLUT),可以用來顯示視窗、管理用戶輸入以及執行其他一些操作,
與函式命名約定類似,OpenGL庫中定義的常量也是GL_COLOR_BUFFER_BIT的形式,常量以GL_作為前綴,并且使用下劃線來分割單詞,這些常量的定義是通過#define來完成的,它們基本可以在OpenGL的頭檔案glcorearb.h和glext.h中找到,
為了能夠方便地在不同的作業系統之間移植OpenGL程式,它還為函式定義了不同的資料型別,例如GLfloat是浮點數型別,此外,比如glVertex*()的函式,它有多種變化形式,如glVertex2d、glVertex2f,在函式名稱的“核心”部分之后,通過后綴的變化來提示函式應當傳入的引數,通常由一個數字和1~2個字母組成,glVertex2f()中的“2”表示需要傳入2個引數,f表示浮點數,
2.老式OpenGL vs 現代OpenGL
(1) 老式OpenGL
在大多數計算機圖形系統中,繪圖的方式是將一些頂點發送給處理管線,管線由一系列功能模塊互相連接而成,最近,OpenGL應用編程介面(API)從固定功能的圖形管線轉換為可編程的圖形管線,
如下圖繪制正方形的代碼,它使用的是老式OpenGL,要為三維圖元(在這個代碼中,是一個GL_QUADS即矩形)指定各個頂點,但隨后每個頂點需要被分別發送到GPU,這是低效的方式, 這種老式編程模式伸縮性不好,如果幾何圖形變得復雜,程式就會很慢,對于螢屏上的頂點和像素如何變換,它只提供了有限的控制,
后續我們將專注于現代的OpenGL,但是網路上也會有老式OpenGL的例子,
(2) 現代OpenGL
現代OpenGL利用一系列的操作,即通過“三維圖形管線”繪制圖形,其基本流程如下圖所示,
簡化三維圖形管線分為6步:
- 三維幾何圖形定義(VBO等), 在第一步,通過定義在三維空間中的三角形的頂點,并指定每個頂點相關聯的顏色,我們定義了三維幾何圖形,
- 頂點著色器, 接下來,變換這些頂點:第一次變換將這些頂點放在三維空間中,第二次變換將三維坐標投影到二維空間,根據照明等因素,對應頂點的顏色值也在這一步中計算,這在代碼中通常稱為“頂點著色器”,
- 光柵化, 將幾何圖形“光柵化”(從幾何物體轉換為像素),
- 片段著色器, 針對每個像素,執行另一個名為“片段著色器”的代碼塊,正如頂點著色器作用于三維頂點,片段著色器作用于光柵化后的二維像素,
- 幀緩沖區操作(深度測驗、混合等), 最后,像素經過一系列幀緩沖區操作,其中,它經過“深度緩沖區檢驗”(檢查一個片段是否遮擋另一個)、“混合”(用透明度混合兩個片段)以及其他操作,其當前的顏色與幀緩沖區中該位置已有的顏色結合,
- 幀緩沖區, 這些變化最終體現在最后的幀緩沖區上,通常顯示在螢屏上,
PS:該部分參考Mahesh Venkitachalam大神撰寫的《Python極客專案編程》,代碼可以查看:https://github.com/electronut/pp
3.OpenGL繪制時鐘
最后補充“xiaoge2016老師”的一段趣味代碼,通過OpenGL繪制時鐘,注意它是跳動的,
# -*- coding: utf-8 -*- from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * import math import time h = 0 m = 0 s = 0 # 繪制影像函式 def Draw(): PI = 3.1415926 R = 0.5 TR = R - 0.05 glClear(GL_COLOR_BUFFER_BIT) glLineWidth(5) glBegin(GL_LINE_LOOP) for i in range(100): glVertex2f(R * math.cos(2 * PI / 100 * i), R * math.sin(2 * PI / 100 * i)) glEnd() glLineWidth(2) for i in range(100): glBegin(GL_LINES) glVertex2f(TR * math.sin(2 * PI / 12 * i), TR * math.cos(2 * PI / 12 * i)) glVertex2f(R * math.sin(2 * PI / 12 * i), R * math.cos(2 * PI / 12 * i)) glEnd() glLineWidth(1) h_Length = 0.2 m_Length = 0.3 s_Length = 0.4 count = 60.0 s_Angle = s / count count *= 60 m_Angle = (m * 60 + s) / count count *= 12 h_Angle = (h * 60 * 60 + m * 60 + s) / count glLineWidth(1) glBegin(GL_LINES) glVertex2f(0.0, 0.0) glVertex2f(s_Length * math.sin(2 * PI * s_Angle), s_Length * math.cos(2 * PI * s_Angle)) glEnd() glLineWidth(5) glBegin(GL_LINES) glVertex2f(0.0, 0.0) glVertex2f(h_Length * math.sin(2 * PI * h_Angle), h_Length * math.cos(2 * PI * h_Angle)) glEnd() glLineWidth(3) glBegin(GL_LINES) glVertex2f(0.0, 0.0) glVertex2f(m_Length * math.sin(2 * PI * m_Angle), m_Length * math.cos(2 * PI * m_Angle)) glEnd() glLineWidth(1) glBegin(GL_POLYGON) for i in range(100): glVertex2f(0.03 * math.cos(2 * PI / 100 * i), 0.03 * math.sin(2 * PI / 100 * i)); glEnd() glFlush() # 更新時間函式 def Update(): global h, m, s t = time.localtime(time.time()) h = int(time.strftime('%H', t)) m = int(time.strftime('%M', t)) s = int(time.strftime('%S', t)) glutPostRedisplay() # 主函式 if __name__ == "__main__": glutInit() glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA) glutInitWindowSize(400, 400) glutCreateWindow("My clock") glutDisplayFunc(Draw) glutIdleFunc(Update) glutMainLoop()
其運行結果如下圖所示:
四.總結
本篇文章主要講解Python和OpenGL基礎知識,包括安裝、基礎語法、繪制圖形等,希望對讀者有一定幫助,也希望這些知識點為讀者從事Python影像處理相關專案實踐或科學研究提供一定基礎,
參考文獻:
[1] 《OpenGL編程指南(第8版)》 作者:Dave Shreiner Granham Sellers等,王銳 譯
[2] 《Python極客專案編程》 作者:Mahesh Venkitachalam,王海鵬 譯
[3] 《OpenGL編程精粹》楊柏林 陳根浪 徐靜 編著
[4] 寫給 python 程式員的 OpenGL 教程 - 許老師(天元浪子)
[5] Python之OpenGL筆記(2):現代OpenGL編程常用的幾個通用函式 - 大龍老師
[6] python3+OpenGL環境配置 - GraceSkyer老師
[7] VS2012下基于Glut OpenGL顯示一些立體圖形示例程式 - yearafteryear老師
[8] Python——OpenGL - 白季飛龍老師
[9] python+opengl顯示三維模型小程式 - xiaoge2016
[10] https://github.com/electronut/pp
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/539645.html
標籤:其他
