全局光照介绍

全局光照技术本身是一个很复杂的技术,有非常多方式实现。从管线来说有光栅化实现的全局光照,有光线追踪实现的更逼真的行为树。而我们当前主流的或者说更多人讨论的是光栅化下的全局光照。

而全局光照又包括了更多的表现,比如天气,环境等都会影响到全局光照,而每一块在引擎中可能都需要单独处理。

当前流行的引擎比如unity用的光照贴图、光照探针,反射平面等方式来模拟间接光照。而ue5用的ssgi+体素+有向距离场来做的间接光照。当前也有一些自己实现的间接光照效果。

最近对这块也挺感兴趣,参考了很多文章和论文,觉得可以用computeshader的gpu运算能力来实现一次全局实时光照效果。(当前还有一些限制,之后再优化)

先看看没有全局光照的效果

实现原理:

要得到全局光照,首先直接光照我们直接用lit算,其次间接光照我们需要考虑两块,一个是ao的信息,这里我直接用ssao的方式实现,然后就是颜色信息,我们知道一个像素他会受到旁边的多个物体或者像素的信息影响。还有一个就是在比较暗的环境下,要获取到周围的颜色信息来叠加。那么基于这几点,我们需要的是全局的法线信息,明亮环境下的颜色信息,步进的方式实现颜色获取和赋值来做间接光照。

我的方法其实还有很多空间,比如我的只有单次反射,而且我的高光没有做,也是因为我在联系全局光照的实现方式,实时全局光照还有其他更高效的方式实现,比如体素,有号距离场等,后续再补上。

我的实现步骤如下:

1.创建可以渲染对象到rt的的renderfeature

2.渲染屏幕空间的法线信息

3.渲染屏幕空间的光亮颜色信息

4.全局光照计算

4.1.computeshader清理颜色信息

4.2.computeshader渲染间接光照

4.3.blit结合间接光照信息

1.创建可以渲染对象到rt的的renderfeature

为什么需要渲染信息到rt的renderfeature呢?首先是因为我们需要一些全局的信息,比如提到的全局法线信息以及光亮颜色信息。而我们的renderfeature是继承renderobject的,是多渲染一个pass的object,是直接上色到cameracolortexture的,而我们是需要基于每个object渲染到我们自己希望的rt上的,所以就有了这个步骤。

这一步其实就是建立了一个继承ScriptableRendererFeature的RenderObjectsToRT,然他创建一个继承ScriptableRenderPass的RenderObjectsToRTPass。而这个RenderObjectsToRTPass也是针对每个object执行渲染,但是我们修改了他的渲染目标rt

cmd.GetTemporaryRT(RT.id, RTResolution.x, RTResolution.y, 8, FilterMode.Bilinear, rtf); cmd.SetRenderTarget(RT.Identifier()); cmd.ClearRenderTarget(true, true, Color.black);

这样再外面就能根据自己需要把物体渲染到对象上了。

像这里我定义passtag就是他的rt名。这样shader中就可以直接用了。

2.渲染屏幕空间的法线信息

既然有了renderobjectstort的方式,就可以添加一个feature,这个feature的materail用我们normal的mat

然后shader中深度需要写入,并且blend是OneZero

然后我们需要写入他的世界空间法线信息

这样得到的结果是

这就是我们需要的法线信息。

3.渲染屏幕空间的光亮颜色信息

上面有提到如果在场景比较暗的情况下我们要获取场景的颜色信息就不能靠直接光照的颜色信息来组成颜色了,因为直接光照已经在场景中没有颜色信息了,是一篇死黑。这时候就需要我们提前拿到一个明亮的信息来给点光源照亮环境的机会。所以我们需要这样的图片。最终的效果在比较黑暗的情况下是这样的:

可以看到他还是把点光源周围的环境照亮了。

4.全局光照计算

接下来进入computeshader实现全局光照的效果。

4.1.computeshader清理颜色信息

首先肯定要清理我的rt中的颜色,不然会残留上次的颜色。比较简单:

