用CocosCreator模拟书本翻页效果

本文主要探讨了如何使用CocosCreator来模拟书本翻页效果,分别介绍了通过使用贝塞尔曲线和verlet积分算法来模拟书页底边在翻页过程中的弯曲形变,最后通过自定义assembler传入更多的顶点,来将原本的书页纹理渲染成弯曲后的样子以达到模拟翻页的效果。

Demo引擎版本:v2.3.1

首先展示一下最终实现的效果:

书页在翻动的过程中会产生一定的弯曲形变,这就意味着在每一帧中,我们所看到的书页都具有不同的形状,如果使用序列帧动画来实现,就要对每一帧单独绘制一张图片,这样将产生大量的资源。如果能够通过代码来模拟,就可以避免资源问题。

所以,我们要解决的问题就变成了如何将原本的书页图片渲染成翻页过程中的弯曲状态呢?

我们先观察一下书页形变后的样子:

通过上面这张图,我们发现,可以将书页的形变简化成这样:

1.把书页的底边想象成一根绳子,固定左边的位置不变,拉着右边向左上方位移形成一条曲线。

2.将整个书页沿着这条曲线向上位移。

通过以上两个步骤,我们就可以模拟出书页在任何一个时刻的样子。不同时刻书页形态的区别实际上就是曲线的不同,只要能够获得任意时刻书页底边的那条曲线,就能模拟出翻页的效果。

那么,该如何获得那条曲线呢?这里提供两个方案。

(1)背景介绍

贝塞尔曲线是应用于二维图形应用程序的数学曲线,通过至少3个控制点,就可以描述出一条曲线。N阶贝塞尔曲线拥有N+1个控制点。

以3阶贝塞尔曲线为例,它的具体公式如下:

只要确定了起点P0,控制点P1,控制点P2和终点P3,就可以画出这样一条曲线,它将和P0P1、P2P3分别相切。

(2)实现

也就是说,只要找到任意时刻4个控制点的位置,我们就可以把书页底边的曲线模拟出来。当然,起点的位置其实是固定不变的(除非你把书页从书上撕下来)。

如果以终点与起点连线和水平面的夹角来描述翻页过程中的某一个具体的位置,那么我们可以观察书页在每一个角度的曲线形状,然后把控制点的位置推导出来,从而还原整个翻书过程,但这样显然是无法实现的。

我们只能先确定部分特殊角度的控制点,然后在这些控制点间做插值。

①选取几个特殊角度,获得控制点的位置,保证在这些特殊角度曲线的形状是较为真实的。

例如,在0°和180°时,我们获得的必然是一条直线,而在90°时,曲线大概是长这样:

②在特殊角度之间插值。

可以想象,均匀插值是不够真实的,以0°到90°为例,终点的运动轨迹应该是类似下图这样,x坐标的变换由慢到快,而y坐标的变换则是由快到慢。

选取一个合适的插值函数,就可以模拟出一个还不错的翻页效果:

使用贝塞尔曲线来模拟的最大问题在于,这种方法与真实的物理运动是没有关系的,它在某一个时刻的形状实际上是根据观察推测出的一个插值公式得到的。这就导致我们最终得到的曲线长度并不等于书页的宽度,所以在获得了曲线之后,我们还需要修正曲线长度与书页宽度一致,具体的实现会在后文提到。

考虑到贝塞尔曲线的缺点,我们需要一种更加接近真实物理运动规律的方法,也就是第二种实现方案——verlet积分算法。

(1)背景知识

质点弹簧系统:

模拟物体变形最简单的方法就是采用弹簧质点系统。在质点弹簧系统中,我们需要定义一系列的质点,也就是有质量的点,并假设质点之间存在一个拥有一定长度、质量为0的弹簧。当质点运动时,会受到内力和外力和影响,内力包括弹簧的弹性力和阻尼力,外力包括重力以及空气阻力等。

模拟物体的运动,实际上就是计算出物体在任意时刻的位置。在质点弹簧系统中,我们可以获得每个质点受到的力以及它的质量,根据牛顿第二定律,我们就可以算出质点的加速度。有了加速度,再通过一定的算法,就可以计算质点的位置。

显式欧拉积分算法:

显式欧拉积分是一种较为简单的算法。在显式欧拉积分中,下一时刻的状态由当前时刻的状态决定。显式欧拉积分的问题在于误差较大且不稳定,所以我们不予采用。

verlet积分算法:

