定向体积光

最近PS5重玩大表哥2,发现他们的体积光有点棒,遂实现一下:

附YYDS大表哥2里的体积光:

一、原理

1.1 体积光散射算法(GPU Gems 3)

此算法属于上古大神的处理方式,全部在屏幕空间处理,具体参照本书13章节体积光散射算法,具体不再赘述,仅放出效果:

1.2 Ray Matching处理

本方法主要是 NVIDIA 2016年开发者大会 发布的Fast, Flexible, Physically-Based Volumetric Light Scattering算法。

总结来说,本算法就是SM的升级版本:SM仅判断物体上的点处理光照,而体积光算法则是,在计算物体上着色的时候,从视点出发,按一定的采样频率逐步进行计算空间点针对光源的可见性,从而计算最终着色
具体示意见下图:

其中实现方向红色即为体积光作用区域,蓝色为不可见区域。

不要被式子中积分吓唬到了,到具体执行的时候都是各种近似,直接沿步长采样即可。想具体了解的可以参照原版PDF。

二、代码实现

本此算法主要是以Ray Matching的方式处理定向体积光。在场景生成后,多加一个render pass,专门来处理体积光即可,下边主要贴出此pass的shader如下:

VS: 屏幕正方形绘制:


struct VertexOut
{float4 PosH : SV_POSITION;float2 Tex : TEX;
};VertexOut main(uint vI : SV_VERTEXID)
{int2 texcoord = int2(vI & 1, vI >> 1);VertexOut vout;vout.Tex = float2(texcoord);vout.PosH = float4(2 * (texcoord.x - 0.5f), -2 * (texcoord.y - 0.5f), 0.0, 1);return vout;
}

PS: Ray Matching处理体积光

