我的专栏目录:

小IVan:专题概述及目录

开篇综述:

场景制作已经越来越复杂,要求也越来越高,已经不是场景美术就能完成效果生产的了。比如大世界(面积几平方公里甚至几十平方公里),云海大气模拟,物理海洋模拟,沙漠戈壁,雨林等。而这些工作是技术美术和图形程序需要完成的。

环境模拟篇将专注于研究游戏中各种环境的模拟,天空大气,海洋,沙漠,草原,大世界地形开发等。环境模拟包括效果,优化,环境交互等内容。


这篇将研究体积云天空的内容。将由浅入深解决大气天空开发过程中的想法,理论知识,实际动手实现等。要想模拟天空效果主要模拟的有以下几部分。

(1)收集准备

在环境模拟之前,必须要先找到目标参考。我选了下面这个比较有代表性的

我把天空模拟分为以下几个部分:

【1】大规模云海,包括云海的运动,光照着色,光线散射

【2】天空的颜色变化,远景

【3】云层下的景物

首先来研究下最难的云层模拟。我把天空的云层分为四个部分

高云族

  • 卷云(Ci, Cirrus):常呈丝条状、羽毛或马尾状、钩状、片状、砧状等。
  • 卷积云(Cc, Cirrocumulus):似鳞片或球状细小云块。
  • 卷层云(Cs, Cirrostratus):呈薄幕状。

中云族

中云于2500m至6000m的高空形成。它们是由过度冷冻的小水点组成。

低云是在2500m以下的大气中形成。

直展云族

  • 积云(Cu, Cumulus):积云如同棉花团,云体垂直向上发展,常见于上午,午间发展最旺盛,并于午后开始逐渐消散。
  • 积雨云(Cb, Cumulonimbus):由积云发展而来,伴随雷暴与阵雨,云体高耸,顶部常呈花菜状或砧状,云底阴暗。

(2)云层模拟的方法

现在业内模拟云层主要有两种方法

(1)模型法

(2)Raymarching

模型法一般适用于非常风格化的游戏。

这种方法制作云好处是方便艺术家控制,坏处就是光影变化麻烦,需要制作一些非常trick的shading mode,而且面数要求极高,要是模拟大规模云海就十分吃力了。除此以外,云海的运动也没办法模拟,因为模型是定死的。

下面是我的生成方式:

先生成一个简单的模型

由这个模型生成体积云

加上噪波

然后再把它转成模型

我的节点如下

可以看到模型的面数巨高,综合考虑下来这种方法有很多局限的地方。所以现在的3A游戏都采用Ray Marching的方法。如果对RayMarching不熟悉可以看我之前的Ray Marching系列文章

小IVan:Begin ray marching in unreal engine 4【第一卷:在材质编辑器中启程】

Begin ray marching in unreal engine 4【第二卷:开始加入光照】

Begin ray marching in unreal engine 4【第三卷:更多图形更复杂的光照】

Begin ray marching in unreal engine 4【第四卷:虚幻4中的MetaBall】

Begin ray marching in unreal engine 4【第五卷:3D体纹理云】

RayMarching模拟云的话主要会使用两种办法,一种是程序化3D噪波生成体积场,一种是使用3D体纹理的方式生成体积场。3D体纹理可以在Houdini中方便创建,而且可以方便艺术家控制效果。3D噪波生成的体积场适合做大规模大范围层云。后面的文章就将围绕这两个方法来实现天空模拟。首先先来研究下程序化3D噪波模拟层云。

首先我们定义一个层云的高度场

然后在其表面铺上噪波扰动这个表面

然后从视角最远处反向向摄像机方向积分浓度

下面是我做的几个效果:

(扭曲RayDirection的结果,可以做出暴风内部的效果)

(正常情况)

下面是我的代码:

        #define NUM_STEPS 200
