首頁 > 軟體

Python+OpenGL製作一個元宵花燈

2022-02-15 10:00:03

又是一年元宵節,作為程式設計師的你,打算怎麼過呢?如果昨天情人節的紅包發得手軟又心疼,不妨靜下心來,瞭解一下三維資料視覺化,順便做一盞花燈送給女朋友,也許比紅包更能討她歡心呢。

1.準備

三維資料快速視覺化工具,我喜歡用WxGL。這是一個基於PyOpenGL的三維資料視覺化庫,提供類似Matplotlib風格的3D繪圖函數。如果熟悉NumPy和Matplotlib的話,只需要幾分鐘時間就可以學會使用WxGL的互動式繪圖。

WxGL模組使用pip命令安裝。

pip install wxgl

安裝完成後,可以在Python IDLE中檢視版本資訊。

>>> import wxgl
>>> wxgl.version
'0.8.5'

2.快速體驗

元宵、花燈和月亮,是元宵節的三大主題元素。我們就以一個文藝範兒的月亮開啟WxGL的體驗之旅吧。

import wxgl.wxplot as plt

plt.title('江天一色無纖塵,皎皎空中孤月輪')
plt.uvsphere((0,0,0), 1, lon=(0,360), lat=(90,-90), texture='res/moon.jpg', light=None)
plt.text('眾裡尋他千百度,', pos=(1.2,-0.3,0), size=128)
plt.text('驀然回首,', pos=(1.2,-0.5,0), size=128)
plt.text('那人卻在燈火闌珊處。', pos=(1.2,-0.7,0), size=128)
plt.show()

這幾行程式碼,使用uvsphere球面函數繪製了一箇中心點在三維座標系原點、半徑為1的月亮。忽略模組名的話,這些程式碼和Matplotlib的風格幾乎是完全一致的,甚至函數名都是相同的。

執行show函數會彈出一個視窗,顯示繪製的模型。視窗底部提供了一組工具按鈕,可以設定系統引數、切換畫布風格、顯示或隱藏座標網格、播放動畫、儲存或錄製螢幕等。和Matplotlib一樣,該視窗將阻塞程式執行,直至關閉該視窗。

3.模型動畫

通過transform引數傳遞一個以時間長度為引數的函數給uvsphere球面函數,就可以讓上面的月亮轉動起來。

import wxgl.wxplot as plt

plt.title('江天一色無纖塵,皎皎空中孤月輪')
plt.uvsphere((0,0,0), 1, 
    lon = (0,360), 
    lat = (90,-90), 
    texture = 'res/moon.jpg', 
    transform = lambda duration : ((0, 1, 0, (0.02*duration)%360),),
    light = None # 關閉燈光效果,環境光會自動增強
)
plt.text('眾裡尋他千百度,', pos=(1.2,-0.3,0), size=128)
plt.text('驀然回首,', pos=(1.2,-0.5,0), size=128)
plt.text('那人卻在燈火闌珊處。', pos=(1.2,-0.7,0), size=128)
plt.show()

程式碼中lambda函數——當然也可以是普通的函數,其引數duration是以毫秒為單位的時間長度。該函數返回月球圍繞一個向量(此處為(0,1,0),即y軸)旋轉的角度。點選播放按鈕,月球即開始以20°/s的速度旋轉。

對了,差點兒忘記提供月球的紋理圖片了。點選此處可下載不帶水印的月球紋理圖片。

4.子圖佈局

在一張畫布上可以任意放置多個子圖。下面的程式碼演示了子圖佈局函數subplot的經典用法。實際上,這個函數比Matplotlib的同名函數更靈活和便捷。

import wxgl.wxplot as plt

plt.subplot(121)
plt.title('經緯度網格生成球體')
plt.uvsphere((0,0,0), 1, color='coral', fill=False, slices=15)
plt.subplot(122)
plt.title('正八面體迭代細分生成球體')
plt.isosphere((0,0,0), 1, color='cyan', fill=False, iterations=2)
plt.show()

在畫布上建立兩個子圖,使用兩種不同的方式繪製球,並設定填充模式。由於使用相同的視點系統,兩個子圖上的模型可以保持同步。

5.顏色對映

對於資料快速視覺化工具來說,顏色對映是必不可少的。下面的程式碼演示了ColorBar的用法。程式碼中的jet、Paired、rainbow等顏色對映表繼承自Matplotlib庫。

