说明:
本文算法部分
整理自 GameRes
上的资料
,原作者 Imagic
。我只是在学习 Android
的过程中,想到这个特效,然后就在Android
上实现出来,并在源算法的基础上添加了雨滴滴落特效,以及划过水面时的涟漪特效。 该程序在模拟器和真机上运行速度都较慢,需要进一步优化或使用 JNI
实现,如果你想到好的优化算法,请联系我:kesalin@gmail.com
。
示例程序下载:http://www.cppblog.com/Files/kesalin/RippleDemo.zip
基础知识:
在讲解代码之前,我们来回顾一下在高中的物理课上我们所学的关于水波的知识。水波有扩散,衰减,折射,反射,衍射等几个特性:
扩散
:
当你投一块石头到水中,你会看到一个以石头入水点为圆心所形成的一圈圈的水波,这里,你可能会被这个现象所误导,以为水波上的每一点都是以石头入水点为中
心向外扩散的,这是错误的。实际上,水波上的任何一点在任何时候都是以自己为圆心向四周扩散的,之所以会形成一个环状的水波,是因为水波的内部因为扩散的
对称而相互抵消了。
衰减
:因为水是有阻尼的,否则,当你在水池中投入石头,水波就会永不停止的震荡下去。
折射
:因为水波上不同地点的倾斜角度不同,所以我们从观察点垂直往下看到的水底并不是在观察点的正下方,而有一定的偏移。如果不考虑水面上部的光线反射,这就是我们能感觉到水波形状的原因。
反射
:水波遇到障碍物会反射。
衍射
:在水池中央放上一块礁石,或放一个中间有缝的隔板,那么就能看到水波的衍射现象了。
算法推导:
好了,有了这几个特性,再运用数学和几何知识,我们就可以模拟出真实的水波了。但是,如果你曾用
3DMax
做过水波的动画,你就会知道要渲染出一幅真实形状的水波画面少说也得好几十秒,而我们现在需要的是实时的渲染,每秒种至少也得渲染
20
帧才能使得水波得以平滑的显示。考虑到电脑运算的速度,我们不可能按照正弦函数或精确的公式来构造水波,不能用乘除法,更不能用
sin
、
cos
等三角函数,只能用一种取近似值的快速算法,尽管这种算法存在一定误差,但是为了满足实时动画的要求,我们不得不这样做。
首先我们要建立两个与水池图象一样大小的数组
buf1[PoolWidth * PoolHeight]
和
buf2[PoolWidth * PoolHeight]
(
PoolWidth
为水池图象的象素宽度、
PoolHeight
为水池图象的象素高度),用来保存水面上每一个点的前一时刻和后一时刻波幅数据,因为波幅也就代表了波的能量,所以在后面我们称这两个数组为波能缓冲区。水面在初始状态时是一个平面,各点的波幅都为
0
,所以,这两个数组的初始值都等于
0
。
下面来推导计算波幅的公式
我们假设存在这样一个一次公式,可以在任意时刻根据某一个点周围前、后、左、右四个点以及该点自身的振幅来推算出下一时刻该点的振幅,那么,我们就有可能用归纳法求出任意时刻这个水面上任意一点的振幅。如左图,你可以看到,某一时刻,
X0
点的振幅除了受
X0
点自身振幅的影响外,同时受来自它周围前、后、左、右四个点(
X1
、
X2
、
X3
、
X4
)的影响(为了简化,我们忽略了其它所有点),而且,这四个点对
X0
点的影响力可以说是机会均等的。那么我们可以假设这个一次公式为:
X0’
= a * (X1 + X2 + X3 + X4) + b * X0
(
公式
1)
|
a, b
为待定系数,
X0’
为
X0
点下一时刻的振幅,
X0
、
X1
、
X2
、
X3
、
X4
为当前时刻的振幅
下面我们来求解
a
和
b
。
假设水的阻尼为
0
。在这种理想条件下,水的总势能将保持不变,水波永远波动。也就是说在任何时刻,所有点的振幅的和保持不变。那么可以得到下面这个公式:
X0’ + X1’ + ... + Xn’ = X0 + X1 + ... + Xn
|
将每一个点用公式
1
替代,代入上式,得到:
(4a + b) * X0 + (4a + b) * X1 + ... (4a + b) * Xn = X0 + X1 + ... + Xn
=
>
4a
+ b = 1
|
找出一个最简解:
a = 1/2
、
b = -1
。
因为
1/2
可以用移位运算符
“>>”
来进行,不用进行乘除法,所以,这组解是最适用的而且是最快的。那么最后得到的公式就是:
X0’=
(
X1 + X2 + X3 + X4
)
/ 2 - X0
|
好了,有了上面这个近似公式,你就可以推广到下面这个一般结论:已知某一时刻水面上任意一点的波幅,那么,在下一时刻,任意一点的波幅就等于与该点紧邻的前、后、左、右四点的波幅的和除以
2
、再减去该点的波幅。
应该注意到,水在实际中是存在阻尼的,否则,用上面这个公式,一旦你在水中增加一个波源,水面将永不停止的震荡下去。所以,还需要对波幅数据进行衰减处理,让每一个点在经过一次计算后,波幅都比理想值按一定的比例降低。这个衰减率经过测试,用
1/32
比较合适,也就是
1/2^5
。可以通过移位运算很快的获得。
到这里,水波特效算法中最艰难的部分已经明了,下面是
Android
源程序中计算波幅数据的代码。
//
某点下一时刻的波幅算法为:上下左右四点的波幅和的一半减去当前波幅,即
//
X0' =
(
X1 + X2 + X3 + X4
)
/ 2 - X0
//
+----x3----+
//
+
|
+
//
+
|
+
//
x1---x0----x2
//
+
|
+
//
+
|
+
//
+----x4----+
//
void
rippleSpread()
{
int
pixels =
m_width
* (
m_height
- 1);
for
(
int
i =
m_width
; i < pixels; ++i) {
//
波能扩散
:
上下左右四点的波幅和的一半减去当前波幅
// X0' =
(
X1 + X2 + X3 + X4
)
/ 2 - X0
//
m_buf2
[i] =
(
short
)(((
m_buf1
[i - 1] +
m_buf1
[i + 1]+
m_buf1
[i -
m_width
] +
m_buf1
[i +
m_width
]) >> 1)
-
m_buf2
[i]);
//
波能衰减
1/32
//
m_buf2
[i] -=
m_buf2
[i] >> 5;
}
//
交换波能数据缓冲区
short
[] temp =
m_buf1
;
m_buf1
=
m_buf2
;
m_buf2
= temp;
}
|
渲染:
然后我们可以根据算出的波幅数据对页面进行渲染。
因
为水的折射,当水面不与我们的视线相垂直的时候,我们所看到的水下的景物并不是在观察点的正下方,而存在一定的偏移。偏移的程度与水波的斜率,水的折射率
和水的深度都有关系,如果要进行精确的计算的话,显然是很不现实的。同样,我们只需要做线性的近似处理就行了。因为水面越倾斜,所看到的水下景物偏移量就
越大,所以,我们可以近似的用水面上某点的前后、左右两点的波幅之差来代表所看到水底景物的偏移量。
在程序中,用一个页面装载原始的图像,用另外一个页面来进行渲染。先取得指向两个页面内存区的指针
src
和
dst
,然后用根据偏移量将原始图像上的每一个象素复制到渲染页面上。进行页面渲染的代码如下:
void
rippleRender()
{
int
offset;
int
i =
m_width
;
int
length =
m_width
*
m_height
;
for
(
int
y = 1; y <
m_height
- 1; ++y) {
for
(
int
x = 0; x <
m_width
; ++x, ++i) {
//
计算出偏移象素和原始象素的内存地址偏移量
:
//offset = width * yoffset
+ xoffset
offset
= (
m_width
* (
m_buf1
[i -
m_width
] -
m_buf1
[i +
m_width
])) + (
m_buf1
[i - 1] -
m_buf1
[i + 1]);
//
判断坐标是否在范围内
if
(i + offset >
0 && i + offset < length) {
m_bitmap2
[i] =
m_bitmap1
[i + offset];
}
else
{
m_bitmap2
[i] =
m_bitmap1
[i];
}
}
}
}
|
增加波源:
俗话说:无风不起浪,为了形成水波,我们必须在水池中加入波源,你可以想象成向水中投入石头,形成的波源的大小和能量与石头的半径和你扔石头的力量都有关系。知道了这些,那么好,我们只要修改波能数据缓冲区
buf
,让它在石头入水的地点来一个负的
“
尖脉冲
”
,即让
buf[x,y] = -n
。经过实验,
n
的范围在(
32 ~ 128
)之间比较合适。
控制波源半径也好办,你只要以石头入水中心点为圆心,画一个以石头半径为半径的圆,让这个圆中所有的点都来这么一个负的
“
尖脉冲
”
就可以了(这里也做了近似处理)。
增加波源的代码如下:
// stoneSize
:
波源半径
// stoneWeight
:
波源能量
//
void
dropStone(
int
x,
int
y,
int
stoneSize,
int
stoneWeight)
{
//
判断坐标是否在范围内
if
((x + stoneSize) >
m_width
|| (y +
stoneSize) >
m_height
||
(x - stoneSize) < 0 || (y - stoneSize) < 0) {
return
;
}
int
value = stoneSize * stoneSize;
short
weight = (
short
)-stoneWeight;
for
(
int
posx = x - stoneSize; posx < x +
stoneSize; ++posx)
{
for
(
int
posy = y - stoneSize; posy < y +
stoneSize; ++posy)
{
if
((posx - x) * (posx - x) + (posy - y) *
(posy - y)
< value)
{
m_buf1
[
m_width
* posy + posx] = weight;
}
}
}
}
|
如果我们想要模拟在水面划过时引起的涟漪效果,那么我们还需要增加新的算法函数
breasenhamDrop
。
void
dropStoneLine(
int
x,
int
y,
int
stoneSize,
int
stoneWeight) {
//
判断坐标是否在屏幕范围内
if
((x + stoneSize) >
m_width
|| (y +
stoneSize) >
m_height
|| (x - stoneSize) < 0 || (y -
stoneSize) < 0) {
return
;
}
for
(
int
posx = x - stoneSize; posx < x +
stoneSize; ++posx)
{
for
(
int
posy = y - stoneSize; posy < y +
stoneSize; ++posy)
{
m_buf1
[
m_width
* posy + posx] = -40;
}
}
}
// xs
, ys
:
起始点,
xe
, ye
:
终止点
// size :
波源半径,
weight :
波源能量
void
breasenhamDrop
(
int
xs,
int
ys,
int
xe,
int
ye,
int
size,
int
weight)
{
int
dx = xe - xs;
int
dy = ye - ys;
dx = (dx >= 0) ? dx : -dx;
dy = (dy >= 0) ? dy : -dy;
if
(dx == 0 && dy == 0) {
dropStoneLine(xs, ys, size, weight);
}
else
if
(dx == 0) {
int
yinc = (ye - ys
!= 0) ? 1 : -1;
for
(
int
i = 0; i <
dy; ++i){
dropStoneLine
(xs, ys, size, weight);
ys += yinc;
}
}
else
if
(dy == 0) {
int
xinc = (xe - xs != 0) ? 1 : -1;
for
(
int
i = 0; i < dx; ++i){
dropStoneLine(xs, ys, size, weight);
xs += xinc;
}
}
else
</
分享到:
Global site tag (gtag.js) - Google Analytics
|
相关推荐
本文将探讨如何在Android中实现两种独特的视觉效果:光点模糊渐变的自旋转圆环特效以及水滴波纹特效。这两种特效能为你的应用程序增添动态美感,提升用户的交互体验。 首先,我们来看自旋转圆环特效。这种效果通常...
本篇文章将深入探讨如何在Android中实现水波纹特效。 首先,水波纹特效的实现离不开Android的动画系统。Android提供了多种动画机制,如属性动画(Property Animation)、视图动画(View Animation)等,而水波纹...
实现Android按钮的水波纹特效通常涉及以下几个步骤: 1. **设置背景**:首先,你需要为按钮创建一个`RippleDrawable`作为背景。这可以通过XML资源文件完成,定义一个`<ripple>`标签,并在其中包含按钮的正常状态和...
总的来说,"Android水波纹特效"是一种利用贝塞尔曲线实现的动态视觉效果,它可以增强用户体验,提升应用程序的美观度。通过深入学习和实践,开发者不仅可以掌握这一特效的实现,还能进一步理解Android图形绘制和动画...
总结起来,实现光点模糊渐变的自旋转圆环特效和水滴波纹特效,需要对Android自定义View、动画系统、图形绘制有深入的理解。通过熟练掌握这些技术,开发者可以创造出更多富有创意和吸引力的UI效果,提升应用的整体...
综上所述,实现Android中的扫描和水波纹特效涉及到对动画系统、图形绘制、性能优化以及自定义组件的理解。通过掌握这些技术,开发者可以为用户创造更加生动、有趣的交互体验。在实际开发中,可以参考提供的"Randar...
"Android实现水波纹特效" Android实现水波纹特效是Android开发中常见的一种视觉效果,能够给用户带来_unique的体验感。水波纹特效是通过不断缩放和渐变的动画来实现的,下面将详细介绍其实现过程。 首先,需要在...
下面我们将详细讨论如何在Android中实现这一特效。 首先,我们要了解Android动画的基础。Android提供了两种主要的动画类型:属性动画(Property Animation)和视图动画(View Animation)。属性动画是在API 11及...
总的来说,实现"Android点击水波纹"特效需要理解RippleDrawable的用法,或者熟悉自定义View的绘制流程。通过掌握这些技术,我们可以为Android应用添加更多个性化的交互元素,提高用户的操作体验。在实际开发中,还...
综上所述,"捕鱼达人"的水波特效和涟漪效果是通过Cocos2d-x的粒子系统、动画、物理引擎、碰撞检测等特性实现的。开发者通过精细的设计和编程,将这些元素融合在一起,为玩家营造出一个生动逼真的游戏世界。在实际...
4. **文本动画效果**:利用ObjectAnimator或PropertyAnimator,可以对TextView的alpha、scaleX、scaleY等属性进行动画操作,实现淡入淡出、放大缩小等特效。 其次,Button作为用户进行交互的主要组件,其特效设计...
下面将详细介绍几个常见的Android UI特效及其实现原理。 1. **渐变色过渡**: 渐变色是一种流行的视觉效果,可以用于背景、按钮或者任何其他UI元素。Android提供了`GradientDrawable`类来创建线性、径向或角度渐变...
在IT行业中,"水波纹特效控件"通常是指一种用户界面(UI)设计元素,它能够为用户提供视觉上的互动反馈,通常是通过模仿水波扩散的效果。这种效果常见于按钮点击、触摸事件或其他用户交互场景,增加了应用或网站的...
在"Android-水波纹涟漪效果可用于设备查找之类的特效"这个主题中,我们将深入探讨如何在Android应用中实现这种效果,并将其应用到如设备查找等场景中。 首先,我们需要理解的是Android的 RippleDrawable。这是...
水波纹特效通常指的是模仿水面上涟漪扩散的动态效果。在Android中,我们可以使用多种方法来实现这一效果,比如通过自定义View、使用属性动画或者Shader。在这个特定的案例中,开发者可能使用了属性动画(Property ...
本项目"Android水波效果工程源码"正是专注于此类特效的实现,通过精巧的编程技巧,为用户呈现了逼真的雨滴落下和水面波动的效果。下面将详细探讨这个项目中的核心知识点。 1. **自定义View**: 在Android中,为了...
在Android开发中,为了提升用户体验,常常需要引入各种特效,其中水波纹效果就是一个常见的视觉交互元素。本篇文章将深入探讨如何利用SurfaceView的特性来实现屏幕点击后出现的水波纹动态效果。 首先,我们需要了解...
【NB水波特效支持库】是一种专为开发者设计的库,用于在应用程序中实现动态、美观的水波特效。这个库通常适用于游戏开发、UI设计、动画制作等场景,能够帮助开发者轻松创建出逼真的水面波动效果,提升用户体验。下面...
不多说直接上图 是不是很震撼.这个库的波纹算法是基于阿德里安波音公司的文章。国外大牛就是厉害。 下面贴出使用方法: 第一步:添加gradledependencies { compile 'com.github.r21nomi:glrippleview:1.0.0' ...