真实感海洋的绘制(三):水面的渲染

真实感海洋的绘制(三):水面的渲染

之前的两篇博客:

  1. [真实感海洋的绘制(一):基于统计学模型的水面模拟方法][http://www.cnblogs.com/hehao98/p/8544121.html]

  2. [真实感海洋的绘制(二):使用快速傅里叶变换加速波形计算][http://www.cnblogs.com/hehao98/p/8604163.html]

根据上述两篇博客,我们已经得到了真实感较高的水面波形和法向量。为了节省所需要的顶点数目,需要将高度场和法线制作成贴图传入着色器,以便从较少的顶点就能渲染很大面积的海面。之后的任务就是对这些波形进行真实感的渲染。

基本原理

首先,高度真实感的、基于真实物理的水面光照模型相当复杂,即使能够用计算机计算,也几乎不可能实时地完成。因此,现有的实时水面渲染方法基本不会使用此类模型。本文的方法将采用只计算一次反射和折射的简单模型。

首先我们先画出高中物理中的反射和折射模型:

其中,\(\vec{v_i}\)是入射光向量,\(\theta_i\)是入射角,\(\vec{v_r}\)是反射光向量,\(\theta_r\)是反射角,\(\vec{v_t}\)为折射光向量,\(\theta_t\)是折射角,\(\vec{n}\)是法线向量。其中,入射角和反射角相等。根据斯涅尔定律(Snell's Law),入射角和折射角满足如下关系

\[n_i\sin\theta_i=n_t\sin\theta_t \]

其中,\(n_i\)\(n_t\)分别为入射介质和折射介质的折射率,对于空气而言,\(n_i=1.00\),对于水而言,\(n_t=1.33\)(近似值)。

以上讨论反映了在表面某一点的反射情况和折射情况。如果要知道这一点反射了哪里的光,可以从观察点开始做一层光线追踪,也就是假设摄像机在\(\vec{v_r}\)所指向的位置,反向计算出\(\vec{v_i}\)。之后我们必须计算出\(\vec{v_i}\)光的颜色。一般的实现方法是把四周的场景预计算,存储为环境贴图,然后就可以根据\(\vec{v_i}\)向量值来计算出这一点的入射光颜色。对于开阔的海面而言,被反射的自然就是天空了。如图所示。

当然,水面并不是一面完美的镜子。射入水面的入射光,一部分反射了回来,另一部分在折射后进入了水面,与此同时,在水面处也有从水面下经过折射后又反射回来的光。在无限大的水面中,我们可以不考虑水下反射光的复杂性,单纯将其假设为某种固定的颜色。

对于水面上任何一点,最终摄像机收到的颜色是反射环境的颜色和折射出的水下颜色之和。对于不同的入射角\(\theta_i\),入射光反射的比例\(R\)和折射进入水面的比例\(T\)是不一样的,但是满足\(R+T=1\)。这在物理上被称作费舍尔效果(Fersnel Effect)。利用电磁学理论我们可以推导出\(R\)有如下的关系

\[R=\frac{1}{2}\{\frac{\sin^2(\theta_t-\theta_i)}{\sin^2(\theta_t+\theta_i)}+\frac{\tan^2(\theta_t-\theta_i)}{\tan^2(\theta_t+\theta_i)}\} \]

当从空气进入水的时候,入射角为0度时\(R=0\),然后随着角度变大而变大,当入射角为90度时\(R=1\)。当从水进入空气时则存在全反射的现象,当入射角大于某个角度后就会全部反射,没有折射。

因此,对于水面任意一点,我们得到简单的颜色计算公式

\[Color = R * Color_{incident} + (1 - R) * Color_{deepWater} \]

就可以完成水面的着色。

一些美化

显然,由于计算资源的限制,我们是不可能高精度绘制无限大的海面的。为了节省计算资源,一种方法是使用LOD技术(Level Of Details),让近处的网格顶点密度高、远处的顶点密度低。如果有时间的话之后我会研究一下各种LOD技术。在这里,我们先使用一个偷懒的方法:加入薄雾。一方面可以遮挡住远处顶点网格的空缺,另一方面可以适当设置雾的颜色以便在远处和天空盒融为一体,最终能够获得足够好的视觉效果。

要在计算机中实现雾其实非常简单。雾可以视为一种叠加在已有物体上的颜色,这个颜色随着距离变远而加深,从而在有雾的场景中,一个像素点的颜色可以表示如下

\[Color = (1 - F(d)) * Color_{object} + F(d) * Color_{fog} \]

其中,\(F(d)\)是雾的参数函数,\(d\)是这个片元像素距离摄像机的距离,通过选取适当的参数函数,我们可以模拟出比较逼真的雾效果。我选取的参数函数如下

\[F(d)=1 - e^{-cd} \]

相信学过高中数学的读者都能画出这个函数的图像。其中,\(c\)是一个常量,其值越大,雾越浓,适当地调整参数可以获得我们想要的效果。

此外,为了和天空盒的远处相融合,我们需要利用摄像机到片元的方向来取值,从天空盒边缘取得适当的颜色来产生真实的雾效果。

实现结果

代码可见[https://github.com/hehao98/OceanSimulator]




参考文献

  1. Tessendorf, Jerry. Simulating Ocean Water. In SIGGRAPH 2002 Course Notes #9 (Simulating Nature: Realistic and Interactive Techniques), ACM Press.
  2. Johanson, Clase. Real-time water rendering - Introducing the projected grid concept. Master of Science Thesis. Lund University
  3. Toman, Wojciech. Rendering Water as a Post-process Effect. https://www.gamedev.net/articles/programming/graphics/rendering-water-as-a-post-process-effect-r2642/
posted @ 2018-04-03 17:08  Hao_He  阅读(1256)  评论(0编辑  收藏  举报