关键词搜索

源码搜索 ×
×

用Python制作一盏 3D 花灯,喜迎元宵佳节

发布2021-02-27浏览960次

详情内容

说起元宵节,各位有没有觉得这是咱们中国人最浪漫的节日呢?国人向来拘谨古板,一年到头都是小心谨慎地过日子,唯有元宵节这天可以纵情豪放一把。东风夜放花千树,宝马雕车香满路,火树银花霓虹闪烁,豪车遍地美女如云。细品,你甚至都能嗅到香奈儿的味道!月上柳梢头,人约黄昏后,这又是何等的浪漫!比起烛光晚宴、鲜花加持,这份浪漫更显纯真。晚至明清,民间元宵节的喜庆气氛,堪比西班牙的奔牛节、巴西的狂欢节、泰国的泼水节。

由于众所周知的原因,估计今年的趵突泉元宵节灯会又要黄了。去哪儿体验“花市灯如昼”的节日气氛呢?Don’t worry,没有什么事能够难倒程序员——用3D技术也可以做出下图这样的走马灯,算是聊胜于无吧。

在这里插入图片描述

1.原材料
花灯纸
如下所示,还可以加上自己喜欢的图案、文字等。

在这里插入图片描述

Python环境和模块
一台安装了Python环境的电脑,Python环境需要安装以下模块。

numpy
pillow
wxgl
如果没有上述模块,请参考下面的命令安装。

