参考 https://www.cnblogs.com/sevenyuan/archive/2013/07/12/3186790.html
看cocos2d源码CCActionPageTurn3D表示理解不能,找了一篇很好的论文正好把源码解释得一清二楚,论文的链接为: http://www.parc.com/content/attachments/turning-pages-3D.pdfhttp://blog.sina.com.cn/s/blog_6eb53bfc0101169t.html
想像一下这个情景,做这个功能的时候,如果没有这篇论文,我们一般的作法就是按照自己的思路,一帧一帧的把顶点算出来,然后渲染。这样也许可以达到翻页的效果,但是无论从时间上,还是空间上来说,很大的可能,都是不合算的。
所以,我们写程序的时候,往往会把一个稍微有点复杂的问题简单化(这种简单不是因为它真的简单,而是因为我们知识不够,误认为它会很简单),用简单的方法(可能是不合适,错误的方法)做出来,却得到了时间和空间上,复杂的计算结果。
下面只是为了理解而翻译,去除了一些介绍性的废话。
引言:
废话,略
1. 介绍
废话,略
2. 相关的工作
废话,略
3. 翻一个3D化的页面
正因为翻页是一本书由一个静态模式转换到另一个静态模式的一个动态的动画,所以从很大程度上来说,一本书在静态模式下是怎么表示的,会影响到在翻页过程中去如何表现它。当然,反过来也是一样的。接下来,我们会描述我们是怎么们让一本打开的书,经过一个流畅的翻页过程,变为另一本打开的书。关闭的书可以看成是打开的书的一种特例,我们可以认为它的左边,或者右边页面是不可见的。
3.1 静态模式
图 1 图 2
图1是一本打开的,3D化形式表现的书,图2是它的横截面。书的横截面可以理解为一本书被拦腰截断,而截断的方向正好垂直于把书的两部分页面联结在一起的轴。在图2中,书的左边部分(我们以后会把它称之为左块),由平行四边形ABCD表示。书的右边部分(我们以后把它称之为右块),由平行四边形EFGC表示。ACBD和EFGC相交于点C。块的宽度(AB或者EF)表示页面的宽度,块的厚度(例如:AB和CD的距离)代表书的每一页的厚度与该块中书的页数的乘积。角DAB和角EFG的大小是预先定义的常数。
4. 翻页过程中页面的变形
我们在第三节中描述了一个可以适应多种变形方式的一个通用的框架。接下来,我们将描述我们是怎样通过一个想像的锥体来对翻动中的顶部页面进行变形的。对于底部的页面也可以用同样的方式来实现。一旦顶部页面和底部页面的曲线形态计算出来以后,其它页面的翻页形态可以根据顶部和底部页面的形态构建出来。
不失一般性,我们假定待翻页的书位于XY平面上,书的下边缘与X轴平行,书的左边缘与Y轴平行(如下图所示)。我们旋转一个刚性的锥体在平面上。轴体的一边(不是中心轴)沿Y轴贴XY平面,把待翻的页面的左边缘紧紧的压在了XY平面上。(看到这里,我觉得可能需要静下心来,慢慢的想像一下我上面说的这个场景,才好继续进行下去,后面的计算都是基于这个场景的。)将页面紧贴着我们放上去的锥体转曲起来,我们便可以得到一个卷曲的页面。我们可以通过改过角θ的大小(慢慢变小),以及Ay的大小(慢慢变小),来得到不同的卷曲的页面,从而模拟出翻页的过程。(这里我们可以这样想像:锥体慢慢发缩,并且慢慢的把锥体向下抽,我们紧贴的页面便可以慢慢的翻过来了。)
图 6
特别的,在翻页的开始,我们把锥体的θ角设定为90度,使得沿着它转曲的页面看起来其实是一个平面。然后,我们慢慢地减小θ角的大小,并同时慢慢的把A点沿着Y轴向下移动。随着锥体表面的变化,书的页面开始从XY平面上卷曲起来,页面的右下角最先开始卷曲,并且卷曲程度最高。最后,我们把A无限的向下移,在这个过程接近结束的时候,书的页面又会恢复到平面上。(这里还是有点理解不了,但是cocos2d的源代码只有翻页的前半个过程,没有涉及到这个阶段,所以,对于理解源码来说,这一节的前半段已经足够了。)
正如上图所示,给定一个页面上的点P(Px, Py, 0),我们需要计算它会映射到锥体上的哪一个点。让我们在XY平面上以半径:
画一个圆,该圆和Y轴的交点我们假定为S。从几何上来说,锥体的形状告诉我们,弧SP会和一个和锥体的下表面平行的圆相重合,并且该圆通过点S。假定P点映射到了锥体上的T点,我们会有ST = SP。我们把上面提到的圆的中心点定义为C,该圆的半径为r,角SCT为β,我们有:
其中:
现在,T(Tx, Ty, Tz)可以通过如下步骤计算而来:1, 将点S绕轴(X = 0, Z = r)旋转角β得到S'(Sx', Sy', Sz'),然后将点S'绕直线(Y = Sy, Z = 0)旋转角θ。简单来说,T可以通过如下公式计算而来:
(中间有些计算过程如果理解不了的话,需要看一下旋转公式。可以参考http://zhidao.baidu.com/question/31601042,到这里,我们已经看到了下面程序段中出现的很多公式了)。
通过这个公式,对于书的页面上的每个点(在游戏中,一般是页面网格中的点),我们计算出它在锥体上的对应点。我们便会得到一个卷曲的结果页面。因为该计算过程之间的前后是没有关联性的,我们可以在每一帧中重新计算出所有顶点新的位置。这个算法即没有对页面进行压缩,也没有对页面进行拉伸,非常的自然。(Consequently, the right anglesat the corners of the pages are guaranteed to be well preserved. 这句没理解到)。并且,我们可以通过改变θ(t)和Ay(t)来得到不同的翻页动画。例如,当圆锥体退化为圆柱体的时候,书的右边缘(而非右下角)会最先开始卷曲。
下面是一些用该算法卷曲页面的效果图片:
最后,附上源码:
void CCPageTurn3D::update(float time)
{
float tt = MAX(0, time - 0.25f);
float deltaAy = (tt * tt * 500);
float ay = -100 - deltaAy;
float deltaTheta = - (float) M_PI_2 * sqrtf( time) ;
float theta = + (float) M_PI_2 +deltaTheta;
float sinTheta = sinf(theta);
float cosTheta = cosf(theta);
for (int i = 0; i <= m_sGridSize.x; ++i)
{
for (int j = 0; j <= m_sGridSize.y; ++j)
{
// Get original vertex
ccVertex3F p = originalVertex(ccg(i ,j));
float R = sqrtf((p.x * p.x) + ((p.y - ay) * (p.y - ay)));
float r = R * sinTheta;
float alpha = asinf( p.x / R );
float beta = alpha / sinTheta;
float cosBeta = cosf( beta );
// If beta > PI then we've wrapped around the cone
// Reduce the radius to stop these points interfering with others
if (beta <= M_PI)
{
p.x = ( r * sinf(beta));
}
else
{
// Force X = 0 to stop wrapped
// points
p.x = 0;
}
p.y = ( R + ay - ( r * (1 - cosBeta) * sinTheta));
// We scale z here to avoid the animation being
// too much bigger than the screen due to perspectve transform
p.z = (r * ( 1 - cosBeta ) * cosTheta) / 7;// "100" didn't work for
// Stop z coord from dropping beneath underlying page in a transition
// issue #751
if( p.z < 0.5f )
{
p.z = 0.5f;
}
// Set new coords
setVertex(ccg(i, j), p);
}
}
}
网友评论