uint2 uv = uint2(id.x, id.y);
SDFRGBTexture[uv]= float4(0, 0, 0, 0);
IndirectLightTexture[uv] = float4(0, 0, 0, 0);

这里两个rt一个是用来现在点光源的光照信息的,一个是间接光照的信息。

4.2.computeshader渲染间接光照

这里应该是我们的渲染的重点。

首先我们会获取深度坐标,然后跟uv一起去转换裁剪空间的坐标到世界坐标,这里做法就比较传统了,就是拿世界空间到裁剪空间的逆矩阵和我们的uv以及depth计算出来的。

float4 clipPos = float4(((float2(float(uv.x), float(uv.y)) / 1024.0000)) * 2.0 - 1.0, (1 - _CameraDepthAttachment[uv].r), 1);
float4 posW = mul(_VPInvMatrix, clipPos);
posW /= posW.w;

然后我们先运算ssao,他就是找当前像素点周围圆环范围内的随机点,最后跟他的距离成反比来得到这个ao值,也是迭代越多越精确了。

float3 v_s1 = PickSamplePoint(curPos, randAddon, i);
//------------ao-----------------
v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * aoRADIUS;
v_s1 = faceforward(v_s1, -normal, v_s1);float3 newPosAOW = posW + v_s1;
float4 newScreenAOPos = mul(_VPMatrix, newPosAOW);
newScreenAOPos /= newScreenAOPos.w;
newScreenAOPos = (newScreenAOPos + float4(1, 1, 1, 1)) / 2;
newScreenAOPos = float4(newScreenAOPos.x * _ScreenRect.x, newScreenAOPos.y * _ScreenRect.y, newScreenAOPos.z, newScreenAOPos.w);
float4 newColorAOPos = ClipToWorld(uint2(newScreenAOPos.xy));//_RenderObjectsToRTFeature[uint2(newScreenAOPos.xy)] * 2000 - 1000;
float3 v_s2_AO = newColorAOPos - posW;// Estimate the obscurance value
float a1_AO = max(dot(v_s2_AO, normal), 0.0);
float a2_AO = dot(v_s2_AO, v_s2_AO) + EPSILON;
aoNum += a1_AO * rcp(a2_AO) * 2;//(_LevelID == 2 ? 2 : 2);

然后就是颜色部分了,也是在世界空间根据当前uv的位置来取方向步进一定的距离,然后拿到颜色再跟他的距离取反比来得到了。当前刚说了还得考虑完全黑的情况,所以会有一些黑暗情况下的颜色处理。然后要注意的一点是颜色一样的就不要相加了,不然会导致整个界面很糊的样子。

