Unity Android海水效果插件(Ocean)

【博物纳新】是UWA重磅推出的全新栏目,旨在为开发者推荐新颖、易用、有趣的开源项目,帮助大家在项目研发之余发现世界上的热门项目、前沿技术或者令人惊叹的视觉效果,并探索将其应用到自己项目的可行性。很多时候,我们并不知道自己想要什么,直到某一天我们遇到了它。


导读
这个项目是基于Unity社区中一个经典Ocean shader多次改进后海洋场景,海平面实现了浮力、波浪、风、气泡、交互泡沫、焦散以及其他的一些光的反射折射效果。本文重点介绍海平面场景的构建,其他效果的实现不作重点介绍。

Unity Android 海水插件 unity ocean_List

                                                                                               普通效果

Unity Android 海水插件 unity ocean_Math_02

                                                                                             与岛屿交互

Unity Android 海水插件 unity ocean_Unity Android 海水插件_03

                                                                                开启风效果,并设置较大风


使用方法

项目作者将重要参数可视化,在Inspector面板中进行修改。如果不太明白该参数的模拟的效果,可点击参数末尾的“?”按钮,会有详细的解释。

例如:Waves Settings 中的参数,由上而下依次可已设置波浪大小、波浪波动大小、流速、波浪密度。

Unity Android 海水插件 unity ocean_List_04

                                                                                    Ocean Object属性面板

并在其中预置了一些参数集作为可选场景,大家也可以保存自己修改后的参数集,添加可选场景。

Unity Android 海水插件 unity ocean_System_05

可选场景列表



实验原理/方法

  • 浮力效果在Buoyancy中实现;
  • 海平面的效果在Ocean.cs中实现,其中SetupOffscreenRendering、RenderReflectionAndRefraction等函数用于一些光影效果的实现,本文不做重点介绍,有兴趣的读者可以下载源代码研究。

Unity Android 海水插件 unity ocean_Math_06

                                                                                     开启折射反射效果

Unity Android 海水插件 unity ocean_List_07

                                                                                         不开启折射反射效果

作者采用绘制Mesh作为海平面、采用LOD技术进行优化。
作者将海平面区域划分为如图所示的11*11块方形区域,采用5级LOD、最外层加载一个铜钱形状Mesh来填充最外围的场景。

Unity Android 海水插件 unity ocean_List_08

                                                                                            海平面分割图

所有Mesh全部生成在名为Ocean的Object下,部分生成的Mesh列表如下:

Unity Android 海水插件 unity ocean_List_09

Mesh列表

船体永远位于5*5Mesh区域内,LOD级别为LOD_0,绘制最为精细的细节与效果。其余部分采取较低级别LOD,如图所示:

Unity Android 海水插件 unity ocean_博物纳新_10

                                                                                             普通场景

Unity Android 海水插件 unity ocean_Unity Android 海水插件_11

                                                                                            开启WireFrame

当船体移动,驶出该区域,Ocean 组件会计算偏移量,然后将Ocean Object整体移动一块Mesh的距离,例如,在场景开始后控制船体向X轴负方向前进,直至驶出该Mesh区域,此时Ocean Object通过计算,也平移了一段距离:

Unity Android 海水插件 unity ocean_System_12

                                                                                        注意Mesh的移动

Unity Android 海水插件 unity ocean_博物纳新_13

                                                                                             移动前

Unity Android 海水插件 unity ocean_Unity Android 海水插件_14

                                                                                             移动后

这样可以保证距离摄像机最近的地方显示效果最佳,距离摄像机较远的地方绘制的Mesh采用较低等级的LOD,以节省开销。

以下节选相应代码:
用于计算偏移量,来决定是否移动Ocean Object

登录后复制


:

 1    void calculateCenterOffset() {
 2        if (followMainCamera && player) {
 3            centerOffset.x = MyFloorInt(player.position.x * sizeInv.x) *  size.x;
 4            centerOffset.z = MyFloorInt(player.position.z * sizeInv.y) *  size.z;
 5            centerOffset.y = transform.position.y;
 6            if(transform.position != centerOffset) {
 7                 ticked = true;
 8                 transform.position = centerOffset; 
 9                //确保在偏移更改时立即更新LOD0
10                updateTiles(0, 1);
11                ticked2 = true;
12             }
13            //计算高度
14            if(player) {
15                if(farLodOffset!=0) {
16                    flodFact = 1f - Mathf.Clamp01((player.position.y)*0.0007f);
17                    //调整摄像机距离
18                    ffact = MyFloorInt(flodFact*10.5f);
19                    if(ffact != oldffact) {
20                        oldffact = ffact;
21                        ticked = true;
22                        updateTiles(1, max_LOD);
23                    }
24                 }
25             }
26        }
27    }