pip install numpy
pip install pillow
pip install wxgl

    NumPy和 pillow 是 Python 旗下最常用的科学计算库和图像处理库,属于常用模块。WxGL 是一个基于 PyOpenGL 的三维数据可视化库,以 wx 为显示后端,提供 Matplotlib 风格的交互式应用模式,同时,也可以和 wxPython 无缝结合,在wx的窗体上绘制三维模型。

    2.制作工序
    花灯制作工序非常简单,只需要三十行代码,可以直接在Python IDLE中以交互方式逐行执行。

    导入模块

    >>> import numpy as np
    >>> from PIL import Image
    >>> import wxgl.wxplot as plt
    

      打开花灯纸图像

      >>> fn = r'D:\temp\light0115\res\paper.png'
      >>> im = np.array(Image.open(fn))/255
      >>> im.shape
      (400, 942, 3)
      
        4

      fn定义的是图像存储路径,请据实修改。Image.open(fn)打开文件,返回一个PIL对象,np.array()将PIL对象转成numpy.ndarray数组对象。除以255,将图像数据从0到255的值域范围变成0到1,适应WxGL的接口要求。查看数组的shape,显示图像分辨率为400像素高、942像素宽,每个像素有三种颜色(此处为RGB)。

      根据花灯纸的大小制作龙骨
      纸长942像素,卷成圆筒,半径就是149.9像素,如果把半径视为1个单位,则高度400像素相当于2.668个单位。

      >>> rows, cols, deep = im.shape
      >>> cols/(2*np.pi)
      149.9239563925654
      >>> r = 1
      >>> h = 2*np.pi*rows/cols
      >>> h
      2.6680192387174464
      
        4
      • 5
      • 6
      • 7

      接下来需要制作半径1个单位、高度2.668个单位的圆筒状龙骨了。

      >>> theta = np.linspace(0, 2*np.pi, cols)
      >>> x = r * np.cos(theta)
      >>> y = r * np.sin(theta)
      >>> z = np.linspace(0, h, rows)
      >>> xs = np.tile(x, (rows,1))
      >>> ys = np.tile(y, (rows,1))
      >>> zs = z.repeat(cols).reshape((rows,cols))
      
        4
      • 5
      • 6
      • 7

      这里的xs、ys、zs就是圆筒状龙骨上各个点的x坐标、y坐标、z坐标。下面的代码,每隔10个点抽取1个点,用mesh的方法画出龙骨形状。当然,也可以画出全部的点,那样顶点就会连成一片。

      >>> plt.mesh(xs[::10,::10], ys[::10,::10], zs[::10,::10], mode='FLBL')
      >>> plt.show()
      
      • 1
      • 2

      用3D的方式画出来的龙骨,效果如下。

      在这里插入图片描述

      给龙骨贴上花灯纸
      有了龙骨,接下来就可以把花灯纸贴在龙骨上了。继续操作之python基础教程前,记得先把刚才弹出的3D龙骨窗口关闭。

      >>> plt.mesh(xs, ys, zs, im)
      >>> plt.show()
      
      • 1
      • 2

      不过,你会立刻发现,花灯纸上下方向贴反了。没关系,我们可以像下面这样反转方向。

      >>> plt.mesh(xs, ys, zs, im[::-1])
      >>> plt.show()
      
      • 1
      • 2

      怎么样,是不是有一点走马灯的雏形了呢?

      在这里插入图片描述

      制作旋转叶轮
      走马灯之所以能够转动,是因为里面有蜡烛加热形成上升气流,推动顶部的叶轮旋转,从而带动花灯旋转。当然,这里的叶轮仅仅是个样子,花灯旋转依赖另外的机制实现。

      >>> theta = np.linspace(0, 2*np.pi, 18, endpoint=False)
      >>> x = r * np.cos(theta)
      >>> y = r * np.sin(theta)
      >>> x[2::3] = x[1::3]
      >>> x[1::3] = 0
      >>> y[2::3] = y[1::3]
      >>> y[1::3] = 0
      >>> z = np.ones(18) * h * 0.9
      >>> vs = np.stack((x,y,z), axis=1)
      >>> plt.mesh(xs, ys, zs, im[::-1])
      >>> plt.surface(vs, color='#C03000', method='T', mode='FCBC', alpha=0.8)
      >>> plt.show()
      
        4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      叶轮设计有6片,用三角形模拟,颜色深红,透明度0.8,整体效果略显粗糙了一点。

      在这里插入图片描述

      加上照明灯和提系
      照明灯用一个白色的圆球表示,提系则是红色的一条直线,兼做照明灯的电源线。

      >>> plt.mesh(xs, ys, zs, im[::-1])
      >>> plt.surface(vs, color='#C03000', method='T', mode='FCBC', alpha=0.8)
      >>> plt.sphere((0,0,h*0.4), 0.4, '#FFFFFF', slices=60, mode='FCBC')
      >>> plt.plot((0,0), (0,0), (0.4*h, 1.5*h), width=3.0, style='solid', cmap='hsv', caxis='z')
      
        4

      让花灯转起来
      花灯旋转的实现非常简单,只需要给show方法一个rotation参数就c#教程可以。

      plt.show(rotation=‘h-’)
      最终的花灯效果如下。

      在这里插入图片描述

      3.完整代码

      # -*- coding: utf-8 -*-
      
      import numpy as np
      from PIL import Image
      import wxgl.wxplot as plt
      
      im = np.array(Image.open('res/paper.png'))/255
      rows, cols, deep = im.shape
      
      r, h = 1, 2*np.pi*rows/cols
      theta = np.linspace(0, 2*np.pi, cols)
      x = r*np.cos(theta)
      y = r*np.sin(theta)
      z = np.linspace(0, h, rows)
      xs = np.tile(x, (rows,1))
      ys = np.tile(y, (rows,1))
      zs = z.repeat(cols).reshape((rows,cols))
      
      theta = np.linspace(0, 2*np.pi, 18, endpoint=False)
      x = r*np.cos(theta)
      y = r*np.sin(theta)
      x[2::3] = x[1::3]
      x[1::3] = 0
      y[2::3] = y[1::3]
      y[1::3] = 0
      z = np.ones(18) * h * 0.9
      vs = np.stack((x,y,z), axis=1)
      
      plt.mesh(xs, ys, zs, im[::-1])
      plt.surface(vs, color='#C03000', method='T', mode='FCBC', alpha=0.8)
      plt.sphere((0,0,h*0.4), 0.4, '#FFFFFF', slices=60, mode='FCBC')
      plt.plot((0,0), (0,0), (0.4*h, 1.5*h), width=3.0, style='solid', cmap='hsv', caxis='z')
      plt.show(rotation='h-')
      
        4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载