import numpy as np
import wxgl.wxplot as plt

vs = np.random.random((300, 3))*2-1
color = np.random.random(300)
size = np.random.randint(3, 15, size=300)
plt.scatter(vs, color, 'jet', size=size)
plt.colorbar('jet', [-1, 1], loc='right')
plt.colorbar('Paired', [-5, 5], loc='bottom', subject='溫度')
plt.colorbar('rainbow', [0, 77], loc='bottom', subject='速度')
plt.title('scatter函數和colorbar函數範例')
plt.show()

WxGL允許在一張圖上使用兩個垂直風格的ColorBar和三個水平風格的ColorBar。

6.走馬燈

去年元宵節我寫過一篇繪製3D花燈的部落格,用的工具也是WxGL,當時的版本還是0.6.4。牛去虎來,整整一年過去了,WxGL終於艱難地升級到了0.8.5,那篇部落格中的程式碼也必須要升級了。

import numpy as np
import wxgl.wxplot as plt

r = 1 # 花燈半徑為1
tf_bull = lambda duration : ((0, 1, 0, (0.02*duration)%360),) # 模型動畫函數

# 以下生成花燈筒狀龍骨
theta = np.linspace(0, 2*np.pi, 361) # 在0°~360°範圍內間隔1°均勻生成361個角度
xs = r * np.tile(np.cos(theta), (150,1)) # 半徑為r的圓周上361點的x座標,重複150次,得到150行361列的二維陣列
zs = r * np.tile(-np.sin(theta), (150,1)) # 半徑為r的圓周上361點的z座標,重複150次,得到150行361列的二維陣列
ys = np.repeat(np.linspace(2.7, 0, 150), 361).reshape(150,361) # 0~2.7範圍內均勻生成150個點,每個重複361,得到150行361列的二維陣列

# 以下生成花燈葉輪
theta = np.linspace(0, 2*np.pi, 18, endpoint=False)
x, z = r * np.cos(theta), r * np.sin(theta)
y = np.ones(18) * 2.5
x[2::3] = x[1::3]
x[1::3] = 0
z[2::3] = z[1::3]
z[1::3] = 0
vs = np.stack((x,y,z), axis=1)

# 公牛動畫函數:順時針旋轉,20°/s,向左平移1.2
tf_bull = lambda duration : ((0, 1, 0, (-0.02*duration)%360), (-1.2,0,0)) 

# 老虎動畫函數:逆時針旋轉,20°/s,向右平移1.2
tf_tiger = lambda duration : ((0, 1, 0, (0.02*duration)%360), (1.2,0,0)) 


plt.figure(elev=20) # 設定相機高度角為20°

# 公牛花燈
plt.mesh(xs, ys, zs, texture='res/bull.jpg', transform=tf_bull, light=None) # 花燈筒
plt.surface(vs, color=(0.75,0.2,0,0.8), transform=tf_bull) # 花燈葉輪
plt.uvsphere((0,0.8,0), 0.4, color='#FFFFFF', transform=((-1.2,0,0),), light=None) # 燈
plt.line([[0,1.2,0],[0,3.5,0]], color='red', width=3.0, transform=((-1.2,0,0),), inside=False) # 線

# 老虎花燈
plt.mesh(xs, ys, zs, texture='res/tiger.jpg', transform=tf_tiger, light=None) # 花燈筒
plt.surface(vs, color=(0.75,0.2,0,0.8), transform=tf_tiger) # 花燈葉輪
plt.uvsphere((0,0.8,0), 0.4, color='#FFFFFF', transform=((1.2,0,0),), light=None) # 燈
plt.line([[0,1.2,0],[0,3.5,0]], color='red', width=3.0, transform=((1.2,0,0),), inside=False) # 線

plt.show()

兩隻花燈使用相同的尺寸,畫在同一個位置。花燈筒和葉輪的模型動畫函數除了旋轉還分別向左右移動了1.2個長度單位,而燈和線則只移動不旋轉。最終效果如下圖所示。

程式碼中用的公牛和老虎的花燈紋理,請點選下載:公牛老虎

到此這篇關於Python+OpenGL製作一個元宵花燈的文章就介紹到這了,更多相關Python OpenGL元宵花燈內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com