verlet积分算法是一种基于位置的积分,通过质点在当前时刻和上一时刻的位置来计算出新的位置。verlet积分算法在精度和稳定性上都要优于显式欧拉积分算法,并且计算的复杂度相差不大。

约束:

假设质点间弹簧的弹力系数为无限大,那么当拉伸或压缩弹簧时,两个质点之间的距离总会保持在原来的长度。利用这种思想我们就可以通过增加质点间的距离约束来简化弹力的计算。

现在,我们把书页底边看作是由一些水平放置的质点连接而成。当我们移动尾质点的位置时,其他质点将会由于质点间弹簧的约束作用而跟着移动。这样,我们就可以使用一种较为真实的方法来获得一条曲线。

使用verlet积分来计算曲线的方法如下:

①首先根据书页的宽度来定义一系列水平放置的质点。

②在每一帧先根据verlet积分公式来更新每个质点的位置。

当前位置和上一个位置的差可以看作是质点的速度,由于空气阻力等造成的能量损耗,这里还需要额外乘上一个衰减系数。

加速度的影响这里可以简化为,一个竖直向下的重力。

③通过弹簧约束来修正质点的位置,修正的次数越多,效果也就越好,但同时带来的性能开销也就越大。

④添加一个移动尾质点的方法。

这里我们就使用和贝塞尔曲线相同的运动轨迹来做对比,可以看到使用verlet积分算法得到的结果要更加真实。

(3)参数对效果的影响

使用不同的参数值,会展现出不同的材质效果。影响verlet积分算法效果的参数主要有:速度衰减系数、重力和纠正次数。使用的时候需要通过调节参数来获得一个较为满意的效果。

速度衰减系数:

速度当前位置与上一个位置的差计算得到,可以看作是惯性的一种近似表现。当运动停止时,各个质点还会由于惯性继续运动下去。速度衰减系数越大,惯性就越大,模拟出的书页就会晃动的比较厉害。

重力:

重力会在每一帧将质点向下拉动一定距离,如果书页在水平方向运动的速度比较慢,就会表现出一种难以将书页拉起的感觉。

纠正次数:

纠正的次数越多,每两个质点之间的距离也就越接近初始的固定值,所以纠正次数会影响书页的柔软度。需要注意的,纠正次数越多,也就意味着更多的计算量,使用时应该均衡考虑。

1.如何将书页纹理渲染成沿着曲线向上位移后的样子?

涉及到渲染问题,我们首先得看一下渲染管线是如何工作的。

首先,顶点着色器会对我们传入的每个顶点进行处理,处理后的顶点将被组合成三角形,这些三角形经过光栅化会形成片段。最后,经过片段着色器的计算得到每个片段的颜色值。

简单来说,Sprite组件根据所在节点的宽、高、坐标、锚点等信息得到了由4个顶点围成的矩形,这个矩形最后显示到屏幕上时会变成许许多多个小的像素点,而每个像素点的颜色则是OpenGL根据片段的纹理坐标和Sprite使用的纹理采样得到的。

如果想要渲染出由曲线围成的书页图片,4个顶点显然是不够的。所以,我们需要自定义一个渲染组件来传入更多的顶点以完成需求:

①将书页的上下边分成若干条线段来拟合曲线,也就是在原本矩形的上下边的两个端点之间创建更多的顶点。

②连接上下边的顶点形成若干个三角形。

最终得到的图形可以看作是由若干个小的矩形拼接得到。显然顶点的数量越多,曲线也就越平滑。而经过前面的分析,每个顶点在任意时刻的坐标我们都可以轻易获得。

使用贝塞尔曲线方法直接套用贝塞尔公式即可得到,而verlet积分方法中的质点则刚好与这里的顶点一一对应。

如果把顶点数调低的话,就可以比较明显的看出各个小矩形的范围:

想要传入更多的顶点,就得自定义渲染组件和Assembler。

Assembler是指处理渲染组件顶点数据的一系列方法,每个渲染组件都拥有一个Assembler成员。Assembler中必须要定义updateRenderData及fillBuffers方法,前者需要更新准备顶点数据,后者则是将准备好的顶点数据填充进VetexBuffer和IndiceBuffer中。

2.修正贝塞尔曲线的长度

如果不对贝塞尔曲线做修正的话,在翻动过程中书页就会被拉宽或缩短:

