赞
踩
十一放假很开心,正好赶上观望了了许久的《尼尔·机械纪元》打折啦。窝在家里搞了三天三夜,终于E结局通关啦!!!真的好久没玩过这么好玩的游戏了,于是乎我的废话应该会多不少,毕竟,写blog的另一个目的就是记录玩过的好玩的游戏,2333。
最开始听说这个游戏的时候,只是被2B小姐姐的人设吸引了,毕竟小姐姐还是很漂亮的。而且游戏类型也是我喜欢的类型,看起来打击感还不错,加上最近打《地铁》和《消失的光芒》玩出3D眩晕症了,就差嗑两片晕车药再打了,正好搞一个动作型的游戏换换口味。
玩起来我才发现游戏的音乐太好听啦,最近简直每天在循环停不下来,场景也是体积光,SSAO之类的高级效果大量使用。
但是游戏通关之后,我才发现游戏的故事本身就很让人深思,加上丰富的支线剧情。我不敢说玩的游戏很多,但是的确实本人最近玩过的画面,音乐,战斗,剧情都很给力的一款游戏了。
好在三周目打完之后,加上读档几次,终于打出了E结局,还算比较完美。然而游戏竟然有26个结局,等到重温时争取都打出来。
唉,一不小心就忍不住想贴贴贴,毕竟游戏太好玩了,但是到此为止了。我不想剧透,下面才是本文的正题。
《尼尔·机械纪元》中有一个关卡--复制之街。刚一进这个场景,我不由得发出一声惊叹,“我靠,这场景贴图是不是丢了?”
不过这个场景风格也是蛮不错的,同时也让我想起了一个略微进阶一些的图形技术,环境光遮蔽(AO)。整个场景看起来没有表示颜色的albedo,但是场景的阴影效果和AO效果还是存在的,这让场景的层次细节在即使没有颜色的情况下也可以展现出来,形成了一种特殊的风格。
环境光遮蔽对效果的提升有多重要,看一下顽皮狗在《Uncharted 2: HDR Lighting》的一个对比图,可以看到,左侧的车底遮挡了大部分光线,形成了阴影看起来很自然,而右侧的车感觉就像飘在上面一样,看起来比较假:
环境光遮蔽(Ambient Occlusion),最经常听到的应该是它的缩写AO。既然名字本身就带Ambient,说明其本身是对于环境光强度的一种控制,所以有必要来先了解一下环境光的计算。
光照是可以线性叠加的,一般来说最终的光照结果 = 直接光照 + 间接光照。我们计算物体的直接光照效果时,可以直接通过BRDF计算,而环境光属于间接光照,要想计算真正的环境光,需要在该点法线方向所对应的半球积分计算,在离线渲染的情况下也只能通过蒙特卡洛积分等方式近似计算,对于光线追踪的方式渲染的情况,自然可以得到比较好的效果,但是即使现在的RTX似乎也不能真正地实时跑光线追踪,所以在实时渲染领域,环境光一般使用的就是环境贴图(SkyBox,Reflection Probe),球谐光照(Spherical Harmonic Lighting),光照贴图(Light Map,需要用离线烘焙),甚至直接加一个固定的环境光值(简单粗暴,比如Unity中的UNITY_LIGHTMODEL_AMBIENT宏)。普通光源的遮挡效果也就是阴影,我们可以通过Shadow Map,模板阴影等来实现,但是对于环境光的遮挡效果,半球上的光线自然没有方法用普通的Shadow Map方式来计算了。所以研究怎样遮挡环境光的强度的就叫环境光遮蔽。
环境光遮蔽主要用来控制物体和物体相交,夹角,褶皱等位置遮挡漫反射光线的效果,简单来说就是某一点对于环境的暴露比例,如果是平面,那么没有遮蔽;如果是夹角,褶皱等那么周围的面就会遮蔽一部分环境光,就导致该点的环境光相对较弱。如果环境光没有遮蔽效果,那么不管褶皱还是平面,环境光照结果是一致的。而环境光遮蔽可以使褶皱,夹角等位置的光照效果变弱(比如一根管子,在管口的位置应该比较亮,而越向内,应该越暗),提高暗部阴影效果达到一种近似自阴影的效果,提升画面的层次感,增加细节。
在了解了环境光遮蔽的基本概念之后,本文主要实现几种主流的环境光遮蔽效果,AO贴图(使用预烘焙的贴图,实现离线的基于GPU的烘焙AO贴图的工具),SSAO(屏幕空间环境光遮蔽),HBAO(水平基准环境光遮蔽)。
首先看一下最简单的AO贴图的使用,这也是性能最好的方式,但是这并不代表这种方法整体性能好,只是在运行时使用了预计算的结果。而AO贴图的生成是使用光线追踪的方式,反而是这几种AO方式种耗时最长但是效果相对更好的一种,毕竟只要一离线,时间什么的都是次要的。
AO贴图技术已经比较古老了,现有的各种3D软件基本都支持AO的烘焙,如3dsMax,Maya等。我今天使用的是Substance Painter,这个功能很强大的软件,而且相比于前两者,烘焙比较方便,但是据说效果没有前两者好。不过这些都不重要,毕竟怎么烘焙,那是美术同学的事情。
使用Substance Painter的烘焙选项,支持直接烘焙Mesh,烘焙面板如下:
我们用一个小狮子的模型导入Substance Painter中,然后使用低模烘焙一发AO贴图,同时工具也支持带有法线贴图的低模烘焙AO贴图,可以把法线细节的AO效果也烘焙出来。烘焙的贴图如下,左侧为直接烘焙,右侧为带有法线贴图之后烘焙的AO效果:
如果抓帧哪个游戏看到某个类似的通道,在褶皱处偏黑的,可能就是AO贴图啦。得到AO贴图之后,下面就需要看一下AO贴图的使用了。
上面说过,光照是可以线性叠加的,全局光照 = 直接光照 + 间接光照。Unity也不例外,下面是Unity官方Shader的光照叠加部分:
- half3 color = diffColor * (gi.diffuse + light.color * diffuseTerm)
- + specularTerm * light.color * FresnelTerm (specColor, lh)
- + surfaceReduction * gi.specular * FresnelLerp (specColor, grazingTerm, nv);
不考虑菲尼尔项的话,就是direct diffuse + direct specular + gi diffuse + gi specular,前两者通过BRDF计算,而后两者就是所谓的环境光,我们看一下Unity官方的GI Shader源代码:
- inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld)
- {
- UnityGI o_gi;
- ResetUnityGI(o_gi);
-
- // Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
- #if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
- half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
- float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
- float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
- data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
- #endif
-
- o_gi.light = data.light;
- o_gi.light.color *= data.atten;
-
- #if UNITY_SHOULD_SAMPLE_SH
- o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
- #endif
-
- #if defined(LIGHTMAP_ON)
- // Baked lightmaps
- half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
- half3 bakedColor = DecodeLightmap(bakedColorTex);
-
- #ifdef DIRLIGHTMAP_COMBINED
- fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
- o_gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);
-
- #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
- ResetUnityLight(o_gi.light);
- o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap (o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
- #endif
-
- #else // not directional lightmap
- o_gi.indirect.diffuse += bakedColor;
-
- #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
- ResetUnityLight(o_gi.light);
- o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
- #endif
-
- #endif
- #endif
-
- #if
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。