#define NUM_NOISE_OCTAVES 4#define HEIGHT_OFFSET 1.5#define USE_TEXTURE false
#define WHITE_NOISE_GRID_SIZE 256.0#define HASHSCALE1 443.8975
//  1 out, 2 in...
float hash12(float2 p)
{float3 p3  = frac(p.xyx * HASHSCALE1);p3 += dot(p3, p3.yzx + 19.19);return frac((p3.x + p3.y) * p3.z);
}// NOTE: the bilinear interpolation is important! without it, the clouds look blocky.
// You can see this by returning noise00 or by having the texture use the "nearest" filter
float BilinearInterpolateWhiteNoise (float2 uv)
{uv = frac(uv);float2 uvPixels = uv * WHITE_NOISE_GRID_SIZE;float2 uvFrac = uvPixels - floor(uvPixels);float2 uvDiscreteFloor = floor(uvPixels) / WHITE_NOISE_GRID_SIZE;float2 uvDiscreteCeil = ceil(uvPixels) / WHITE_NOISE_GRID_SIZE;float noise00 = hash12(float2(uvDiscreteFloor.x, uvDiscreteFloor.y));float noise01 = hash12(float2(uvDiscreteFloor.x, uvDiscreteCeil.y ));float noise10 = hash12(float2(uvDiscreteCeil.x , uvDiscreteFloor.y));float noise11 = hash12(float2(uvDiscreteCeil.x , uvDiscreteCeil.y ));float noise0_ = lerp(noise00, noise01, uvFrac.y);float noise1_ = lerp(noise10, noise11, uvFrac.y);float noise = lerp(noise0_, noise1_, uvFrac.x);return noise;
}float RandomNumber (in float3 position)
{// NOTE: the ceil here is important interestingly. it makes the clouds look round and puffy instead of whispy in a glitchy wayfloat2 uv = (position.yz + ceil(position.x)) / float(NUM_STEPS);return BilinearInterpolateWhiteNoise(uv);
}float4 mainImage(float2 fragCoord, float2 iResolution, float iTime, float3 rd, float3 ro, float3 wpos)
{float4 fragColor = float4(1,1,1,1);float3 direction = rd;//float3 direction = float3(0.8, fragCoord - 0.8);float3 skyColor = float3(0.6, 0.7, 0.8);float3 pixelColor = skyColor;for (int rayStep = 0; rayStep < NUM_STEPS; rayStep++){float3 position = 0.05 * float(NUM_STEPS - rayStep) * direction + ro * 0.001;position.xy += iTime;float noiseScale = 0.5;float signedCloudDistance = position.z + HEIGHT_OFFSET;for (int octaveIndex = 0; octaveIndex < NUM_NOISE_OCTAVES; ++octaveIndex){position *= 2.0;noiseScale *= 2.0;signedCloudDistance -= RandomNumber(position) / noiseScale;}if (signedCloudDistance < 0.0)pixelColor += (pixelColor - 1.0 - signedCloudDistance * skyColor.zyx) * signedCloudDistance * 0.4;}fragColor.rgb = pixelColor;fragColor.a = 1.0;return fragColor;
}

可以看到云大体上感觉出来了,不过有个明显的问题就是噪波不连续。

为了解决这个噪波不连续的问题,我们需要深入理解噪波的生成原理。下面是我之前做的噪波专题的文章

小IVan:虚幻4渲染编程(程序化纹理篇)【第一卷:UnrealSubstance工具节点搭建---噪波】

我重新使用Value Noise来生成云层的体积场。下面是使用新噪波后的效果

(考虑到视频大小我压得有点狠,视频有点糊)

可以看到云层不再有不连续问题

代码如下:

        #define NUM_STEPS 200
