UnityShader——挺进体积光
原本是想找找体积雾的,无意中发现了 GPU Gems 3上的一篇屏幕特效实现体积光散射的文章,实现了一波发现文章里公式列的很高大上,结果具体实现却出乎意料的简单,只是通过像素的屏幕位置和光源的屏幕位置计算光线方向,然后在一个循环中沿光线方向将上一个像素的颜色衰减后叠加到下一个像素,具体可参考 GPU Gems 原文,在 Unity 中效果如下:
周末又在Github发现一个500多星星的体积光,项目主要参考了 GPU Pro 5 上的一个体积光实现方法,效果很棒,忍不住下下来研究了一下
我们从最简单并具有代表性的一种情况入手,即当相机在点光源范围外的时候,效果如上图所示,通过绘制一个和点光源范围一样的球,通过raytrace的方法遍历球中的位置并计算其光照,raytrace 路径如图所示,我们只需要遍历光线在球内部的位置:
实际用使用的代码如下:
float3 rayStart = _WorldSpaceCameraPos;float3 rayEnd = i.wpos;float3 rayDir = (rayEnd - rayStart);float rayLength = length(rayDir);rayDir /= rayLength;float3 lightToCamera = _WorldSpaceCameraPos - _LightPos;float b = dot(rayDir, lightToCamera);float c = dot(lightToCamera, lightToCamera) - (_VolumetricLight.z * _VolumetricLight.z);float d = sqrt((b*b) - c);float start = -b - d;float end = -b + d;
光看代码有点不好理解,示意图如下:
我们以 CC 表示相机所在点,LL 表示点光源所在点,根据上面代码我们可以看出,
-b=CA,
c=CL^2-DL^2,
d^2 = b^2-c = CA^2-CL^2+DL^2=DL^2-(CL^2-CA^2)=DL^2-LA^2=BL^2-LA^2=AB^2
故 d=ABd=AB(上面的计算完全就当高中几何算了就没管方向了)
因此,在点光源范围内没有遮挡物的情况下 -b-d 和 -b+d 分别就是raytrace的起点和终点,再通过 _CameraDepthTexture 计算遮挡物位置并与当前的 end 做比较取最小值,然后,我们就可以做 raytrace了,对于点光源,光照计算较为简单,光线的衰减 Unity 已经帮我们算好了,其具体实现在 Wiki 中也有,然后是阴影,对于点光源,其 ShadowMap 以 texCUBE 的形式存储, UnityShadowLibrary.cginc 文件中,我们可以看到其实现:
#if defined (SHADOWS_CUBE)samplerCUBE_float _ShadowMapTexture;
inline float SampleCubeDistance (float3 vec)
{#ifdef UNITY_FAST_COHERENT_DYNAMIC_BRANCHINGreturn UnityDecodeCubeShadowDepth(texCUBElod(_ShadowMapTexture, float4(vec, 0)));#elsereturn UnityDecodeCubeShadowDepth(texCUBE(_ShadowMapTexture, vec));#endif
}
inline half UnitySampleShadowmap (float3 vec)
{float mydist = length(vec) * _LightPositionRange.w;mydist *= 0.97; // bias#if defined (SHADOWS_SOFT)float z = 1.0/128.0;float4 shadowVals;shadowVals.x = SampleCubeDistance (vec+float3( z, z, z));shadowVals.y = SampleCubeDistance (vec+float3(-z,-z, z));shadowVals.z = SampleCubeDistance (vec+float3(-z, z,-z));shadowVals.w = SampleCubeDistance (vec+float3( z,-z,-z));half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;return dot(shadows,0.25);#elsefloat dist = SampleCubeDistance (vec);return dist < mydist ? _LightShadowData.r : 1.0;#endif
}#endif // #if defined (SHADOWS_CUBE)
GitHub 中的项目选择了手动计算衰减,而实际上我们直接使用 UNITY_LIGHT_ATTENUATION 宏也可以达到一样的效果,在这个衰减的基础上,我们可以再手动加上一个衰减系数方便效果调整。
然后是散射,我们之所以能看见“体积光”就是因为光线在介质中发生了散射,在点光源中,我们可以简单的设置一个系数来决定raytrace的每一步有多少光源散射到了摄像机方向,到了这一步,我们便可以得到下面的结果:
图中的结果是直接在光源位置放置了一个等大小的 Sphere,但实际上还存在一个很大的问题,要产生阴影,我们则不能将其渲染队列设置为透明,但在 Geometry 的渲染队列绘制,如果我们渲染天空盒,则体积光便会被天空盒覆盖掉,如图所示:
把渲染队列调整至 Transparent 之后则是这样的:
因此,Github 中的项目使用了 commandbuffer 在 Unity 渲染完 ShadowMap 后,绘制不透明物体之前绘制体积光的Sphere 并将rendertarget 设置为自定义的 rendertexture,最后再通过ImageEffect的方式,将体积光混合到当前的 framebuffer 中
当然,上面提到的都是项目中比较基础的部分,Github 项目中还使用了 Deithered Offset 的方法来使 raytrace 的采样更加均匀,可以在使用更少的步数的情况下,达到更好的效果
以及先降采样再通过模糊和超采样还原的方法来减少运算量
这里不再一一分析,有兴趣的可以查看 GPU Pro 原文或者阅读 Github 的项目源码
to be continue… or not
UnityShader——挺进体积光相关推荐
- 【UnityShader】光线追踪体积光
最近尝试实现了一下光线追踪体积光,效果如下: 光线追踪(Ray tracing)是三维计算机图形学中的特殊渲染算法,跟踪从眼睛发出的光线而不是光源发出的光线,通过这样一项技术生成编排好的场景的数学模型 ...
- UnityShader 实现简单的体积光
现实中的体积光效果: 在生活中,我们常会看到类似这样的效果,也就是丁达尔效应. 其他游戏中的使用 例如深海迷航中的使用(P.S. 游戏真的很好玩,续集的零度之下也是非常nice的) Unity中的简单 ...
- UnityShader实战 之 体积光的实现
大家好,我是Zander. 今天我们用Shader来模拟体积光.先看一下效果: 这是在ShadowGun场景里的一个实现效果. Shader代码如下: Shader "Custom/Blin ...
- UnityShader实现简单的体积光
欢迎来到我的博客 又开始疯狂摸鱼了,药丸药丸!!!真是是间歇性踌躇满志,持续性混吃等死啊. 本篇文章用两种比较简单的方法实现了体积光,因为简单,所以瑕疵还是有的. 上图是百度百科的丁达尔效应那弄来的图 ...
- unity3d 求两个点长度_Unity3D实现体积光
体积光是现实中常见的因丁达尔效应而产生的一种大气现象,文人墨客常用"慵懒的阳光泄下"描绘该现象带来的美感.笔者在一次旅游后见到了这种神奇的自然现象,遂决定在游戏中实现并使用这样的效 ...
- 【Unity Shader】聚光灯体积光效果的简单实现
效果如下: Unity中的聚光灯SpotLight,可以用作手电筒,射灯等类似的效果,比如这样的 但是如果想把光束的效果做出来,就超出了SpotLight的能力范围了,本篇就为了记录一下一种简单的实现 ...
- webgl径向模糊实现体积光
体积光介绍 首先,我们要确认一下什么是体积光.体积光通俗来说是我们能看见的"光路",并不是所有灯光都会形成体积光效果,它是光照到大气中粒子散射后得到的效果(丁达尔效应).我们有时候 ...
- 体积光 GodRay
1.灯光控制系统 体积光属于一种室内灯光控制系统,其主要呈现一种灯光洒过某种介质后在物体周围所形成的一种光泽. 体积光的渲染方法: 先打开一盏主光灯,调好主光灯的参数,原地复制一个出来(但这时注意不要 ...
- Unity Shader学习:体积光/体积阴影
Unity Shader学习:体积光/体积阴影 在前向渲染下实现平行光的体积光影效果,需要全屏深度图,延迟渲染会更划算. 思路:通过ray marching的步进点位置计算该点是否在阴影中,采样阴影贴 ...
最新文章
- 从0 开始 DIY你的Arduino UNO
- xcode armv6 armv7 armv7s arm64
- Linux学习总结(七十)docker-2
- Android优化——UI优化(二) 使用include标签复用布局
- UltraEdit常用快捷键
- 数据库的查询优化建议整理
- 190829课堂母版与子版
- Flutter State生命周期 Flutter Widget生命周期 Flutter 应用程序生命周期
- 浅谈equals和hashcode
- Tips--Solidworks 2016绘制工程图时显示gtol.sym文件缺失的解决方法
- python程序调用函数的过程是什么_Python:函数定义和调用时都加*,有什么作用?...
- python knnmatch_opencv python 特征匹配
- Error creating bean with name 'userServiceImpl': Injection of autowired dependencies failed
- 关于exe应用程序做成Windows服务爬过的坑
- 190717每日一句
- java pdf 分页_java操作PDF文件,可支持分页、合并、图片转PDF等
- java种语言包在线翻译_Java 实现在线翻译功能 调用微软Bing API
- 《SAFe 4.0参考指南:精益软件与系统工程的规模化敏捷框架》SAFe基础
- python 基类是什么,python 基类是什么意思
- 黑群晖二合一安装不了套件_家庭NAS部署指南(二)——如何自己动手安装一台黑群晖主机...