日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

用OpenGL导演一场烟花盛会,迎接即将到来的新年

發(fā)布時(shí)間:2024/1/8 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用OpenGL导演一场烟花盛会,迎接即将到来的新年 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

忙碌了一年,今天終于放假了。原本打算好好休息一下,沒成想只過了半天就覺得有點(diǎn)無聊。看家人和朋友們都在忙年,那我就用OpenGL導(dǎo)演一場(chǎng)煙花盛會(huì),獻(xiàn)給即將到來的新年吧。

一說到OpenGL,很多人都會(huì)覺得復(fù)雜,其實(shí)不然。只要掌握了幾個(gè)基本的概念,借助于工具軟件,任何人都可以很輕松地上手。在制作煙花之前,我先介紹一下WxGL這個(gè)三維數(shù)據(jù)快速可視化工具。

1. 安裝WxGL

WxGL是一個(gè)基于PyOpenGL的三維數(shù)據(jù)可視化庫,以wx為顯示后端,提供Matplotlib風(fēng)格的交互式應(yīng)用方式。WxGL也可以和wxPython無縫結(jié)合,在wx的窗體上繪制三維模型。使用pip命令即可快速安裝WxGL及其所依賴的其他模塊。

pip install wxgl

2. 快速體驗(yàn)

  • 下面這幾行代碼,繪制了一個(gè)中心在坐標(biāo)原點(diǎn)半徑為1的純色圓球。忽略模塊名的話,這些代碼和Matplotlib的風(fēng)格是完全一致的。
>>> import wxgl.wxplot as plt >>> plt.uvsphere((0,0,0), 1, color='cyan') >>> plt.title('快速體驗(yàn):$x^2+y^2=1$') >>> plt.show()

生成一個(gè)地球模型是如此簡單。

>>> plt.uvsphere((0,0,0), 1, texture='res/earth.jpg', xflip=True, yflip=False) >>> plt.show()

讓地球自轉(zhuǎn),更是易如反掌。

>>> plt.uvsphere((0,0,0), 1, texture='res/earth.jpg', xflip=True, yflip=False,transform = lambda tn,gms,tms : ((0, 1, 0, (0.01*tms)%360),) ) >>> plt.show()

勾選“屏幕錄制”,點(diǎn)擊“播放”按鈕,即可保存為gif文件或mp4/avi格式的視頻文件。

這是代碼中用的的地球紋理圖片,可以直接下載使用。

3. 編寫自己的著色器

WxGL不僅提供了線段、散點(diǎn)、曲面、三維等值面等一系列繪圖函數(shù),還支持用戶定制著色器程序,以實(shí)現(xiàn)更復(fù)雜的功能。下面這個(gè)例子,用粒子技術(shù)模擬了煙花升空的過程。

# -*- coding: utf-8 -*-import numpy as np import wxgl import wxgl.wxplot as pltdef rise(n, pos, h, v, a, cycle):"""煙花升空模型n - 粒子數(shù)量pos - 初始位置h - 上升行程v - 初始速度a - 上升加速度cycle - 循環(huán)周期"""vshader_src = """#version 330 corein vec4 a_Position;in vec4 a_Color;in float a_Delay; // 粒子發(fā)射延遲時(shí)間(s)uniform float u_Ts; // 持續(xù)時(shí)間(s)uniform float u_V; // 初始速度uniform float u_A; // 上升加速度uniform mat4 u_MVPMatrix;out vec4 v_Color;out float v_Ts;void main() { float t = u_Ts - a_Delay;if (t < 0) t = 0;float s = u_V * t + 0.5 * u_A * t * t;gl_Position = u_MVPMatrix * vec4(a_Position.x, a_Position.y+s, a_Position.z, a_Position.w); gl_PointSize = 1;v_Color = a_Color;v_Ts = u_Ts;}"""fshader_src = """#version 330 corein vec4 v_Color;uniform float u_Tmax;in float v_Ts;void main() { if(v_Ts > u_Tmax) discard;vec2 temp = gl_PointCoord - vec2(0.5);float f = dot(temp, temp);if(f > 0.25) discard;gl_FragColor = vec4(v_Color.rgb, 1); } """vs = np.array(pos) + (np.random.random((n,3)) - 0.5) * h/100color = np.tile(np.array((1.0,1.0,0.8)), (n,1))delay = np.float32(np.absolute(np.random.randn(n))) / 10tmax = (pow(v*v+2*a*h, 0.5)-v)/a + delay.max()m = wxgl.Model(wxgl.POINTS, vshader_src, fshader_src, sprite=True)m.set_vertex('a_Position', vs)m.set_color('a_Color', color)m.set_argument('a_Delay', delay)m.set_argument('u_Ts', lambda tn,gms,tms:(tms/1000)%cycle)m.set_argument('u_V', v)m.set_argument('u_A', a)m.set_argument('u_Tmax', tmax)m.set_mvp_matrix('u_MVPMatrix') # 設(shè)置模型矩陣、視點(diǎn)矩陣和投影矩陣return mvs = np.array([[-1.5,2,1], [-1.5,0,1], [1.5,2,1], [1.5,0,1], [-1.5,2,-1], [-1.5,0,-1], [1.5,2,-1], [1.5,0,-1]]) vs = vs[[0,1,2,3,0,2,1,3,4,5,6,7,4,6,5,7,0,4,1,5,2,6,3,7]] m = rise(n=500, pos=(0,0,0), h=1.5, v=2, a=-1.2, cycle=5)plt.figure(zoom=0.7, elev=10) plt.line(vs, color=(0,1,1), method='isolate') # 六面體線框,表示煙花燃放的空間 plt.model(m) plt.show()