#define NUM_NOISE_OCTAVES 4#define HEIGHT_OFFSET 1.5#define USE_TEXTURE false
#define WHITE_NOISE_GRID_SIZE 256.0#define HASHSCALE1 443.8975#if 0
//  1 out, 2 in...
float Noise(float3 p)
{float3 p3  = frac(p.xyz * HASHSCALE1);p3 += dot(p3, p3.yzx + 19.19);return frac((p3.x + p3.y) * p3.z);
}
#endiffloat hash(float3 p)
{p = frac(p * 0.3183099 + 0.1);p *= 17.0;return frac(p.x * p.y * p.z * (p.x + p.y + p.z));
}float Noise(in float3 x)
{float3 p = floor(x);float3 f = frac(x);f = f * f * (3.0 - 2.0 * f);return lerp(lerp(lerp( hash(p + float3(0,0,0)), hash(p + float3(1,0,0)),f.x),lerp( hash(p + float3(0,1,0)), hash(p + float3(1,1,0)),f.x),f.y),lerp(lerp( hash(p + float3(0,0,1)), hash(p + float3(1,0,1)),f.x),lerp( hash(p + float3(0,1,1)), hash(p + float3(1,1,1)),f.x),f.y),f.z);
}// NOTE: the bilinear interpolation is important! without it, the clouds look blocky.
// You can see this by returning noise00 or by having the texture use the "nearest" filter
float BilinearInterpolateWhiteNoise (float3 uv)
{uv = frac(uv);float3 uvPixels = uv * WHITE_NOISE_GRID_SIZE;float3 uvFrac = uvPixels - floor(uvPixels);float3 uvDiscreteFloor = floor(uvPixels) / WHITE_NOISE_GRID_SIZE;float3 uvDiscreteCeil = ceil(uvPixels) / WHITE_NOISE_GRID_SIZE;float noise00 = Noise(float3(uvDiscreteFloor.x, uvDiscreteFloor.y, uvDiscreteFloor.z));float noise01 = Noise(float3(uvDiscreteFloor.x, uvDiscreteCeil.y ,uvDiscreteCeil.z));float noise10 = Noise(float3(uvDiscreteCeil.x , uvDiscreteFloor.y, uvDiscreteFloor.z));float noise11 = Noise(float3(uvDiscreteCeil.x , uvDiscreteCeil.y, uvDiscreteCeil.z));float noise0_ = lerp(noise00, noise01, uvFrac.y);float noise1_ = lerp(noise10, noise11, uvFrac.z);float noise2_ = lerp(noise10, noise11, uvFrac.x);float noise = lerp(lerp(noise0_, noise1_, uvFrac.x), noise2_, uvFrac.y);return noise;
}float RandomNumber (in float3 position)
{// NOTE: the ceil here is important interestingly. it makes the clouds look round and puffy instead of whispy in a glitchy wayfloat3 uv = (position.yzx + ceil(position.x)) / float(NUM_STEPS);//return BilinearInterpolateWhiteNoise(uv);return Noise(position);
}float4 mainImage(float2 fragCoord, float2 iResolution, float iTime, float3 rd, float3 ro, float3 wpos)
{float4 fragColor = float4(1,1,1,1);float3 direction = rd;//float3 direction = float3(0.8, fragCoord - 0.8);float3 skyColor = float3(0.6, 0.7, 0.8);float3 pixelColor = skyColor;for (int rayStep = 0; rayStep < NUM_STEPS; rayStep++){float3 position = 0.05 * float(NUM_STEPS - rayStep) * direction + ro * 0.001;position.xy += iTime;float noiseScale = 0.5;float signedCloudDistance = position.z + HEIGHT_OFFSET;for (int octaveIndex = 0; octaveIndex < NUM_NOISE_OCTAVES; ++octaveIndex){position *= 2.0;noiseScale *= 1.6;signedCloudDistance -= RandomNumber(position) / noiseScale;}if (signedCloudDistance < 0.0)pixelColor += (pixelColor - 1.0 - signedCloudDistance * skyColor.zyx) * signedCloudDistance * 0.4;}fragColor.rgb = pixelColor;fragColor.a = 1.0;return fragColor;
}

至此我们完成了天空模拟的第一部分,后续文章将继续研究天空模拟的剩下部分。

Enjoy it.


Next:

小IVan:虚幻4渲染编程(环境模拟篇)【第二卷:体积云天空模拟(2)---3D体纹理低云】


参考文章

【1】高效真实的云渲染算法 - effulgent - 博客园

【2】http://killzone.dl.playstation.net/killzone/horizonzerodawn/presentations/Siggraph15_Schneider_Real-Time_Volumetric_Cloudscapes_of_Horizon_Zero_Dawn.pdf

【3】Lele Feng:游戏中的云海效果是怎样实现的?

