元宵节就要到了,手把手教你用Python打造一款3D花灯

Karen110
• 阅读 1403

1 前言

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

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

元宵节就要到了,手把手教你用Python打造一款3D花灯

2 原材料

2.1 花灯纸

如下所示,还可以加上自己喜欢的图案、文字等。

元宵节就要到了,手把手教你用Python打造一款3D花灯

2.2 Python环境和模块

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

  • numpy

  • pillow

  • wxgl

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

pip install numpy  
pip install pillow  
pip install wxgl  

NumPy和pillow是Python旗下最常用的科学计算库和图像处理库,属于常用模块。WxGL是一个基于PyOpenGL的三维数据可视化库,以wx为显示后端,提供Matplotlib风格的交互式应用模式,同时,也可以和wxPython无缝结合,在wx的窗体上绘制三维模型。关于WxGL的更多信息,请参阅我的另一篇博客《十分钟玩转3D绘图:WxGL完全手册》。

3 制作工序

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

3.1 导入模块

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

3.2 打开花灯纸图像

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

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

3.3 根据花灯纸的大小制作龙骨

纸长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  

接下来需要制作半径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))  

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

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

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

元宵节就要到了,手把手教你用Python打造一款3D花灯

3.4 给龙骨贴上花灯纸

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

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

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

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

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

元宵节就要到了,手把手教你用Python打造一款3D花灯

3.5 制作旋转叶轮

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

>>> 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()  

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

元宵节就要到了,手把手教你用Python打造一款3D花灯

3.6 加上照明灯和提系

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

>>> 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')  

3.7 让花灯转起来

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

plt.show(rotation='h-')  

最终的花灯效果如下。

元宵节就要到了,手把手教你用Python打造一款3D花灯

4 完整源代码

有了上面的解说,完整的源代码就不用注释了。全部代码三十余行,各位可自行扩展,制作出更多的花灯来。

# -*- 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-')  

**-----**------**-----**---**** End **-----**--------**-----**-****

元宵节就要到了,手把手教你用Python打造一款3D花灯

往期精彩文章推荐:

元宵节就要到了,手把手教你用Python打造一款3D花灯

欢迎各位大佬点击链接加入群聊【helloworld开发者社区】:https://jq.qq.com/?_wv=1027&k=mBlk6nzX进群交流IT技术热点。

本文转自 https://mp.weixin.qq.com/s/Xoekhczojym2LzJMlust-w,如有侵权,请联系删除。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
2年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
8个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这