for (int i = 1; i <= sCount; i++){
#if defined(SHADER_API_D3D11)// This 'floor(1.0001 * s)' operation is needed to avoid a DX11 NVidia shader issue.i = floor(1.0001 * i);
#endiffloat3 v_s1 = PickSamplePoint(curPos.xy, randAddon, i);//------------ao-----------------v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * aoRADIUS;v_s1 = faceforward(v_s1, -normal, v_s1);float3 newPosAOW = curPos.xyz + v_s1;float4 newScreenAOPos = mul(_VPMatrix, float4(newPosAOW,1));newScreenAOPos /= newScreenAOPos.w;newScreenAOPos = (newScreenAOPos + float4(1.0, 1.0, 1.0, 1.0)) / 2.0;newScreenAOPos = float4(newScreenAOPos.x * _RTResolution.x, newScreenAOPos.y * _RTResolution.y, newScreenAOPos.x * _RTResolution.z, newScreenAOPos.y * _RTResolution.w);float4 newColorAOPos = ClipToWorld(uint2(newScreenAOPos.zw), newScreenAOPos.zw, 1);//_RenderObjectsToRTFeature[uint2(newScreenAOPos.xy)] * 2000 - 1000;float3 v_s2_AO = newColorAOPos.xyz - curPos.xyz;// Estimate the obscurance valuefloat a1_AO = max(dot(v_s2_AO, normal), 0.0);// *distance(_CameraPosition.xyz, posW) / 1000;float a2_AO = sqrt(dot(v_s2_AO, v_s2_AO)) + EPSILON;aoNum += a1_AO * rcp(a2_AO);//(_LevelID == 2 ? 2 : 2);v_s1 = PickSamplePoint(curPos.xy, randAddon, i);//------------ao-----------------// Make it distributed between [0, _Radius]v_s1 *= sqrt((i + 1.0) * rcpSampleCount) * RADIUS;v_s1 = faceforward(v_s1, -normal, v_s1);v_s1 = normalize(v_s1);/*uint isNextStep = 1;for (int j = 0; j < sCount; j++){if (dot(vS1floats[j], v_s1) > 0.95){isNextStep = 0;break;}}if (isNextStep == 0){continue;}vS1floats[i] = v_s1;*/for (int j = 1; j <= colNum; j+= 1){float3 newPosW = curPos.xyz + v_s1 * j;float4 newScreenPos = mul(_VPMatrix, float4(newPosW,1));newScreenPos /= newScreenPos.w;newScreenPos = (newScreenPos + float4(1, 1, 1, 1)) / 2;newScreenPos = float4(newScreenPos.x * _RTResolution.x, newScreenPos.y * _RTResolution.y, newScreenPos.z, newScreenPos.w);uint2 newUV = uint2(newScreenPos.xy);float4 newColorPos = float4(newPosW,1);//_RenderObjectsToRTFeature[newUV] * 2000 - 1000;float3 v_s2 = newColorPos.xyz - curPos.xyz;/*float vDistance = 0.1;if (abs(v_s1.y) < vDistance || abs(v_s1.x) < vDistance || abs(v_s1.z) < vDistance){continue;}*/float a2 = dot(v_s2, v_s2);if (a2 > 1){realAddNum++;float4 cameraColor = _CameraColorTexture[newUV];float3 colorValue = cameraColor.xyz - curColor.xyz;float colorLen = (colorValue.x * colorValue.x + colorValue.y * colorValue.y + colorValue.z * colorValue.z);//颜色相近就不加了if (colorLen < 0.5){continue;}float4 newColorNum = cameraColor;// *rcp(pow(2, _LevelID));if (cameraColor.r > 0.05 || cameraColor.g > 0.05 || cameraColor.b > 0.05){if (curColor.r < 0.01 && curColor.g < 0.01 && curColor.b < 0.01){newColorNum += onlyColor;// *((1 + materialProp) * 2);}}a2 += 1;color += newColorNum * rcp(colNum);//color = color / sqrt(a2);//rcp(a2 * (_LevelID == 0 ? 0.3 : (_LevelID == 1 ? 0.3 : 1.7)));// *((1 + materialProp) * 1);}}}color *= (realAddNum == 0) ? 1 : rcp(realAddNum);color *= 1 - color.a;color *= RADIUS;//color.rgb *= 20;aoNum *= aoRADIUS;// Apply contrastaoNum = PositivePow(aoNum * INTENSITY * rcpSampleCount, kContrast);color.a = 1 - aoNum;// Apply contrast//aoNum = PositivePow(aoNum * INTENSITY * rcpSampleCount, kContrast);IndirectLightTexture[uv] = color;

最后得到的效果是

这个就是间接光照的效果,可以看到他会产生很多锯齿,如果就这样展示整个界面还是有点问题的

可以看到锯齿很明显,那么我们就得抗锯齿,抗锯齿得方式很多,比如fxaa,taa等。我选择的是直接模糊,因为他的消耗还能接受而且效果也不错。

4.3.blit结合间接光照信息

最后我们肯定要把间接光照结合直接光一起展示,所以我再blit上做,当然如果有后处理,再那边也要做一遍。

总结:

整体就是上面的思路。最后说一下我是建立了一个叫GlobalLight的ScriptableRendererFeature,再里面通过commandbuffer组织computeshader来实现的。

CommandBuffer cmd = CommandBufferPool.Get(profilerTag);
using (new ProfilingScope(cmd, m_ProfilingSampler))
{cmd.Clear();Camera currentCamera = renderingData.cameraData.camera;var vpMatrix = currentCamera.projectionMatrix * currentCamera.worldToCameraMatrix;giComputeShader.SetMatrix(VPMatrixID, vpMatrix);giComputeShader.SetMatrix(VPInvMatrixID, vpMatrix.inverse);giComputeShader.SetVector(ScreenRectID, new Vector2(Screen.width, Screen.height));Vector3 screenLightPos = currentCamera.WorldToScreenPoint(AreaLight.areaLightPos);Vector3 prevScreenLightPos = currentCamera.WorldToScreenPoint(AreaLight.prevLightPos);giComputeShader.SetTexture(ClearKernel, IndirectLightTexturert, IndirectRT);giComputeShader.SetTexture(ClearKernel, SDFRGBTexturert, outputRGBRT);giComputeShader.SetVector(PrevScreenAreaLightPos, prevScreenLightPos);cmd.DispatchCompute(giComputeShader, ClearKernel, 32, 32, 1);giComputeShader.SetVector(ScreenAreaLightPos, screenLightPos);giComputeShader.SetVector(WorldLightPos, AreaLight.areaLightPos);giComputeShader.SetTexture(kernel, SDFRGBTexturert, outputRGBRT);cmd.SetComputeTextureParam(giComputeShader, kernel, cameraDepthTexture.id, cameraDepthTexture.Identifier());cmd.DispatchCompute(giComputeShader, kernel, 32, 32, 1);giComputeShader.SetTexture(IndirectLightkernel, IndirectLightTexturert, IndirectRT);cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, onlyColorRTFeature.id, onlyColorRTFeature.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, outputNormalRT.id, outputNormalRT.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, cameraOpaqueTexture.id, cameraOpaqueTexture.Identifier());cmd.SetComputeTextureParam(giComputeShader, IndirectLightkernel, cameraDepthTexture.id, cameraDepthTexture.Identifier());cmd.SetComputeVectorParam(giComputeShader, cameraPos, currentCamera.transform.position);cmd.SetComputeIntParam(giComputeShader, qLevelID, (int)mEQLevel);cmd.SetComputeIntParam(giComputeShader, rayDistanceID, mRayDistance); cmd.DispatchCompute(giComputeShader, IndirectLightkernel, 32, 32, 1);{giComputeShader.SetTexture(RGBkernel, IndirectLightTexturert, IndirectRT);giComputeShader.SetTexture(RGBkernel, IndirectLightTexturert, IndirectRT);cmd.DispatchCompute(giComputeShader, RGBkernel, 32, 32, 1);}AreaLight.prevLightPos = AreaLight.areaLightPos;cmd.EndSample(profilerTag);
}

