以前的两篇博客:html
[真实感海洋的绘制(一):基于统计学模型的水面模拟方法][http://www.cnblogs.com/hehao98/p/8544121.html]git
[真实感海洋的绘制(二):使用快速傅里叶变换加速波形计算][http://www.cnblogs.com/hehao98/p/8604163.html]github
根据上述两篇博客,咱们已经获得了真实感较高的水面波形和法向量。为了节省所须要的顶点数目,须要将高度场和法线制做成贴图传入着色器,以便从较少的顶点就能渲染很大面积的海面。以后的任务就是对这些波形进行真实感的渲染。ide
首先,高度真实感的、基于真实物理的水面光照模型至关复杂,即便可以用计算机计算,也几乎不可能实时地完成。所以,现有的实时水面渲染方法基本不会使用此类模型。本文的方法将采用只计算一次反射和折射的简单模型。函数
首先咱们先画出高中物理中的反射和折射模型:post
其中,\(\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\)(近似值)。spa
以上讨论反映了在表面某一点的反射状况和折射状况。若是要知道这一点反射了哪里的光,能够从观察点开始作一层光线追踪,也就是假设摄像机在\(\vec{v_r}\)所指向的位置,反向计算出\(\vec{v_i}\)。以后咱们必须计算出\(\vec{v_i}\)光的颜色。通常的实现方法是把四周的场景预计算,存储为环境贴图,而后就能够根据\(\vec{v_i}\)向量值来计算出这一点的入射光颜色。对于开阔的海面而言,被反射的天然就是天空了。如图所示。.net
固然,水面并非一面完美的镜子。射入水面的入射光,一部分反射了回来,另外一部分在折射后进入了水面,与此同时,在水面处也有从水面下通过折射后又反射回来的光。在无限大的水面中,咱们能够不考虑水下反射光的复杂性,单纯将其假设为某种固定的颜色。htm
对于水面上任何一点,最终摄像机收到的颜色是反射环境的颜色和折射出的水下颜色之和。对于不一样的入射角\(\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\)。当从水进入空气时则存在全反射的现象,当入射角大于某个角度后就会所有反射,没有折射。blog
所以,对于水面任意一点,咱们获得简单的颜色计算公式
\[ 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]