Unity 利用GPU实现翻页效果
图形图像技术CGGraph渲染图形学 4915 203
实名

通过了实名认证的内容创造者

发布于 2018-4-7 00:23:22

您需要 登录 才可以下载或查看,没有账号?注册

x
之前看到过类似“Unity怎么实现类似书本的翻页效果”之类的问题,答案大多是利用现成的插件来实现,这听上去似乎并没有实际上解决这个问题。后来又看到过一些更靠谱的解决方案例如利用UGUI的vertex modifier修改顶点、或者使用骨骼动画。
等一下,修改顶点?
修改网格数据这事没有必要一定要在cpu上进行,我们把这活放到GPU上让它来实现顶点的修改是不是更有趣一点呢。
事实上我们只需要一个Plane,在vs中根据某个属性来修改它顶点的x值和y值。
345925ac89ce1e7e82.png
1.png

而一个最简单的修改方案,就是根据玩家的翻页角度theta来更改顶点的坐标。

    float4 flip_book(float4 vertex)
    {

                    ...
        temp.x = vertex.x * cos(theta);
        temp.y = vertex.x * sin(theta);

        vertex = temp;

        return vertex;
    }

那么theta的值是怎么来的呢?一页书的翻动角度在[0,180]之间,变成弧度值就是[0,π],因此我们只需要在脚本中计算玩家拖动的距离和总长度的一个比例ratioValue,将这个ratioValue传递给vs后再和π相乘就求得了theta。

因此,在C#脚本中就需要使用这几个接口了。

IDragHandler, IPointerDownHandler, IPointerUpHandler

这样,在只经过一个pass的情况下,翻页的初步效果已经实现了。



但是如果翻过90°,可以发现此时不仅第二页没有内容,而且第一页的背面也是空的。

因此,我们还需要另外2个Pass分别渲染第一页的背面和第二页的内容。

ok,接下来我们就来完成第二个pass。

    fixed4 frag_flip_back (v2f i) : SV_Target
    {
        i.uv.x = 1 - i.uv.x;
        fixed4 col = tex2D(_BackTex, i.uv);
        return col;
    }
    //翻起来的背面
    Pass
    {
        Cull Front

        CGPROGRAM
        #pragma vertex vert_flip
        #pragma fragment frag_flip_back
        ENDCG
    }

其实很简单,只需要剔除正面,修改一下uv,然后正常的采用背面的纹理_BackTex就ok了。

可以看到当书页被翻过90°之后,书页的背面已经能够正确的显示了。

之后就是最后一个pass了,我们用这个pass来显示第二页的内容。其实这个pass很简单,仍然是只需要正常的采用背面的纹理_BackTex就ok了。但是这里要注意一个问题,那就是深度的问题。还记得第一个pass吗?第一个pass绘制了第一页的内容。但是最后一个pass同样也要绘制页面的内容,而且默认情况下深度会覆盖第一个pass绘制的内容。

因此,我们要在最后一个pass中正确的处理深度问题,所以我在这里使用了Offset。

    //第二页
    Pass
    {
        Cull Back
        Offset 1, 1

        CGPROGRAM

        #pragma vertex vert_next_page
        #pragma fragment frag_flip_back

        ENDCG
    }

OK,shader部分完工了。之后我们只需要在C#脚本中简单的确定当前的页数,来设置相应的前页的tex和后页的tex给shader。
最后的结果大概是这个样子的。

当然,为了让翻书的效果更自然,为翻动中的书页增加一些弧度似乎是一个不错的选择。
其实原理也并不复杂,在vs修改顶点位置的时候处理就好了。首先来看看翻页时候页面弯曲的一个大概形状,似乎有点像钟型?
而一说到钟型,各位应该能够想到高斯函数了吧?
所以,接下来我们画一个简单的高斯函数图形。



(推荐一下这个在线图形计算器)
它大概就长这样。
所以在vs修改顶点坐标时,把这个高斯函数考虑进去,就能够获取一个更自然的效果了。

        float flipCurve = exp(-0.1 * pow(vertex.x - 0.5, 2)) * _CurPageAngle;

        theta += flipCurve;

        temp.x = vertex.x * cos(clamp(theta, 0, pi));
        temp.y = vertex.x * sin(clamp(theta, 0, pi));


不过这里又有一个新的问题需要考虑,就是变成了弧形的书页可能会导致深度上的问题。
这个问题主要是在第二个pass,在翻书和前一页快重合时,因为第二个pass中的某些顶点的深度要大于第一个pass的深度,从而造成穿帮。所以在第二个pass的时候就要加上Offset -1 -1了。

    //翻起来的背面
    Pass
    {
        Cull Front
        Offset -1, -1

        CGPROGRAM
        #pragma vertex vert_flip
        #pragma fragment frag_flip_back
        ENDCG
    }


还没有设置签名!您可以在此展示你的链接,或者个人主页!
使用道具 <
yeshengwu  发表于 2018-4-7 00:35:58  
2#
感谢分享~~
回复 收起回复
使用道具
雨空弦  发表于 2018-10-14 16:32:29  
3#
哇好给力的帖子66666,感谢楼主无私分享!
回复 收起回复
使用道具
胆小的霸王龙  发表于 2018-10-16 01:46:40  
4#
不明觉厉,回复支持
回复 收起回复
使用道具
839310129  发表于 2018-10-16 01:48:58  
5#
回复 收起回复
使用道具
qq_漫步云端_SL8  发表于 2018-10-16 05:59:05  
6#
元素帖子强,满满正能量!!
回复 收起回复
使用道具
fatlong  发表于 2018-10-16 09:56:51  
7#
非常好,感谢分享
回复 收起回复
使用道具
小臭℃不二情。  发表于 2018-10-17 11:31:51  
8#
66666
回复 收起回复
使用道具
孱雾  发表于 2018-10-17 12:47:31  
9#
元素帖子强,满满正能量!
回复 收起回复
使用道具
qq_wangbingxv_i  发表于 2018-10-18 10:13:55  
10#
多谢分享。
回复 收起回复
使用道具
一指望月  发表于 2018-10-19 09:00:21  
11#
感谢分享
回复 收起回复
使用道具
莲见青猫  发表于 2018-10-19 09:01:36  
12#
为啥现在摇奖又要做任务了?
回复 收起回复
使用道具
山水有相逢  发表于 2018-10-20 00:32:41  
13#
local
回复 收起回复
使用道具
fengzhiyun  发表于 2018-10-20 09:29:09  
14#
感谢分享~~~~~
回复 收起回复
使用道具
白雲╄悠悠  发表于 2018-10-20 09:43:37  
15#
想要成大触,天天上元素!
回复 收起回复
使用道具
均码  发表于 2018-10-20 10:37:08  
16#
我晕~~千点万点,不如微元素指点
回复 收起回复
使用道具
qq_冰心_GLs  发表于 2018-10-20 10:37:34  
17#
66666666666
回复 收起回复
使用道具
一指望月  发表于 2018-10-20 10:38:14  
18#
感谢分享
回复 收起回复
使用道具
ceeka  发表于 2018-10-20 23:51:06  
19#
首发必发微元素,荣耀加身装备酷
回复 收起回复
使用道具
Shiva希瓦  发表于 2018-10-21 00:04:52  
20#
多谢了
回复 收起回复
使用道具
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表