产生Mesh并选择对应的LOD级别(变量christ):

登录后复制


1   void GenerateTiles() {
 2
 3        int chDist, nmaxLod=0; // Chebychev distance
 4
 5        //设置LOD级别
 6  for (int y=0; y<tiles; y++) {
 7            for (int x=0; x<tiles; x++) {
 8                chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x));
 9                chDist = chDist > 0 ? chDist - 1 : 0;
10                if(nmaxLod<chDist) nmaxLod = chDist;
11            }
12        }
13        max_LOD = nmaxLod+1;
14
15        flodoffset = new float[max_LOD+1];
16        float ffact = farLodOffset/max_LOD;
17        for(int i=0; i<max_LOD+1; i++) {
18            flodoffset[i] = i*ffact;
19        }
20
21        btiles_LOD = new List<Mesh>();
22        tiles_LOD = new List<List<Mesh>>();
23//添加Mesh
24        for (int L0D=0; L0D<max_LOD; L0D++) {
25            btiles_LOD.Add(new Mesh());
26            tiles_LOD.Add (new List<Mesh>());
27        }
28
29        GameObject tile;
30
31        int ntl = LayerMask.NameToLayer ("Water");
32
33        for (int y=0; y<tiles; y++) {
34            for (int x=0; x<tiles; x++) {
35                chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x));
36                chDist = chDist > 0 ? chDist - 1 : 0;
37                if(nmaxLod<chDist) nmaxLod = chDist;
38                float cy = y - Mathf.Floor(tiles * 0.5f);
39                float cx = x - Mathf.Floor(tiles * 0.5f);
40                tile = new GameObject ("Lod_"+chDist.ToString()+":"+y.ToString()+"x"+x.ToString());
41
42                Vector3 pos=tile.transform.position;
43                pos.x = cx * size.x;
44                pos.y = transform.position.y;
45                pos.z = cy * size.z;
46
47                tile.transform.position=pos;
48                tile.AddComponent <MeshFilter>();
49                tile.AddComponent <MeshRenderer>();
50                Renderer renderer = tile.GetComponent<Renderer>();
51
52                tile.GetComponent<MeshFilter>().mesh = btiles_LOD[chDist];
53                //tile.isStatic = true;
54
55    //选择Material
56                    if(numberLods==2) {
57                        if(chDist <= sTilesLod) { if(material) renderer.material = material; }
58                        if(chDist > sTilesLod) { if(material1) renderer.material = material1; }
59                    }else if(numberLods==3){
60                        if(chDist <= sTilesLod ) { if(material) renderer.material = material; }
61                        if(chDist == sTilesLod+1) { if(material1) renderer.material = material1; }
62                        if(chDist > sTilesLod+1) { if(material2) renderer.material = material2; }
63                    }
64                } else {
65                    renderer.material = material;
66                }
67
68//设置为子节点               
69                tile.transform.parent = transform;
70
71//也不希望在进行折射/反射传递时绘制这些,
72//所以将添加到水层以便于过滤。
73
74                tile.layer = ntl;
75
76                tiles_LOD[chDist].Add( tile.GetComponent<MeshFilter>().mesh);
77            } 
78        }
79
80        //是否开启最外层铜钱状Mesh
81        initDisc();
82    }

不同的LOD级别也对应不同的Material

登录后复制


1mat[0] = material;
2        mat[1] = material1;
3        mat[2] = material2;

Unity Android 海水插件 unity ocean_System_15

不同LOD级别的材质



性能测试
本次性能测试中,使用了开启多线程渲染的版本,分别在小米8、红米Note2两款设备上进行了测试,并使用UWA GOT Online获取性能数据。得到数据如下:

Unity Android 海水插件 unity ocean_博物纳新_16

可以看到即使在红米Note2这样的低端机上,这个Demo也可以跑出平均46帧,在这样的效果来说属于性能非常不错的移动端海洋效果,推荐在移动设备上使用。


今天的推荐就到这儿啦,或者它可直接使用,或者它需要您的润色,或者它启发了您的思路~请不要吝啬您的点赞和转发,让我们知道我们在做对的事。当然如果您可以留言给出宝贵的意见,我们会越做越好。

快用UWA Lab合辑Mark好项目!

Unity Android 海水插件 unity ocean_博物纳新_17


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空