Pygame 的水波纹效果

37
我已经在谷歌上搜索了它,但是没有现成的脚本可以使用,与Flash上的相同效果不同。我检查了The Water Effect Explained上的算法,并测试了Perlin噪声的实现,它提供了一个很好的模拟平面水波末端的效果。我正在寻找与多个Flash Effects上相同的实现,基于mouseover/hover操作。这是针对一个交互式地板库的,我希望避免Flash,特别是为了避免代码被很容易地反向工程化——是的,我知道它可以只使用一些现成的Flash代码,但我会把它作为最后的选择。
有人看到适用于Pygame(使用OpenGL或不使用)的合适实现吗?
编辑:有人能够使用OpenCV/OpenGL和Pygame提供一个合适的实现吗?
罪魁祸首是(代码)接口,用于传递从外部解释器(追踪器-不是TUIO)通过Python发送的值列表。我已尝试了几天,但Pygame不能像OpenGL中着色器所用的纯C/C++代码那样快速生成任何内容,而我对C/C++的了解为零。因此,目标至少是从Python代码中获得该值列表。
一个很好的例子,不同于Flash效果但仍然很好的是使用Java applet进行水模拟
(赏金提示显示答案缺乏细节,因为这是最接近“OP无法创建所需代码,因为他缺乏基本技能,这个答案可能对几个人有用”的答案)

1
这是你想要的工作方式吗?http://flash-effects.com/tutorial-create-a-water-ripple-mouse-follow/ - mpenkov
1
你可能也想尝试一下 gamedev.stackexchange.com,那里可能会更好运。 - Davy8
6
我感到无聊,于是使用 C++ 和 OpenCV 实现了你第一个链接中的算法(我还没有尝试过 pygame)。链接如下:https://github.com/mpenkov/sandpit/tree/master/stackoverflow。实现并不难,但它看起来不像我上面链接中的 Flash 版本那么好。我仍然不知道原因。 - mpenkov
1
@misha,我在我的Ubuntu虚拟机上成功运行了你编写的代码。太棒了! - planestepper
1
@misha,我认为Pygame比纯C++代码慢。我稍后会尝试将您的实现移植到纯Pygame和Pygame + OpenGL,并在此处发布结果和/或源代码。这应该需要我一到三天的时间。 - planestepper
显示剩余5条评论
2个回答

9
经过做功课(也就是研究)并试图将问题中发布的Java代码参考直接转换为Python后,尝试让Python / Numpy根据它们的位置更新大量像素颜色数组以产生涟漪效果时,我经历了非常非常悲惨的经历(抱歉,我的母语不是英语),因此需要对每次效果计算进行多个(x,y)位置的解析,并将其绘制到屏幕上显示的表面上(surfarray随之而来)。我得出的结论是 - 这得到了其他评论者的支持 - Pygame根本不足以实际遍历整个像素数组并在屏幕上的每个像素上应用计算结果,以获得至少24 fps的最低体验(对于以下平均体验)。

引用Last Light Productions和前Project Geometrian的开发人员Ian Mallet所说:

PyGame对于像素推动不太好。 除了GPU之外没有任何东西可以做到这一点。

搜索结果表明这其实是在寻找 Alkahest,但这个东西似乎永远无法被真正找到。基于相同的涟漪图像理念,此次我使用透明度来透视多层 Pygame 表面,发布了关于 Pygame 圆形裁剪/遮罩 的问题在 Gamedev 上。所选答案 实际上证实了我早已担心的事实,即 Pygame 永远不会足够强大去胜任该任务。
一天后,我回到之前的开发想法,并发现了 Ogre3D。原来(1)Ogre3D 和样例都是开源的,而且(2)其中一个样例就是一个与移动物体交互的 3D 水模型,恰好是我尝试用 2D 实现的东西,但操作更加专业。
由于我对C/C++的了解为零,所以我决定询问如何自定义Ogre3D水效果演示,以便了解从何处开始查找,其中一个答案指向Touchscape提供SDK的软件(请参见this answer)。
Ogre3D基本上已经完成了所有工作。水波纹效果、OpenGL(根据硬件可以选择使用),游戏引擎和通过Python-Ogre的Python包装器 - 所以我的答案是回答我自己的问题,

是否有人能够使用OpenCV/OpenGL和Pygame提供适当的实现该效果?

基本上就是这样。
是的。检查Ogre3D SDK提供的水演示,并通过Python-Ogre将其插入到Python中。

5
以下使用numpy的代码可能会帮助您入门。虽然它已经足够快了,但您可以在Python中实现更快的速度(请查看此处 http://www.scipy.org/PerformancePython)。
顺便提一下,这种方法有几个缺点:
  1. 您无法控制涟漪的速度 - 要做到这一点,您需要修改涟漪函数中使用的方程式(如果您弄清楚它与波动方程式的关系 http://en.wikipedia.org/wiki/Wave_equation,那么您就完成了)
  2. “池塘”的“深度”是固定的(可能太浅)。我添加了一个深度参数以放大效果
  3. 该文章读取整数像素偏移量 - 使用插值值会得到更好的结果(我猜您可以使用OpenGL来完成,但我对那方面的知识为零)
代码:
import numpy

def ripple(w1, w2, damp, n = 1):
    for _ in xrange(n):
        w2 *= -2
        w2[1:-1,1:-1] += w1[0:-2, 1: -1]
        w2[1:-1,1:-1] += w1[2:  , 1: -1]
        w2[1:-1,1:-1] += w1[1:-1, 0: -2]
        w2[1:-1,1:-1] += w1[1:-1, 2:   ]
        w2 *= .5 * (1. - 1./damp)
        w1, w2 = w2, w1

def refract(x, y, w, rindex, depth = 10):
    sx = x[0,1] - x[0,0]
    sy = y[1,0] - y[0,0]

    dw_dx = (w[2: ,1:-1] - w[:-2,1:-1]) / sx * .5
    dw_dy = (w[1:-1,2: ] - w[1:-1,:-2]) / sy * .5

    xang = numpy.arctan(dw_dx)
    xrefract = numpy.arcsin(sin(xang) / rindex)
    dx = numpy.tan(xrefract) * dw_dx * depth

    yang = numpy.arctan(dw_dy)
    yrefract = numpy.arcsin(sin(yang) / rindex)
    dy = numpy.tan(yrefract) * dw_dy * depth

    dx *= numpy.sign(dw_dx)
    dy *= numpy.sign(dw_dy)

    xmin = x[0,0]
    xmax = x[0,-1]
    x[1:-1,1:-1] += dx
    x[:,:] = numpy.where(x < xmin, xmin, x)
    x[:,:] = numpy.where(x > xmax, xmax, x)

    ymin = y[0,0]
    ymax = y[-1,0]
    y[1:-1,1:-1] += dy
    y[:,:] = numpy.where(y < ymin, ymin, y)
    y[:,:] = numpy.where(y > ymax, ymax, y)

x和y应该是从numpy.meshgrid调用返回的网格:以下是示例用法:

    x,y = meshgrid(x,y)
    w = 10 * exp(- (x*x + y*y))
    w1 = w.copy()
    x1,y1 = meshgrid(r_[0:len(x):1.0], r_[0:len(y):1.0])
    ripple(w, w1, 16) # w1 will be modified
    refract(x1, y1, w1, rindex=2, depth=10) # x1 and y1 will be modified
    numpy.around(x1, out=x1) # but you will get better results with interpolate
    numpy.around(y1, out=y1) # 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接