地形渲染之爬过的坑

2020-12-29 09:58:04 浏览数 (1)

目前我们采用TiledMap的菱形模式来编辑地形,然后再导入到Unity, 将TiledMap的每一个菱形以Unity中的Quad为单位来拼出来。

以目前我的知识水平来看,这么做至少有4个问题。

  1. Quad是以正方形为单位拼接的,而我们在TiledMap中每一个菱形是以Quad为单位渲染而成,客户端在使用Quad进行渲染时,为了表现的像个菱形,每两个Quad都会在顶角进行重叠,这需要我们美术出的图四个角Alpha通道都是0,这会增加无用的Overdraw。
  2. 为了降低纹理大小,整个地形都是由有限个基础Tile相互叠加来生成不同的地形。所以在TiledMap中,整个地形是由好几层组成,这就意味着每一个菱形都有可能需要几个Tile进行混合而成。这同样会增加Overdraw, 而且大概猜测一下,半透明渲染渲染由于不会写zbuffer, 所以在渲染之前可能还需要类似画家算法一样进行排序,这同样是开销。
  3. 虽然整个地形只加载9屏,但是由于每一个Quad都是一个GameObject, 这导致我们客户端在做性能测试时,刚起来就需要Instantiate数千个GameObject并常驻。
  4. 本质上每个Quad就是一块mesh,但是他有顶点UV总是从0到1,所以我们无法良好的使用法线贴图来增加地表细节(虽然我不懂渲染,但是作为一个玩家来讲,一块平板地表,我是不能接受的^_^!)。

最开始我并没有接触到,客户端采用的什么方式进行地形渲染,只是在开发中期,我们在一个叫UWA的网站上进行了一次真人真机性能分析。我们发现客户端会Instantiate 和Destroy大量的GameObject并且Overdraw居高不下。仔细了解下来,才发现他们是使用上面所说的方式进行渲染的。

虽然后来将Quad进行池化, 但是依然会造成1000~2000的GameObject常驻内存。

此时虽然我已经写过一次光栅化程序, 大致了解了渲染流程。但是除了池化的建议外,我也想不到更好的办法。对于居高不下的Overdraw我更是没有任何办法。

随着后来对Unity Shader的熟悉,我发现了一个可以降低Overdraw和GameObject一举两得的办法。

那就是对地图使用的这些Quad进行自定义Shader, 我们只要需要保证每一个菱形都是由一个Quad渲染而成,那么上面所说的问题2所带来的开销就不存在了。

而实现这一需求也很简单,可以让一个Shader有多个纹理输入,把每一层的纹理都输入进去,然后在shader内部去手动混合后,直接输出最终颜色值。至此我黔驴技穷,再也没有想法了。

在又学了一次计算机图形学之后,基于上面的方案我又有了新的想法。

即然现在整个地形是由很多Quad组合而成,如果我们对整个抽象进行“降维打击”。从最终渲染单位来看,其实整个地形是由很多个三角形组成,那我们完全可以创建一个Mesh,这些Mesh的顶点数据和相应的Quad上的顶点数据(position,uv)完全一样。这样我们只需要一个GameObject就能渲染出一屏的地形来。

当然不仅仅是节省GameObject这么简单,有了这个Mesh我们可以做很多事。

比如我可以给每个顶点增加一组UV坐标,这个坐标用于采用整个地形的法线纹理。这样我只需要一张对应整个地形的法线纹理,就可以极大的加强地形细节效果。甚至我们还可以再增加对应整个地形的高度图来各种连续起伏的山脉。

同时,由于我们在一张Mesh中,不可能也不需要采用Quad相互重叠来达到菱形的效果。我们在创建Mesh时采用的顶点可以是恰好菱形的四个顶点。这样问题1,3,4都在一定程度上解决了。更棒的是我们还可以使用TiledMap, 整个工作流也没有任何变化。对于美术来讲惟一的变化是他们需要多提供一张法线纹理。

--------------

原本我以为我这个方案已算是极好。

但是最近我们在改版游戏时, 我了解到了一个地形编辑器叫WorldCreator, 一种叫做splatting的地形渲染方案,该方案在知乎上有详细的介绍及Demo.

这个方案相比上文的最终方案来讲更灵活。假如我们地形最多由四层纹理混合而成。

WorldCreator除了会使用四层纹理之后,还会额外生成三张对应整个地图的三张纹理,splatting,normalmap,heightmap。

其中splatting的四个通道会控制四层纹理在混合时的权重,在上文我的方案中,固定的四层纹理混合到一起效果是固定不变的。但是splatting渲染方案下,即使相同的四层纹理,在splatting图的控制下依然会形完全不同的效果,可以做到全地形唯一。

heightmap的作用与上文我的方案并无太大差别,这里就不做说明。

其中normalmap纹理是用作增加地形细节的,比如有一座高山,我们的Mesh三角形很大,如果仅凭zbuffer, 很多明暗细节就表现不出来,这时就需要靠这张normalmap来达到逼真的效果。

WorldCreator生成的四层纹理,每一层纹理有三张贴图组成,分别叫diffuse,disp,normal。

diffuse就是地貌纹理,normal是用于使diffuse的细节更加逼真。

最为惊艳的就是这个disp贴图,通过这个disp贴图,我们可以知道以纹素为单位的高度。在混合时,除了可以依据splatting纹理的权重外,纹理间的高度对比也是生成逼真的细节的重要部分,如沙子只出现在砖缝里,这也是我的方案难以企及的效果。

0 人点赞