由于贝塞尔曲线是推测得出的,所以最后获得的曲线长度与实际书页宽度并不相等。修正的方法也很简单,累加当前得到的每两个顶点之间的距离(也就是小矩形的宽度),如果大于真实宽度,就进行衰减处理,在最后一个顶点处进行多减少补,从而使得所有小矩形宽度之后等于真实宽度。

另一个需要注意的点是纹理坐标的计算,经过贝塞尔曲线公式计算后,顶点将不再是均匀分布,所以纹理坐标必须根据每个小矩形占总长度的比例来获得。如果使用等分来获得纹理坐标,效果将如下图所示:

3.背面纹理实现

在之前演示的效果图中可以看到,书页正反面的纹理是不一样的,这个是如何实现的呢?这里提供两种思路。

①背面剔除

通过设置背面剔除,OpenGL会根据节点的环绕顺序来识别正面和背面,并将所有背面的顶点都剔除掉,不参与最终的渲染。

只要使用两个节点来分别表示书页正面和背面,并一个剔除掉背面,一个剔除掉正面,即可拼出最终的图像。但还需要考虑到正面和背面的层级问题,在贝塞尔曲线的实现中,背面始终在正面的层级之上,但verlet积分算法实现的效果中就显然不是这样了,有时甚至会出现一个面穿插在另一个面中的情况:

所以背面剔除的方法具有一定的局限性。

②自定义shader

另一种方法是创建一个自定义shader,传入两张纹理,并增加一个新的顶点属性来告诉片段着色器应该对哪一张纹理进行采样。

判断一个小矩形应该使用正面纹理还是反面纹理其实很简单,只要通过前后两个顶点的x坐标来判断就行,后一个顶点的x坐标大则为正。而由于后绘制的顶点本身就会覆盖前面的顶点,所以使用这种方法就不用考虑层级的问题了。

贝塔所在的乐府互娱,是一家专注于精品移动游戏研发和运营的明星初创企业。核心团队是《少年三国志》《少年西游记》系列作品的原班人马,长期深耕卡牌手游等品类,打造过数款月流水过亿的产品,包括《少年三国志》《少年西游记》等,游戏累计流水近100亿元。目前正在高薪招聘CocosCreator开发工程师,感兴趣的小伙伴可以扫下方二维码了解详情哟!

