zoukankan      html  css  js  c++  java
  • 游戏中水的渲染技术系列一

    笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术具体解释》电子工业出版社等。

    CSDN视频网址:http://edu.csdn.net/lecturer/144

    水的渲染一直是图形学须要解决的问题,网上也有非常多关于这方面的技术实现,本博客的系列文章也是给读者做一个总结,本篇博客主要介绍用傅里叶变换算法实现的水反射,也是一种假反射效果,目的是优化效率。实现的效果例如以下图所看到的:


    使用傅里叶系数来表示地形高度的假反射效果,

    在我们开发的游戏中使用水着色器。告诉读者我将怎样利用引擎处理水的反射,我们自己开发水渲染效果,须要在优化方面考虑,计算每帧渲染时间。

    我们要处理渲染简化的低分辨率反射地图。由于我们还要渲染对于流动的水平面。

    算法的实现事实上都是源于生活,大家假设平时出去旅游,经常会看到山丘的反射,近处反射的比較清晰。但在远处它仅仅是一个黑色的斑点。试想一下,假设我们能够记录每一个水点周围的水面上方的地形的角度,然后我能够在水着色器中使用这个反射光线,它应该是从“天空”过渡到“地形”的点。

    Spherical harmonics球面谐波是一种众所周知的技术,通经常使用于全局照明。

    非常简要地总结:每一个顶点存储一组预先计算的系数,这同意我们重建击中对象的环境光。这些系数基本上存储从每一个方向照耀该点的光的映射图。

    反射/环境光一般是非常低的频率。因此这是这些系数怎样包括这么“多”的信息原因。

    我决定用我引擎中的水尝试相似的技术。每一个顶点保存一组小系数,描写叙述地形在水上方环绕该点的每一个方向上升的角度。这能够用一个四元系列

    的系数来描写叙述 - 基本上是球面谐波的2d方程。当在顶点之间插值时,这些傅里叶系数就能够计算出来了,给读者展示一下效果图吧。


    在水上的一个点周围的採样方向上的各种角度。

    我们计算每一个水顶点的系数。这涉及每一个顶点的操作:

    1、在点周围选择  k个均匀间隔的採样方向。k的值仅仅影响计算,因此您能够将其设置为一定

    的高以实现其仿真度,我眼下使用13。


    2、对于每一个採样方向,运行光线跟踪。一次运行一个高度地图像素。測量水面上方的地形角度。

    你想要精确的反射取决于你离岸的距离,在本文的演示样例应用程序中。我眼下使用5个像素。

    假设你的游戏涉及从低水平面的不同的水观察视角,你将须要使用很多其它(后面很多其它)。


    3、如今我们有一个函数(每2π循环)表示点周围的地形高度。


    4、为了获得表示该函数的傅里叶系数,我们须要对每一个系数的表达式进行积分计算,确切的表达式能够在网

    上找到。我使用数值积分,分辨率为400(比如每一个函数400个样本),使用的数字仅影响计算。


     5、我计算前8个系数,这个数字直接影响效果的品质和性能。8对我的目的来说肯定够好了,当然我们会尽量降低。

    我把我的系数作为16位浮点存储在我的顶点结构中(因此每一个顶点占用16个字节)。

    在水着色器中。我使用反射向量来确定我设置的角度,代码例如以下:

    float3 reflectionRay = reflect(worldPosition - CameraPosition, normal);
    float angle = atan2(-reflectionRay.z, -reflectionRay.x) + PI;
    //这给出了0和2π之间的角度,然后我们能够使用它来查找地形高度。

    本文实现的傅里叶评估函数看起来像这样(t是角度):

    float EvaluateFourier(float t, float4 coefs1, float4 coefs2)
    {
      float4 sins;
      float4 coses;
      sincos(float4(t, 2 * t, 3 * t, 4 * t), sins, coses);
      float value = coefs1.r; // a0
      value += coefs1.g * coses.r; // a1
      value += coefs1.b * sins.r; // b1
      value += coefs1.a * coses.g; // a2
      value += coefs2.r * sins.g; // b2
      value += coefs2.g * coses.b; // a3
      value += coefs2.b * sins.b; // b3
      value += coefs2.a * coses.a; // a4
      return value;
    }

    方程给了我一个角度,这也是算法与编程结合的函数实现,然后我能够比較水面上的反射光线的角度,以确认我们是否应该绘制天空或反射的地形,

    眼下仅仅是使用黑色的反射地形,效果似乎满足需求假设我们想要更好的效果。还能够存储地形的颜色,除了高度。

    当然这将使所需的数据量添加

    四倍。

    那它是怎样工作的呢?

    您能够查看本文顶部的照片作为演示样例。

    这里有一个版本号的顶点网格绘制。每一个顶点存储16字节的数据在我当前的实现。


    上图显示了我使用的顶点分辨率效果。

    在水面上使用的法线贴图有助于实现这样的假反射效果,实现的效果例如以下所看到的:


    以上实现的效果在性能方面也给读者分析一下。这也有助于读者优化Shader的渲染效果:

    上面给读者实现了一种假反射,以避免渲染昂贵的反射贴图,因此它须要具有高性能。不幸的是,这须要大

    量的着色器指令在我当前的实现中评估。

    atan2约有20条指令。HLSL产生4个标量sincos指令,其

    实际上每一个占用8个指令槽。总共,它为像素着色器加入了约64个指令槽。


    针对上述问题。我们的下一步任务是找到一种降低指令数量的方法。

    能够使用

    atan,然后是正弦和余弦。我能够通过做一些三角代替来降低这一点。或者我能够考虑使用e与虚数的幂的和来评估该系列。

    当然我们还能够降低系数的数量。

    另外。我将看到我能否够存储每一个系数在单个字节而不是16位浮点。

    最后总结一下,对于具有很多其它不同视图的游戏,这可能不是一个非常好的选择。还有这个技术的一个问题是它仅仅反射静态对象,

    地形,以及你决定在你的射线检測算法中包括的不论什么其它游戏元素。

  • 相关阅读:
    如何实现ZBrush 4R7中按钮颜色的自定义
    Zbrush遮罩边界该怎么实现羽化和锐化
    怎么在ZBrush中通过遮罩得到子物体
    怎样用好ZBrush中的PaintStop插件
    SQL 如果存在就更新,如果不存在就添加,使用 Merge 函数(SQL2008版本及以上)
    SQL 实现,如果存在就更新,如果不存在就添加
    验证码,字体旋转。
    瀑布流代码,简洁版 带分页
    瀑布流代码,简洁版
    EF
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7338984.html
Copyright © 2011-2022 走看看