说话间,一年一度的中秋节又来到了跟前儿。除了品尝月饼,中秋节最重要的活动大约就是赏月了。一轮高悬月,万念俱清新。古往今来,多少文人墨客借月抒发胸臆,留下了无数吟咏明月的诗词歌赋。现如今,写诗成了贾浅浅们的专享,普通人更喜欢在中秋月圆之夜拍月亮发微博或微信朋友圈与亲朋好友互祝平安。
用手机拍月亮,大概每个人都尝试过吧?效果肯定是令人失望的。当初华为P30 Pro手机号称自带“月亮拍摄模式”,最终也被认为“拍出了不存在的细节,月亮是P上去的”。倘若使用相机拍摄的话,需要配备至少200mm以上的长焦镜头,比如尼康的COOLPIX-P1000相机,拥有3000mm的长焦镜头。除了专业的装备之外,还得选择合适的地点、合适的时机,另外还需要一点点运气。
不过,对于程序员来说,想要拍出大片的感觉根本需要这么麻烦,甚至,连手机或者相机都不需要。不信?请跟我来一起体验一下吧。
这是一张全月地貌图,古今中外的人们看到的月亮都是这样的,每个摄影师拍到的月亮也是如此。请下载并保存为moon.jpg,后面的代码会用到它。
如果已经安装了wxgl模块的话(没有的话,请先使用pip install wxgl安装),只用4行就可以把全月地貌图变成了一个三维的月球,可以旋转缩放的那种。uvsphere函数使用经纬度网格生成球,其第1个参数为月球的球心坐标,第2个参数为月球的半径(此处为1),刚才保存的图片成了纹理参数。
import wxgl
import wxgl.glplot as glt
glt.uvsphere((0,0,0), 1, texture=wxgl.Texture('images/moon.jpg'))
glt.show()
细心的同学会发现,这个月球有问题:初始位置不是月球的正面。没关系,uvsphere函数可以通过ur和vr两个关键字参数设置纹理坐标。下面的代码将水平纹理坐标从默认的(0,1)改为(-0.25,0.75),月球就可以正面朝向我们了。
import wxgl
import wxgl.glplot as glt
texture_moon = wxgl.Texture('images/moon.jpg')
glt.uvsphere((0,0,0), 1, texture=texture_moon, ur=(-0.25,0.75))
glt.show()
上面的月球默认阳关是从画面的右前方照射过来的,通过重新设置灯光,可以让阳关从正面照射月球。请注意,wxgl坐标系的x轴指向屏幕右方,y轴指向屏幕上方,z轴指向屏幕里面。
import wxgl
import wxgl.glplot as glt
texture_moon = wxgl.Texture('images/moon.jpg')
light_sun = wxgl.SunLight(direction=(0,0,-1))
glt.uvsphere((0,0,0), 1, texture=texture_moon, ur=(-0.25,0.75), light=light_sun)
glt.show()
现在的月球看起来差不多了。接下来,需要为它找一个合适的背景,就以下面这张夜景图片为例吧,请将其下载并命名为bg_1.jpg,后面的代码会用到它。
下面这段代码使用quad函数将背景图片放在了月球的后面,看起来像是三维的,但拖拽旋转的话,很容易露出马脚,因此代码中限制了高度角和方位角。如果使用天空盒技术的话,效果会好一些,但对背景图片要求太高,不容易找到合适的图片。
import wxgl
import wxgl.glplot as glt
from PIL import Image
file_bg = 'images/bg_1.jpg'
file_moon = 'images/moon.jpg'
k = 8 # 背景图片高度与月球半径之比,值约大,月球看起来越小
x, y = 6, 4 # 月球在背景中的位置坐标,中心为背景坐标原点
w, h = Image.open(file_bg).size # 获取背景图片宽和高
d = k * w/h # 背景图片宽度
vs = [[-d,k,-1.5], [-d,-k,-1.5], [d,-k,-1.5], [d,k,-1.5]] # # 背景图片位置
light_base = wxgl.BaseLight()
light_sun = wxgl.SunLight(direction=(0,0,-1))
texture_moon = wxgl.Texture(file_moon)
texture_bg = wxgl.Texture(file_bg, yflip=True)
texcoord_bg = ((0,1), (0,0), (1,0), (1,1))
glt.figure(zoom=0.6, elev_range=(0,0), azim_range=(-10,10))
glt.quad(vs, texture=texture_bg, texcoord=texcoord_bg, light=light_base)
glt.uvsphere((x,y,0), 1, texture=texture_moon, ur=(-0.25,0.75), light=light_sun)
glt.show()
改变代码中k值,可以调整月球的大小;改变x和y,可以调整月球在图中的位置。效果如下图所示。
读懂了这段代码,就可以随意“拍摄”月亮了:想在哪儿拍就在哪儿拍,想拍多大就拍多大。
增加一个模型几何变换函数,还可以让月亮缓缓地升高转动。
import wxgl
import wxgl.glplot as glt
from PIL import Image
file_bg = 'images/bg_2.jpg'
file_moon = 'images/moon.jpg'
k = 10 # 背景图片高度与月球半径之比,值约大,月球看起来越小
x, y = -7, 0 # 月球在背景中的位置坐标,中心为背景坐标原点
w, h = Image.open(file_bg).size # 获取背景图片宽和高
d = k * w/h # 背景图片宽度
vs = [[-d,k,-1.5], [-d,-k,-1.5], [d,-k,-1.5], [d,k,-1.5]] # # 背景图片位置
def tf(t):
"""月球移动函数"""
global x, y
t %= 10000
dx, dy = t/1000, t/3000
x += dx
y += dy
return ((dx, dy, 0), )
light_base = wxgl.BaseLight()
light_sun = wxgl.SunLight(direction=(0,0,-1))
texture_moon = wxgl.Texture(file_moon)
texture_bg = wxgl.Texture(file_bg, yflip=True)
texcoord_bg = ((0,1), (0,0), (1,0), (1,1))
glt.figure(zoom=0.6, elev_range=(0,0), azim_range=(-10,10))
glt.quad(vs, texture=texture_bg, texcoord=texcoord_bg, light=light_base)
glt.uvsphere((x,y,0), 1, texture=texture_moon, ur=(-0.25,0.75), light=light_sun, transform=tf)
glt.show()
由于CSDN限定上传图片文件大小不能超过5M,gif只能做成这样了。
掌握了以上要点,这个中秋节就可以在微博和朋友圈随意秀出月亮大片了。