THE END
1.算法{欧拉回路}找欧拉回路的算法算法{欧拉回路} 本文介绍了有向图和无向图中的欧拉路径与欧拉回路概念,包括半欧拉路径的定义和性质。文章还探讨了如何通过算法找到这些路径,并提供了相关证明和例题,强调了图的结构特征对于存在欧拉路径和回路的重要性。 摘要由CSDN通过智能技术生成 算法{https://blog.csdn.net/qq_66485519/article/details/128589050
2.欧拉回路算法欧拉回路算法 一、欧拉回路的判定 主要分为两大类 无向图欧拉回路判定: 1、欧拉路径:即可以一笔画,充要条件是度数为奇数的点的个数为0或2。 2、欧拉回路:欧拉路径构成一个圈,充要条件是全部是偶点。 二、有向图欧拉回路判定 1、欧拉路径:起点出度比入度大1,终点入度比出度大1,其他点全部是偶点 2、欧拉https://wenku.baidu.com/view/353df0f4ef3a87c24028915f804d2b160b4e86f3.html
3.欧拉ora代表什么欧拉(Ora)可能是指欧拉算法,它是由瑞士数学家莱昂哈德·欧拉(Leonhard Euler)在18世纪发展出来的一种数论方法。欧拉算法主要用于解决模逆元问题,即给定一个整数 本内容来自用户发表,不代表汽车之家的观点和立场。 有用反馈 本内容来自用户发表,不代表汽车之家的观点和立场。 有用反馈 https://www.autohome.com.cn/ask/6663551.html
4.AUTODYN算法简介之欧拉算法(Euler)AUTODYN 算法简介之欧拉算法(Euler) AUTODYN 软件拥有拉格朗日( Lagrange)、欧拉( Euler)、任意拉格朗日欧(ALE)和光滑粒子流体动力(SPH)等多个求解器,此外,在求解同一问题时,可以允许对模型的不同部分选用不同的数值方法,数值方法不同的网格可以相互耦合在一起而有效地解决不同物理场之间耦合分析的问题。下面对 https://www.stuch.cn/article/539
5.欧拉路径和Hierholzer算法该算法的思想是一步步构造出回路。由欧拉图的充要条件:G是欧拉图 G是若干个边不重的圈(环)的并,我们可以先找到一个环,而剩下的边一定还存在环,且这两个部分必有公共点,从而可以形成更大的环,这样直到包括所有边,即可找到欧拉回路。该算法时间复杂度为 https://www.jianshu.com/p/8394b8e5b878
6.科学网—计算方法:Euler法及其改进欧拉曾给过一个算法,这个算法是所有数值求解常微分方程的算法中最简单最直观的。即 这个算法可以想象精度非常差。这点可以通过考虑一类特殊情况非常明显地看到。假设f仅是x的函数,这时方程可以直接积出来, 其实就是要对f函数做个数值积分,也就是要求下图中f曲线下的面积。 https://blog.sciencenet.cn/blog-100379-1094683.html
7.算法分析渐近估阶与欧拉麦克劳林公式因此,在对实际算法的分析中,最终很可能会归结到对某个量的渐近估阶,在文章 函数增长与渐近分析入门 中我们介绍了渐近分析中的一些基本概念。本文我们通过一个例子,即 ∑n=1∞1nn=1∑∞n1,来看一下渐近估阶中的一个重要方法也就是欧拉-麦克劳林公式的应用。推导出这个量的渐近估计,我们就可以进一步得到快速排序https://leetcode.cn/circle/discuss/Y6CWMP/
8.欧几里德辗转相除法费马小定理欧拉定理扩展欧几里德算法简介欧几里德辗转相除法是最大公约数(greatest common divisor)的求法。 C++代码如下: int gcd(int a, int b) { if(b == 0) return a; else return gcd(b, a%b); } 这个算法就是利用了gcd(a, b) = gcd(b, a mod b)。 证明: 对于a, b的任意公约数r,则r|a, r|b。 https://www.cnblogs.com/littlehoom/p/4211819.html
9.四欧拉定理·RSA算法原理·看云因此,7的任意次方的个位数(例如7的222次方),心算就可以算出来。 欧拉定理有一个特殊情况。 假设正整数a与质数p互质,因为质数p的φ(p)等于p-1,则欧拉定理可以写成 这就是著名的费马小定理。它是欧拉定理的特例。 欧拉定理是RSA算法的核心。理解了这个定理,就可以理解RSA。https://www.kancloud.cn/kancloud/rsa_algorithm/48487
10.?欧拉算法?题目汇总 本页面为所有题目汇总,点击相应题号进入该题,或直接访问: http://PE-CN.github.io/题号/ 001 ~ 100101 ~ 200201 ~ 300301 ~ 400401 ~ 500501 ~ 600601 ~ 700701 ~ 800801 ~ 900901 ~ now 001101201301401501601701801901 002102202302402502602702802902 http://pe-cn.github.io/problems/
11.基于S一种基于结构化任意拉格朗日-欧拉算法(S-ALE)的耦合方法用于刻画水上迫降过程中的飞机与水的相互作用。采用S-ALE流固耦合方法开展了飞机水上迫降动力学分析,提取飞机的俯仰姿态和过载的变化规律,并与传统的ALE罚函数耦合方法以及相关实验结果进行对比。结果显示:相对于传统的ALE罚函数耦合方法,S-ALE流固耦合方法较好http://qks.cqu.edu.cn/html/cqdxzrcn/2020/6/20200603.htm
12.欧拉定理&费马小定理在了解欧拉定理(Euler's theorem)之前,请先了解 欧拉函数。定理内容如下:若 ,则 。证明?实际上这个证明过程跟上文费马小定理的证明过程是非常相似的:构造一个与 互质的数列,再进行操作。设 为模 意义下的一个简化剩余系,则 也为模 意义下的一个简化剩余系。所以 ,可约去 ,即得 。当 为素数时,由于 ,http://magic.vicp.io/oi-wiki/math/number-theory/fermat/
13.欧拉公式证明欧拉公式推导过程欧拉公式的意义在计算机领域中广泛使用的RSA公钥密码算法也正是以欧拉函数为基础的。在分析领域,是欧拉综合了戈特弗里德·威廉·莱布尼茨的微分与艾萨克·牛顿的流数。他在1735年由于解决了长期悬而未决的贝塞尔问题而获得名声:其中是黎曼函数。欧拉将虚数的幂定义为如下公式这就是欧拉公式,它成为指数函数的中心。在初等分析中,从本质http://sx.ychedu.com/SXJA/GEJA/600781.html