虚幻4渲染编程(环境模拟篇)【第一卷:体积云天空模拟(1)---层云】相关推荐

  1. 虚幻4渲染编程(环境模拟篇)【第三卷:体积云天空模拟(3)---高层云】

    我的专栏目录: 小IVan:专题概述及目录 目前业内流行有两种体积云模拟的方式,模型+特殊shader法,RayMarching法.我前两篇文章已经对它们都做了介绍.当然还有些比较非主流的,比如粒子云 ...

  2. 虚幻4渲染编程(灯光篇)【第二卷:体积光】

    我的专栏目录: 小IVan:专题概述及目录 体积光在游戏里被越来越多地用到,对烘托场景气氛,提高游戏的逼格有比较重要的作用.这篇就来由浅入深研究一下这个东西.从容易的做法到高端做法依次递进. 首先先来 ...

  3. 虚幻4渲染编程(环境模拟篇)【第五卷:可交互物理植被模拟 - 上】

    我的专栏目录: 小IVan:专题概述及目录 开篇综述 这一卷将会开始研究可交互植被环境的模拟.我把可交互植被环境模拟这个大的课题拆解为几个部分.我挑选了几个森林模拟至关重要的几个要素并且实现它们. [ ...

  4. 虚幻4渲染编程(Pipeline篇)【第一卷:PBR Production Pipeline】

    我的专栏目录: 小IVan:专题概述及目录 简介: 当技术这边完成理论推导和研发后,需要为其配备一整套完整的落地方案,并且这套方案的成熟度,完善度和开发效率直接决定了产品的最后品质和公司的生产成本.随 ...

  5. 虚幻4渲染编程(灯光篇)【第一卷:各种ShadowMap】

    小IVan:专题概述及目录 灯光篇开篇概述: 灯光在游戏里非常重要,游戏画面的整体感觉,游戏运行的效率都和它息息相关.然而虚幻把这些逻辑封得死死地.各种概念和庞大的系统常常令人束手无策.本篇将从灯光光 ...

  6. 虚幻4渲染编程(光线追踪篇)【第一卷:光线追踪篇开篇综述】

    MY BLOG DIRECTORY: 小IVan:专题概述及目录​zhuanlan.zhihu.com INTRODUCTION: 什么都不说了先上个效果: 光线追踪云 电子游戏的光线追踪时代即将到来 ...

  7. 虚幻4渲染编程(游戏Demo篇)【第一卷:飞机大战】

    我的专栏目录: 小IVan:急速游戏开发综述及目录 概述: 技术美术需要对整个游戏制作流程都非常清楚,这样才能在开发中提出合理的制作方法.有时候有空可以自己做做游戏demo,这个过程也非常有趣.游戏D ...

  8. 虚幻4渲染编程(图元汇编篇)【第五卷:游戏中的动力学模拟】

    我的专栏目录 小IVan:专题概述及目录 还是先上效果吧 目前(2018年)在游戏中,通常使用韦尔莱积分做动力学模拟.使用韦尔莱积分可以模拟大部分物体的运动.布料,绳子,弹簧,软体,棍子都可以模拟.但 ...

  9. 渲染到ui_虚幻4渲染编程(UI篇)【第二卷:程序化UI特效-[1]】

    MY BLOG DIRECTORY: 小IVan:专题概述及目录​zhuanlan.zhihu.com INTRODUCTION: 当遇见某些特殊需求,比如对游戏效果有很多变化的要求,这时使用静态的贴 ...

  10. 虚幻4皮肤材质_虚幻4渲染编程(材质编辑器篇)【第六卷:各向异性材质amp;玻璃材质】...

    My blog directory: YivanLee:专题概述及目录​zhuanlan.zhihu.com Introduction: 各向异性材质 玻璃材质 材质编辑器篇的很多效果都非常简单,可以 ...

最新文章

  1. 五十音图平假名流氓记忆(MD~!真难)
  2. 2000坐标系高程与85高程转换_科普 | 如何在大疆智图中设置坐标系
  3. DataRow 数组转化成DataTable
  4. android 按比例缩放,Android postScale不按比例缩放
  5. android颜色选择状态,androidUiAutomator如何根据颜色判断控件的状态
  6. c++primer plus 第11章 编程题第7题
  7. 用高等数学“铲雪”!这个200多年前的证明太厉害了,有城市用它省了2000多万..........
  8. 微信小程序基础(一)
  9. 二叉树学习之非递归遍历
  10. oracle 下和 db2的syscat 对应的,oracle db2命令对比(整理中)
  11. 安卓源代码_如何从在安卓Android手机获取微信小程序源代码
  12. 微机综合保护装置怎么选择?在高压柜中起什么作用?
  13. ubuntu系统,网页版音乐播放器无声音
  14. wps打印预览工具栏消失_在WPS电子表格中看不到打印预览怎么办?
  15. python-opencv第四期:threshold函数详解
  16. 图片阴影怎么设置_电影大片风格!教你用PS调出胶片质感的图片
  17. 程序员如何实现“互联网+”03-为什么需要有个网站
  18. Windows10和Ubuntu双系统如何卸载Ubuntu系统
  19. 互联网人才结构与流动报告2021
  20. Django基础之MVT

热门文章

  1. 对于Gitlab项目的Developer权限问题 (Guest,Reporter,Developer,Maintainer)
  2. 数学有趣地超乎你的想象!超级有趣!
  3. 80C51单片机的基本信息
  4. 韶关市教育信息化名教师蓝凌工作室成员简介
  5. php 视频添加水印,记php调用ffmpeg给视频加文字水印
  6. Android Parcel数据传输源码解析
  7. 班得瑞[Bandari]音乐介绍
  8. linux 跨网段ping,Linux中跨网段ping问题
  9. 如何快速删除百度相关搜索中的关键词?
  10. 网络操作系统 Linux配置与管理,网络操作系统—Linux配置与管理