Texture2D<float> depthTx : register(t2);
Texture2D shadowDepthMap : register(t4);struct VertexOut
{float4 PosH : SV_POSITION;float2 Tex  : TEX;
};// *************** DITHER 抖动算法 ***************
static const float2x2 BayerMatrix2 =
{1.0 / 5.0, 3.0 / 5.0,4.0 / 5.0, 2.0 / 5.0
};static const float3x3 BayerMatrix3 =
{3.0 / 10.0, 7.0 / 10.0, 4.0 / 10.0,6.0 / 10.0, 1.0 / 10.0, 9.0 / 10.0,2.0 / 10.0, 8.0 / 10.0, 5.0 / 10.0
};static const float4x4 BayerMatrix4 =
{1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
};static const float BayerMatrix8[8][8] =
{{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 },{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 },{ 9.0 / 65.0, 57.0 / 65.0, 5.0 / 65.0, 53.0 / 65.0, 12.0 / 65.0, 60.0 / 65.0, 8.0 / 65.0, 56.0 / 65.0 },{ 41.0 / 65.0, 25.0 / 65.0, 37.0 / 65.0, 21.0 / 65.0, 44.0 / 65.0, 28.0 / 65.0, 40.0 / 65.0, 24.0 / 65.0 },{ 3.0 / 65.0, 51.0 / 65.0, 15.0 / 65.0, 63.0 / 65.0, 2.0 / 65.0, 50.0 / 65.0, 14.0 / 65.0, 62.0 / 65.0 },{ 35.0 / 65.0, 19.0 / 65.0, 47.0 / 65.0, 31.0 / 65.0, 34.0 / 65.0, 18.0 / 65.0, 46.0 / 65.0, 30.0 / 65.0 },{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 },{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 }
};//bayer dithering
inline float ditherMask2(in float2 pixel)
{return BayerMatrix2[pixel.x % 2][pixel.y % 2];
}inline float ditherMask3(in float2 pixel)
{return BayerMatrix3[pixel.x % 3][pixel.y % 3];
}inline float ditherMask4(in float2 pixel)
{return BayerMatrix4[pixel.x % 4][pixel.y % 4];
}inline float ditherMask8(in float2 pixel)
{return BayerMatrix8[pixel.x % 8][pixel.y % 8];
}inline float dither(in float2 pixel)
{return ditherMask8(pixel);
}//other
float2 dither(float2 coord, float seed, float2 size)
{float noiseX = ((frac(1.0 - (coord.x + seed * 1.0) * (size.x / 2.0)) * 0.25) + (frac((coord.y + seed * 2.0) * (size.y / 2.0)) * 0.75)) * 2.0 - 1.0;float noiseY = ((frac(1.0 - (coord.x + seed * 3.0) * (size.x / 2.0)) * 0.75) + (frac((coord.y + seed * 4.0) * (size.y / 2.0)) * 0.25)) * 2.0 - 1.0;return float2(noiseX, noiseY);
}float2 mod_dither(float2 u)
{float noiseX = fmod(u.x + u.y + fmod(208. + u.x * 3.58, 13. + fmod(u.y * 22.9, 9.)), 7.) * .143;float noiseY = fmod(u.y + u.x + fmod(203. + u.y * 3.18, 12. + fmod(u.x * 27.4, 8.)), 6.) * .139;return float2(noiseX, noiseY) * 2.0 - 1.0;
}
// *************** DITHER 抖动算法 ***************// *************** Saturate算法 ***************
bool IsSaturated(float value)
{return value == saturate(value);
}
bool IsSaturated(float2 value)
{return IsSaturated(value.x) && IsSaturated(value.y);
}
bool IsSaturated(float3 value)
{return IsSaturated(value.x) && IsSaturated(value.y) && IsSaturated(value.z);
}
bool IsSaturated(float4 value)
{return IsSaturated(value.x) && IsSaturated(value.y) && IsSaturated(value.z) && IsSaturated(value.w);
}
// *************** Saturate算法 ***************// SM 3x3 PCF
float CalcShadowFactor_PCF3x3(SamplerComparisonState samShadow,Texture2D shadowMap,float3 uvd, int smSize, float softness)
{if (uvd.z > 1.0f)return 1.0;float depth = uvd.z;const float dx = 1.0f / smSize;float percentLit = 0.0f;float2 offsets[9] ={float2(-dx, -dx), float2(0.0f, -dx), float2(dx, -dx),float2(-dx, 0.0f), float2(0.0f, 0.0f), float2(dx, 0.0f),float2(-dx, +dx), float2(0.0f, +dx), float2(dx, +dx)};[unroll]for (int i = 0; i < 9; ++i){offsets[i] = offsets[i] * float2(softness, softness);percentLit += shadowMap.SampleCmpLevelZero(samShadow,uvd.xy + offsets[i], depth).r;}return percentLit /= 9.0f;}//根据可见距离与雾级强度计算雾化指数参数
float ExponentialFog(float dist)
{float fog_dist = max(dist - fog_start, 0.0);    float fog = exp(-fog_dist * fog_density);return 1 - fog;
}float4 main(VertexOut input) : SV_TARGET
{//是否考虑阴影if (current_light.casts_shadows == 0){return 0;}//当前像素在当前视角下的场景深度float depth = max(input.PosH.z, depthTx.SampleLevel(linear_clamp_sampler, input.Tex, 2));//获取视空间下点位置float3 P = GetPositionVS(input.Tex, depth);//视空间最远点距离float3 V = float3(0.0f, 0.0f, 0.0f) - P;float cameraDistance = length(V);V /= cameraDistance;float marchedDistance = 0;float3 accumulation = 0;//定向光方向,后续点光源聚光灯等皆需要对应调整const float3 L = current_light.direction.xyz;float3 rayEnd = float3(0.0f, 0.0f, 0.0f);//沿视线方向的采样数,影响体积光的采样质量与耗const uint sampleCount = 16;const float stepSize = length(P - rayEnd) / sampleCount;// 抖动射线法来弥补采样不足:P = P + V * stepSize * dither(input.PosH.xy);// 执行ray match,沿着视图光线积分光量:  [loop]for (uint i = 0; i < sampleCount; ++i){//不同match下的阴影采样点世界系下的位置  shadow_matrix1:当前V的逆矩阵与sm中的VP矩阵结果float4 posShadowMap = mul(float4(P, 1.0), shadow_matrix1);float3 UVD = posShadowMap.xyz / posShadowMap.w;UVD.xy = 0.5 * UVD.xy + 0.5;UVD.y = 1.0 - UVD.y;[branch]if (IsSaturated(UVD.xy)){             //处理阴影衰减作用             float attenuation = CalcShadowFactor_PCF3x3(shadow_sampler, shadowDepthMap, UVD, shadow_map_size, softness);//处理雾级衰减作用 attenuation *= ExponentialFog(cameraDistance - marchedDistance);accumulation += attenuation;}//ray match +marchedDistance += stepSize;P = P + V * stepSize;}accumulation /= sampleCount;//定向体积光效果return max(0, float4(accumulation * current_light.color.rgb * current_light.volumetric_strength, 1));
}

