虚幻4渲染编程(环境模拟篇)【第一卷:体积云天空模拟(1)---层云】
我的专栏目录:
小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)---层云】相关推荐
- 虚幻4渲染编程(环境模拟篇)【第三卷:体积云天空模拟(3)---高层云】
我的专栏目录: 小IVan:专题概述及目录 目前业内流行有两种体积云模拟的方式,模型+特殊shader法,RayMarching法.我前两篇文章已经对它们都做了介绍.当然还有些比较非主流的,比如粒子云 ...
- 虚幻4渲染编程(灯光篇)【第二卷:体积光】
我的专栏目录: 小IVan:专题概述及目录 体积光在游戏里被越来越多地用到,对烘托场景气氛,提高游戏的逼格有比较重要的作用.这篇就来由浅入深研究一下这个东西.从容易的做法到高端做法依次递进. 首先先来 ...
- 虚幻4渲染编程(环境模拟篇)【第五卷:可交互物理植被模拟 - 上】
我的专栏目录: 小IVan:专题概述及目录 开篇综述 这一卷将会开始研究可交互植被环境的模拟.我把可交互植被环境模拟这个大的课题拆解为几个部分.我挑选了几个森林模拟至关重要的几个要素并且实现它们. [ ...
- 虚幻4渲染编程(Pipeline篇)【第一卷:PBR Production Pipeline】
我的专栏目录: 小IVan:专题概述及目录 简介: 当技术这边完成理论推导和研发后,需要为其配备一整套完整的落地方案,并且这套方案的成熟度,完善度和开发效率直接决定了产品的最后品质和公司的生产成本.随 ...
- 虚幻4渲染编程(灯光篇)【第一卷:各种ShadowMap】
小IVan:专题概述及目录 灯光篇开篇概述: 灯光在游戏里非常重要,游戏画面的整体感觉,游戏运行的效率都和它息息相关.然而虚幻把这些逻辑封得死死地.各种概念和庞大的系统常常令人束手无策.本篇将从灯光光 ...
- 虚幻4渲染编程(光线追踪篇)【第一卷:光线追踪篇开篇综述】
MY BLOG DIRECTORY: 小IVan:专题概述及目录zhuanlan.zhihu.com INTRODUCTION: 什么都不说了先上个效果: 光线追踪云 电子游戏的光线追踪时代即将到来 ...
- 虚幻4渲染编程(游戏Demo篇)【第一卷:飞机大战】
我的专栏目录: 小IVan:急速游戏开发综述及目录 概述: 技术美术需要对整个游戏制作流程都非常清楚,这样才能在开发中提出合理的制作方法.有时候有空可以自己做做游戏demo,这个过程也非常有趣.游戏D ...
- 虚幻4渲染编程(图元汇编篇)【第五卷:游戏中的动力学模拟】
我的专栏目录 小IVan:专题概述及目录 还是先上效果吧 目前(2018年)在游戏中,通常使用韦尔莱积分做动力学模拟.使用韦尔莱积分可以模拟大部分物体的运动.布料,绳子,弹簧,软体,棍子都可以模拟.但 ...
- 渲染到ui_虚幻4渲染编程(UI篇)【第二卷:程序化UI特效-[1]】
MY BLOG DIRECTORY: 小IVan:专题概述及目录zhuanlan.zhihu.com INTRODUCTION: 当遇见某些特殊需求,比如对游戏效果有很多变化的要求,这时使用静态的贴 ...
- 虚幻4皮肤材质_虚幻4渲染编程(材质编辑器篇)【第六卷:各向异性材质amp;玻璃材质】...
My blog directory: YivanLee:专题概述及目录zhuanlan.zhihu.com Introduction: 各向异性材质 玻璃材质 材质编辑器篇的很多效果都非常简单,可以 ...
最新文章
- 五十音图平假名流氓记忆(MD~!真难)
- 2000坐标系高程与85高程转换_科普 | 如何在大疆智图中设置坐标系
- DataRow 数组转化成DataTable
- android 按比例缩放,Android postScale不按比例缩放
- android颜色选择状态,androidUiAutomator如何根据颜色判断控件的状态
- c++primer plus 第11章 编程题第7题
- 用高等数学“铲雪”!这个200多年前的证明太厉害了,有城市用它省了2000多万..........
- 微信小程序基础(一)
- 二叉树学习之非递归遍历
- oracle 下和 db2的syscat 对应的,oracle db2命令对比(整理中)
- 安卓源代码_如何从在安卓Android手机获取微信小程序源代码
- 微机综合保护装置怎么选择?在高压柜中起什么作用?
- ubuntu系统,网页版音乐播放器无声音
- wps打印预览工具栏消失_在WPS电子表格中看不到打印预览怎么办?
- python-opencv第四期:threshold函数详解
- 图片阴影怎么设置_电影大片风格!教你用PS调出胶片质感的图片
- 程序员如何实现“互联网+”03-为什么需要有个网站
- Windows10和Ubuntu双系统如何卸载Ubuntu系统
- 互联网人才结构与流动报告2021
- Django基础之MVT
热门文章
- 对于Gitlab项目的Developer权限问题 (Guest,Reporter,Developer,Maintainer)
- 数学有趣地超乎你的想象!超级有趣!
- 80C51单片机的基本信息
- 韶关市教育信息化名教师蓝凌工作室成员简介
- php 视频添加水印,记php调用ffmpeg给视频加文字水印
- Android Parcel数据传输源码解析
- 班得瑞[Bandari]音乐介绍
- linux 跨网段ping,Linux中跨网段ping问题
- 如何快速删除百度相关搜索中的关键词?
- 网络操作系统 Linux配置与管理,网络操作系统—Linux配置与管理