computeshader实现全局光照相关推荐

  1. Unity 5 中的全局光照技术详解

    简介 全局光照,简称GI,是一个用来模拟光的互动和反弹等复杂行为的算法,要精确的仿真全局光照非常有挑战性,付出的代价也高,正因为如此,现代游戏会先一定程度的预先处理这些计算,而非游戏执行时实时运算. ...

  2. 基于RadeonRays的光线追踪全局光照实现方案

    基于 RadeonRays 的光线追踪全局光照实现方案 最近半年一直在做全局光照方面的工作,陆续实现了辐射度算法和光线追踪两套方案,最终由于辐射度算法的局限性(只能基于漫反射)还是使用了光线追踪的方案 ...

  3. VR有五种全局光照渲染引擎和各自的优缺点

    来自: 喜欢(有些日子总是令人无法淡忘) 2008-10-20 15:53:26 VR有五种全局光照渲染引擎:  Irradiance map(发光帖图)  Photon map(光子帖图)  QMC ...

  4. 全局光照技术解析Global Illumination Explained

    解析全局光照Global Illumination Explained 前言:Global Illumination全局光照技术是实时渲染的必然发展方向.我参考了一些研究成果,琢磨了一下,让更多的人可 ...

  5. NVIDIA的黑科技3:VXGI体素全局光照

    每一个行业都有自己的"圣杯",例如能源方面的核聚变.医药方面的癌症特效药以及空间探索方面的超光速推进力. 任何领域中"圣杯"的定义都是难以实现和代价高昂的,或者 ...

  6. UE3 Lightmass静态全局光照

    文档变更记录: 由 Daniel Wright 创建. Lightmass静态全局光照 概述 版本 遗留支持 在新的地图中使用旧的UE3光照 转换一个现有地图来使用Lightmass Lightmas ...

  7. Q88:全局光照(Global Illumination)——Path Tracing

    88.1 引入(Introduction) 截至当前,回忆一下我们学过的针对直接光照和间接光照的不同反射模型. 直接光照: Phong反射模型.包含漫反射部分和高光反射部分. 间接光照: 对于镜面材料 ...

  8. GAMES101课程学习笔记—Lec 14(2)~16:Ray Tracing(2) BRDF、渲染方程、全局光照、路径追踪

    GAMES101课程学习笔记-Lec 14(2)~16:Ray Tracing(2) BRDF.渲染方程.全局光照.路径追踪 0 引入--辐射度量学概述 1 相关概念 1.1 Radiant Ener ...

  9. Unity Shader 学习笔记(33) 全局光照(GI)、反射探针、线性空间和伽马空间、高动态范围(HDR)

    Unity Shader 学习笔记(33) 全局光照(GI).反射探针.线性空间和伽马空间.高动态范围(HDR) 参考书籍:<Unity Shader 入门精要> [<Real-Ti ...

  10. Godot 4.0中的基于有向距离场SDF(Signed Distance Field)的实时全局光照技术

    原文地址 Godot官网 <Godot 4.0 gets SDF based real-time global illumination> 作者 Juan Linietsky 6月28日 ...

最新文章

  1. tomcat环境部署
  2. Qt实用技巧:使用OpenCV库的视频播放器(支持播放器操作,如暂停、恢复、停止、时间、进度条拽托等...
  3. jstack和线程dump分析
  4. jsonproperty注解_Jackson注解详解
  5. Tableau系列之构建和浏览数据视图
  6. Alibaba-AndFix Bug热修复框架的使用
  7. 10道腾讯的Java面试题
  8. 使用kubeadm安装kubernetes高可用集群
  9. 工程师追查线上问题(或运维)常用的shell命令
  10. 泛微oa系统什么框架_泛微移动办公OA系统走进江苏国曜信息科技有限公司
  11. python 生成器_python 生成器 - 刘江的python教程
  12. C# 从类库中获取资源图片,把图片资源保存到类库中
  13. 网易云音乐歌曲带时间轴歌词的提取
  14. Pr 视频效果:颜色校正
  15. UNITY 对话系统
  16. 苹果拍照怎么显示地点和时间_手机拍照自带功能,照片上能添加时间和地点?一键按下搞定...
  17. 关于AD之PCB各层的简单说明
  18. unity3d 如何UI优化和减少DC(DrawCall)
  19. VMware下载安装教程(超详细)
  20. 18. SPI协议,spi转can,MCP2515裸机驱动详解

热门文章

  1. edius隐藏快捷键_EDIUS7 Pro快捷键使用方法及全部快捷键功能
  2. NVMe PM951 硬盘写入速度优化
  3. 动视暴雪消费产品集团首次亮相2018年中国授权展
  4. Linux查看JDK版本和安装位置
  5. ArcMap 导入 wrl_飞时达总图软件GPCADZ三角曲面模型导入三维配管PDMS软件
  6. iPhone手机上最不起眼的5款APP,用过的人都赞不绝口!
  7. 金山发布数字办公平台
  8. Taro使用wxParse富文本组件
  9. 几家大的券商的PB系统以及算法交易概况大致是怎样的?
  10. mysql查询市区县_通过数据库获取省份城市区县的名字