大数跨境
0
0

[unity]吃水线效果简单解析

[unity]吃水线效果简单解析 游戏开发技术教程
2025-11-11
6
导读:最近尝试了一下吃水线效果,发现网上只有几个展示效果和介绍思路的视频和文章。在作者厚脸皮的提问和各位大佬的耐心解答下,经历了许多挫折,犯了很多傻,最后终于实现了。

1.前言

最近尝试了一下吃水线效果,发现网上只有几个展示效果和介绍思路的视频和文章。在作者厚脸皮的提问和各位大佬的耐心解答下,经历了许多挫折,犯了很多傻,最后终于实现了。

我写这篇文章是为了记录一下这两周的学习过程,同时给各位有这方面想法的朋友提供一些拙见,权当抛砖引玉,还请各位大佬多多担待,有什么问题请务必指出,我一定好好听取。

我做的是较为基础的效果,还有很多的问题,需要优化,后面会有专门一栏讲讲我这个实现方式的弊端。

下面是我参考过的文章

Boat attack吃水线效果开发:

https://zhuanlan.zhihu.com/p/697302545

Unity 吃水线(waterline)效果分析

https://zhuanlan.zhihu.com/p/697364253

非常感谢这两位大佬。


2.原理讲解

想要实现吃水线效果,我们就必须得到水下的mask。

其中,重要的有两个部分,一个是相机近裁面上的世界坐标,一个是水面的高度,我们只需要得到这两个坐标的y轴进行比对,就能得到水下的mask了。

水下的mask

近裁面的世界坐标,我们通过c#端传入相机的四个角,通过screenUV的插值即可得到。

水面的高度,我采用的做法是从水面的正上方用正交相机往下拍一张rt,这个rt记录的是用深度图重建出来的水面世界坐标,将这张rt传入shader,通过我们之前的相机近裁面的世界坐标的xz轴采样,就可以得到水面在相机近裁面上的高度。

3.代码解析

1.近裁面世界坐标

在c#端,我们ViewportToWorldPoint函数获取到正交俯视相机的四个角的世界坐标,将其作为数组传入我们的shader中,同时传入我们正交相机的大小为后面的世界uv作准备。

传入shader后,通过对屏幕uv来进行插值,即可得到近裁面上的世界坐标。

为什么要这么做呢,其实是个简单的数学问题,可以理解为将相机的四个角的坐标分别对应屏幕UV的(0,0),(0,1),(1,0),(1,1)四个坐标,然后通过lerp来补齐其中缺失的坐标,正好对应我们近裁面的世界坐标。

因为我这里使用的是后处理shader,所以直接用i.uv替代,等效于screenUV。

2.水面的高度

在水面的正上方摆一个俯视的正交相机,这个正交相机只渲染水面,其他的要剔除。

重新建立一个新的后处理shader,给我们的正交相机,记住要完全覆盖水面,不然会出错。

同时创建一个512x512的rt赋给我们的正交相机作为结果输出。(大小自己定,正方形就行)

开启正交相机的深度模式,然后在shader中通过深度图重建水面世界坐标。

获取vp矩阵的逆矩阵

在c#端传入VP矩阵的逆矩阵到shader中

声明

线性深度

与透视相机不同的是,我们的正交相机实际上是一个长方体,所以我们不需要做透视除法,也不需要将深度线性化,因为正交相机的深度本身是线性的。

将屏幕空间转化到ndc空间,在乘以我们的两个逆矩阵,即可得到世界坐标。

想要更详细的重建世界坐标方法可以看看其他大佬的文章

【UnityShader】从深度图重建世界坐标(1):

https://zhuanlan.zhihu.com/p/547213235

重建成功后将世界坐标输出即可。

这时候我们就会看到我们的rt会随着水面高度变化而变化,那么就算成功了。

如果没有看到变化,看看自己的水面是不是比0低,如果是就把水面高度抬高至大于1,具体原因会在后面的问题中讲到。

随水波变化而变化的rt

3.比对两者获得水下mask

回到我们第一个创建的后处理shader,将这水面高度rt传入。

将这个rt通过我们近裁面的世界坐标的xz轴进行采样,世界坐标的uv要除以我们正交相机的size的两倍,这样才能变成正常的uv(正交相机的size一格对应的是2m)。

然后将我们的近裁面的y轴和水面高度的y轴进行比对,得到水下的mask。

最后得到的结果

4.最后

我们已经完成了最关键的一步,接下来只需要按照自己的想法对这个mask进行处理就好了,比如通过边缘检测找出吃水线,对水下加上焦散和雾效等等。

我这里是简单参考了一下ABZU的ramp深度效果。

4.效果展示


5.其中的问题

1.由于我们的水面高度是通过rt传入shader的,受到rt的rgb只有0到1的限制,我们的水面高度只有在大于0的时候才能被正确记录(也就是说水面不能摆的比0低)。

比0低后的rt

2.如果只是用相机近裁面的世界坐标xz轴采样的话,对于水的位置就有特殊要求,不能做到动态变化。

水面的transform信息,水面就是一个普通的plane

6.结尾

由于这个效果网上并没有很多资料,我也是边尝试边做的,所以其中涉及的脚本和不必要的操作较多,为了不影响观感,这里已经尽量将重要的部分给挑出来了。

对于没有完整放上代码,希望各位大佬可以谅解(因为实在太多了。。。)

有什么缺失的部分,可以联系留言,我会及时补上。

其中的问题也会在我找到方法后也会及时更新,如果大佬有好的解决方案,还请不吝赐教,真的非常感谢!!!

放上莫斯提马美图








声明:发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢。

鸣谢:感谢作者的授权转载,欢迎大家投稿,一起分享交流游戏开发技术。

作者:陈酒十里醇香

原文:https://zhuanlan.zhihu.com/p/744793928




关注【游戏开发技术教程

游戏开发技术、技巧、教程和资源,答疑解惑,内推面试


【声明】内容源于网络
0
0
游戏开发技术教程
各类跨境出海行业相关资讯
内容 1532
粉丝 0
游戏开发技术教程 各类跨境出海行业相关资讯
总阅读13.1k
粉丝0
内容1.5k