具体流程见代码注释即可,运行后可见如下效果:

叠加之前场景后整体渲染效果如下:

至于点光源与聚光灯原理相同,以后有时间了再整吧

Global Illumination_Directional Volumetric Light (定向体积光)相关推荐

  1. [图形学] Killzone: Shadow Fall 中的体积光效果

    reference : <GPU Pro 5> 简介 体积光效果是由于光线在潮湿.多尘或烟雾环境中散射产生的(如图3.1所示).这是一个令人惊叹的自然现象,它可以用于创建壮观的图像,并为环 ...

  2. [OpenGL] 体积光效果实现

    reference: Volumetric Light Effects in Killzone: Shadow Fall [1] Interactive Rendering Method for Dis ...

  3. unity3d 求两个点长度_Unity3D实现体积光

    体积光是现实中常见的因丁达尔效应而产生的一种大气现象,文人墨客常用"慵懒的阳光泄下"描绘该现象带来的美感.笔者在一次旅游后见到了这种神奇的自然现象,遂决定在游戏中实现并使用这样的效 ...

  4. 【Unity Shader】聚光灯体积光效果的简单实现

    效果如下: Unity中的聚光灯SpotLight,可以用作手电筒,射灯等类似的效果,比如这样的 但是如果想把光束的效果做出来,就超出了SpotLight的能力范围了,本篇就为了记录一下一种简单的实现 ...

  5. unity-builtin实现体积光

    参考GitHub - SlightlyMad/VolumetricLights: Volumetric Lights for Unity这里实现的体积光会包括几个方面的影响. 制作的核心: 核心是灯光 ...

  6. RayMarching实现体积光渲染

    RayMarching实现体积光效果(平行光) 前言 本次Demo核心代码来自github https://github.com/AsehesL/VolumetricLight 本次Demo分享简化了 ...

  7. 虚幻引擎图文笔记:使用指数级高度雾(Exponential Height Fog)实现体积光

    所谓 体积光(Volumetric Lighting) 是光线在充满灰尘等杂质的空间中传播,由于漫反射产生的,好像光线经过之处形成了"体积"的现象. 效果图 基础场景 体积光实现步 ...

  8. Global Illumination_Real-Time Volumetric Cloud(实时体积云)

    实时体积云渲染可以营造出很棒的天空效果,今天我们就来看一下如何高效的生成动态体积云,至于原理,可以直接看一下地平线中体积云的技术介绍THE REAL-TIME VOLUMETRIC CLOUDSCAP ...

  9. 纯后处理的volumetric light

    转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2059 按照先前的计划,KlayGE 4.2中将加入volumetric light的效果,目 ...

最新文章

  1. spring官方文档阅读笔记
  2. 在多个游戏视图间切换环境准备
  3. http-helloworld
  4. 面试题 03.06. 动物收容所
  5. 分治法(divide conquer algorithm)的理解
  6. eslint php,eslint配置
  7. avast android高级版,排名第一 Android杀毒软件avast!评测
  8. winhex数据恢复入门教程
  9. 留言板显示服务器错误,动易Cms:解读SiteFactory 留言板出现:服务器无响应,错误代码:500-动易Cms教程...
  10. 51单片机-DS1302时钟芯片(自己理解的
  11. 这10个对冲基金策略你必须烂熟于心
  12. 海南安全员A证怎么考多选题库
  13. 【C语言函数递归】递归计算最大公约数
  14. 1265 最近公共祖先
  15. 墨尘目标检测4--yoyo3模型解析及训练自己的数据集
  16. Android自定义View之数字密码锁
  17. 云计算基础及应用 第一章 云计算基础
  18. IT项目管理痛点和应对方法总结
  19. html语言q,HTML: q 标签
  20. 英语和计算机技能,个人简历中需要突出英语和计算机的技能

热门文章

  1. 小米推送点击无效的原因
  2. 阿里云服务器可以做什么?十大使用场景举例说明
  3. Linux vi 双屏显示,如何在XFCE中设置双显示器?
  4. ElasticSerach6.0.1测试拼音分词器,IK分词器,并且次测试语法
  5. matlab转灰度图片
  6. 微信小程序 模板消息 {errcode40037,errmsginvalid template_id}
  7. 计算机联锁k5b的D5板含义,计算机联锁k5b研究.pptx
  8. Linux系统中与网络配置相关的工具
  9. Rancher2.x安装及k8s集群部署
  10. 浅谈APS精益生产排程六大要素