4. 綻放的煙花

只要理解了煙花升空的代碼,很容易寫出煙花在空中爆炸的著色器程序。下面的代碼除了煙花升空的著色器,還提供了兩種煙花爆炸的著色器,其中用到了一個(gè)紋理圖片,可直接下載下面這張圖使用。


如果將上面的紋理圖片替換成文字,就可以在煙花爆炸的瞬間顯示出文字了。WxGL提供了一個(gè)文本轉(zhuǎn)PIL圖形對(duì)象的函數(shù),可以直接作為紋理使用。

# -*- coding: utf-8 -*-import numpy as np import wxgl import wxgl.wxplot as pltdef rise(n, pos, h, v, a, cycle):"""煙花升空模型n - 粒子數(shù)量pos - 初始位置h - 上升行程v - 初始速度a - 上升加速度cycle - 循環(huán)周期"""vshader_src = """#version 330 corein vec4 a_Position;in vec4 a_Color;in float a_Delay; // 粒子發(fā)射延遲時(shí)間(s)uniform float u_Ts; // 持續(xù)時(shí)間(s)uniform float u_V; // 初始速度uniform float u_A; // 上升加速度uniform mat4 u_MVPMatrix;out vec4 v_Color;out float v_Ts;void main() { float t = u_Ts - a_Delay;if (t < 0) t = 0;float s = u_V * t + 0.5 * u_A * t * t;gl_Position = u_MVPMatrix * vec4(a_Position.x, a_Position.y+s, a_Position.z, a_Position.w); gl_PointSize = 1;v_Color = a_Color;v_Ts = u_Ts;}"""fshader_src = """#version 330 corein vec4 v_Color;uniform float u_Tmax;in float v_Ts;void main() { if(v_Ts > u_Tmax) discard;vec2 temp = gl_PointCoord - vec2(0.5);float f = dot(temp, temp);if(f > 0.25) discard;gl_FragColor = vec4(v_Color.rgb, 1); } """vs = np.array(pos) + (np.random.random((n,3)) - 0.5) * h/100color = np.tile(np.array((1.0,1.0,0.8)), (n,1))delay = np.float32(np.absolute(np.random.randn(n))) / 10tmax = (pow(v*v+2*a*h, 0.5)-v)/a + delay.max()m = wxgl.Model(wxgl.POINTS, vshader_src, fshader_src, sprite=True)m.set_vertex('a_Position', vs)m.set_color('a_Color', color)m.set_argument('a_Delay', delay)m.set_argument('u_Ts', lambda tn,gms,tms:(tms/1000)%cycle)m.set_argument('u_V', v)m.set_argument('u_A', a)m.set_argument('u_Tmax', tmax)m.set_mvp_matrix('u_MVPMatrix') # 設(shè)置模型矩陣、視點(diǎn)矩陣和投影矩陣return m, tmaxdef bomb_1(n, pos, start, a, cycle):"""煙花爆炸模型n - 粒子數(shù)量pos - 位置start - 時(shí)間a - 下降加速度cycle - 循環(huán)周期"""vshader_src = """#version 330 corein vec4 a_Position;in vec3 a_Data;uniform float u_Ts;uniform float u_Start;uniform float u_A;uniform mat4 u_MVPMatrix;out vec4 v_Color;out float v_Ts;void main() { float t = u_Ts - u_Start;if (t < 0) t = 0;float lat = radians((a_Data.x - 0.5) * 90);float lon = radians(a_Data.y * 360);float r = (a_Data.z * 0.3 + 0.7) * 0.3 * t * (1 + 0.3 * a_Position.z);float y = r * sin(lat) + a_Position.y - 0.5*u_A*t*t;float xz = r * cos(lat);float x = xz * cos(lon) + a_Position.x;float z = xz * sin(lon) + a_Position.z;gl_Position = u_MVPMatrix * vec4(x,y,z,a_Position.w); gl_PointSize = 3 * t;v_Ts = t;int i = gl_VertexID % 6;if (i == 0) v_Color = vec4(1,0,0,1);else if (i == 1) v_Color = vec4(0,1,0,1);else if (i == 2) v_Color = vec4(0,0,1,1);else if (i == 3) v_Color = vec4(1,1,0,1);else if (i == 4) v_Color = vec4(0,1,1,1);else v_Color = vec4(1,0,1,1);}"""fshader_src = """#version 330 corein vec4 v_Color;in float v_Ts;void main() { if(v_Ts <= 0 || v_Ts > 2) discard;vec2 temp = gl_PointCoord - vec2(0.5);float f = dot(temp, temp);if(f > 0.25) discard;//float alpha = v_Color.a * exp(1-30*f) * (4-v_Ts*v_Ts)/2;float alpha = v_Color.a * (1-4*f) * (4-v_Ts*v_Ts)/2;gl_FragColor = vec4(v_Color.rgb, alpha); } """vs = np.tile(np.array(pos), (n,1))data = np.float32(np.random.random((n,3)))m = wxgl.Model(wxgl.POINTS, vshader_src, fshader_src, sprite=True, opacity=False)m.set_vertex('a_Position', vs)m.set_argument('a_Data', data)m.set_argument('u_Start', start)m.set_argument('u_A', a)m.set_argument('u_Ts', lambda tn,gms,tms:(tms/1000)%cycle)m.set_mvp_matrix('u_MVPMatrix') # 設(shè)置模型矩陣、視點(diǎn)矩陣和投影矩陣return mdef bomb_2(pos, start, texture, a, size, cycle):"""煙花爆炸模型pos - 位置start - 時(shí)間texture - 紋理a - 下降加速度cycle - 循環(huán)周期"""vshader_src = """#version 330 corein vec4 a_Position;uniform float u_Ts;uniform float u_Start;uniform float u_A;uniform float u_Size;uniform mat4 u_MVPMatrix;out float v_Ts;void main() { float t = u_Ts - u_Start;if (t < 0) t = 0;if (t < 2) gl_PointSize = t * u_Size/2 * (1 + 0.3 * a_Position.z);else gl_PointSize = u_Size * (1 + 0.3 * a_Position.z);gl_Position = u_MVPMatrix * vec4(a_Position.x, a_Position.y-0.5*u_A*t*t, a_Position.z, a_Position.w); v_Ts = t;}"""fshader_src = """#version 330 coreuniform sampler2D u_Fireworks;in float v_Ts;void main() { if(v_Ts <= 0 || v_Ts > 2) discard;vec4 color = texture2D(u_Fireworks, gl_PointCoord);gl_FragColor = vec4(color.rgb, color.a*(4-v_Ts*v_Ts)/2);} """vs = np.array(pos).reshape(-1,3)m = wxgl.Model(wxgl.POINTS, vshader_src, fshader_src, sprite=True)m.set_vertex('a_Position', vs)m.set_argument('u_A', a)m.set_argument('u_Size',size)m.set_argument('u_Start', start)m.set_argument('u_Ts', lambda tn,gms,tms:(tms/1000)%cycle)m.add_texture('u_Fireworks', texture, wxgl.TEXTURE_2D, yflip=False)m.set_mvp_matrix('u_MVPMatrix') # 設(shè)置模型矩陣、視點(diǎn)矩陣和投影矩陣return mif __name__ == '__main__':vs = np.array([[-1.5,2,1], [-1.5,0,1], [1.5,2,1], [1.5,0,1], [-1.5,2,-1], [-1.5,0,-1], [1.5,2,-1], [1.5,0,-1]])vs = vs[[0,1,2,3,0,2,1,3,4,5,6,7,4,6,5,7,0,4,1,5,2,6,3,7]]plt.figure(zoom=0.5, elev=10)plt.line(vs, color=(0,1,1,0), method='isolate') # 六面體線框,表示煙花燃放的空間# ------------------------------h, v, a, cycle = 1.7, 2.2, -1.2, 4for i, ch in enumerate('新春快樂'):x = -1.5 + im1, start = rise(n=300, pos=(x,0,1), h=h, v=v, a=a, cycle=cycle)m2 = bomb_1(200, (x,h,1), start, a=0.1, cycle=cycle)m3 = bomb_2((x,h,1), start, wxgl.text2image(ch, 96, (1,0,0)), a=0.1, size=100, cycle=cycle)plt.model(m1)plt.model(m2)plt.model(m3)# -------------------------------for i in range(20):x, z = (np.random.random()-0.5)*4, (np.random.random()-0.5)*2h, v, a = 1.5+(np.random.random()-0.5)*0.4, 2.2, -1.2cycle = np.random.randint(4, 7)m1, start = rise(n=300, pos=(x,0,z), h=h, v=v, a=a, cycle=cycle)m2 = bomb_1(200, (x,h,z), start, a=0.1, cycle=cycle)plt.model(m1)plt.model(m2)# -------------------------------for i in range(20):x, z = (np.random.random()-0.5)*4, (np.random.random()-0.5)*2h, v, a = 1.5+(np.random.random()-0.5)*0.4, 2.3, -1.2cycle = np.random.randint(4, 7)m1, start = rise(n=300, pos=(x,0,z), h=h, v=v, a=a, cycle=cycle)m2 = bomb_2((x,h,z), start, 'res/fw.png', a=0.1, size=300, cycle=cycle)plt.model(m1)plt.model(m2)plt.show()

最終的效果如下面的gif所示。

總結(jié)

以上是生活随笔為你收集整理的用OpenGL导演一场烟花盛会,迎接即将到来的新年的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。