unity GI Shader 实现

这篇具有很好参考价值的文章主要介绍了unity GI Shader 实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

之前分享了一篇对unity全局光照的解析,里面提到了一些东西,需要在Shader内实现,在这一篇补上。
要实现对全局GI的shader实现,我们可以通过对unity内置的Lit进行解析查看。

烘焙的方式有很多种,选择合适的方式烘焙和使用合适类型的光源尤为重要。

首先,我们先实现一下最基础的烘焙光照显示,就是将Light Map贴图显示出来。需要设置的是,将需要烘焙的模型全设置成静态,光照设置为Baked,并且模型开启了第二套LightMap UV。
unity GI Shader 实现
准备工作结束。

纯烘焙,无动态

光照全部为静态的,照明模式将不会起作用,因为它选择哪个烘焙都一样,将直接光,间接光和阴影都烘焙到Light Map上面。
那么开始写shader,首先要开启宏

#pragma multi_compile _ LIGHTMAP_ON //光照贴图支持

获取LightMap的UV

float2 staticLightmapUV : TEXCOORD1;

LightMapUV是第二套

output.staticLightmapUV = input.staticLightmapUV * unity_LightmapST.xy + unity_LightmapST.zw;

然后计算完UV偏移以后,传递到片元着色器
在片元着色器中,我们只需要和之前的SH进行一个宏的判断,用来对间接光漫反射进行修改。

#if defined(LIGHTMAP_ON)
    float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_Lightmap,samplerunity_Lightmap,input.staticLightmapUV);
    Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));
#else
 Irradiance = SampleSH(WorldNormal);//SH,Light Probe
#endif

首先是对宏进行判断,然后读取LightMap贴图,读取到的贴图是被编码过的,所以还需要一步解码,这个是unity内置的功能,所以可以直接使用内值的变量去解码。如果当前没有开启LIGHTMAP_ON,我们还可以按照之前的那种SH方法去获取间接光漫反射。

在这种模式下,动态模型接收的是Light Probe的漫反射光照,因为没有主光源,实时渲染时也没有主光源进行渲染。
unity GI Shader 实现
接下来兼容一下Lightmap方向贴图。

#pragma multi_compile _ DIRLIGHTMAP_COMBINED //方向贴图

首先要设置宏,用来判断是否开启方向贴图。
然后再LightMap中判断方向贴图,去设置方向贴图

half3 Irradiance = half3(0,0,0);
#if defined(LIGHTMAP_ON)
    float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_Lightmap,samplerunity_Lightmap,input.staticLightmapUV);
    Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));
    #if defined(DIRLIGHTMAP_COMBINED)
        float4 direction = SAMPLE_TEXTURE2D(unity_LightmapInd,samplerunity_Lightmap,input.staticLightmapUV);
        half3 LightDir = direction * 2.0f - 1.0f;
        half halfLambert = dot(WorldNormal,LightDir) * 0.5 + 0.5;
        Irradiance = Irradiance * halfLambert / max(1e-4,direction.w);
    #endif
#else
 Irradiance = SampleSH(WorldNormal);//SH,Light Probe
#endif

注意,unity_Lightmap是内置LightMap,unity_LightmapInd是内置的方向贴图名称,samplerunity_Lightmap是公用的采样器。
那么,有个问题,我们有了光照的方向,法向以及视角方向,是不是可以计算LightMap模型的高光效果,答案是肯定的。不管是blinnPhong还是使用GGX高光数据都够,那肯定能直接计算出来。
以下是示例代码:

#if !defined(_DIRHIGHLIGHT_OFF)
    half BlinnPhong = pow(saturate(dot(WorldNormal,normalize(LightDir + ViewDir))),30); //计算出高光强度
    Irradiance = Irradiance + Irradiance * BlinnPhong; //高光和漫反射相加
#endif

特点:

  1. 场景中的光照全靠Light Map,Light Probe,Refletion Probe提供。没有实时光源,节约性能。
  2. 所有物体缺少动态高光,静态物体勉强可以使用方向贴图获取朝向计算,动态物体的话只能使用其它方式实现。
  3. 动态物体不能产生投影。

总结:纯烘焙模式,由于其性能优秀,一般会采用在移动端固定视角类型的游戏上,动态物体就使用其它方式比如全局设置一个光的朝向等方法实现。动态物体无阴影,一般采用假阴影,比如平面阴影那种,还是比较适合移动端。
使用推荐:移动端2.5D俯视角游戏。

使用Mixed混合光照烘焙

之前说过,使用了Mixed混合光源以后,会额外的生成一组阴影贴图,也就是静态阴影贴图,然后根据我们设置的光照模式,有不同的区别。

Subtrative模式

Mixed灯光对LightMap物体无效,通过light.distanceAttenuation来屏蔽Mixed光源,如果是使用LightMap的物体,这个值将为0,避免的重复着色。
在这个模式下,静态物体的直接光,间接光,阴影都被烘焙到LightMap和LightProbe上面,动态物体还是会被Mixed光源照射,进行实时计算。这种方式是一种非常合理的设置方式,静态物体固定,用LightMap计算即可,动态物体刚好使用Mixed光照实时渲染,漫反射使用LightProbe。但是还是有几个问题需要解决,如果能解决,这将适合大多数移动端平台性能最优方案:

  1. 动态物体无法投射阴影到静态物体身上。
    解决方案:
//SUBTRACTIVE模式下的混合光照,用于处理实时光照和光照贴图的混合
#if defined(LIGHTMAP_ON) && defined(_MIXED_LIGHTING_SUBTRACTIVE)
    #if defined(_MAIN_LIGHT_SHADOWS_SCREEN)
        float4 positionCS = TransformWorldToHClip(WorldPos);
        float4 ShadowCoord = ComputeScreenPos(positionCS);
    #else
        float4 ShadowCoord = TransformWorldToShadowCoord(WorldPos);
    #endif
    float4 ShadowMask = float4(1.0, 1.0, 1.0, 1.0);
    Light mainLight = GetMainLight(ShadowCoord, WorldPos, ShadowMask);
    Irradiance = SubtractDirectMainLightFromLightmap(mainLight, WorldNormal, Irradiance);
#endif

这种方式是在对间接光源的辐射度里面去处理,从名字SubtractDirectMainLightFromLightmap就可以看出,是从光照贴图冲减去主光源,属于trick的方式。
首先还是要获取到平衡光主光源,这种方式只适合平衡光主光源。
上面的宏_MIXED_LIGHTING_SUBTRACTIVE是在unity内不声明的,只需要判断即可。
对于其的实现原理,我们可以查看源码学习一下:
unity GI Shader 实现
从效果上看,在移动端是可以接受的:
unity GI Shader 实现
2. 动态物体的实时光照无法被静态物体遮挡。
解决方案:就是在获取实时光源时,通过宏进行区别。

//ShadowMask是用来处理静态投影和动态投影的结合
#if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
    half4 ShadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
#elif !defined(LIGHTMAP_ON)
    half4 ShadowMask = unity_ProbesOcclusion;
#else
    half4 ShadowMask = half4(1, 1, 1, 1);
#endif

以上是获取ShadowMask的方式,如果使用光照烘焙,则直接获取ShadowMask的贴图(如果有的情况下)。如果没有LIGHTMAP_ON,那就是实时渲染,则在Light Probe上面去获取遮挡关系,遮挡关系我们也可以通过编辑器查看:
unity GI Shader 实现
设置显示全部Light Probe,然后显示遮挡,会发现在阴影处的Light Probe明显比如在亮光处的Light Probe要暗。
unity GI Shader 实现
unity的内置处理是一个叫MainLightShadow的函数:
unity GI Shader 实现
这种方式是在移动端平台最推荐的一种方式,静态物体单纯的从Light Map上面获取直接光,间接光和阴影,能够实时从动态物体上面获取实时阴影进行混合。动态物体能够实时计算直接光,并且能够通过烘焙的Light Probe实时更新被场景遮挡的关系,性能和效果达到的一种均衡。
还有一些问题:角色被阴影遮挡没有实时遮挡那么明确,实时渲染阴影和烘焙阴影之间融合的也不好。如果移动平台的话,勉强够用。毕竟其它方式更耗。

ShadowMask模式

如果目标是主机或者pc,那就不用这么拮据了,可以使用ShadowMask模式,这种模式和上面的Subtrative的区别在于,烘焙时不会将阴影直接烘焙到LightMap上面,而是单独生成一份ShadowMask贴图,用于阴影遮挡关系。这样,就不用使用那种trick的方式计算阴影,而是直接两个阴影直接融合,并且动态物体身上也能够获取到烘焙的阴影的影响。但是你会发现,烘焙出来的LightMap的体积会增加一倍,这也算一个缺点。
unity GI Shader 实现
你会发现每个贴图都有对应的一张shadowmask,ShadowMask模式下还有两种模式,之前也说过,
unity GI Shader 实现
在质量里面可以设置ShadowMask的模式,ShadowMask就是静态物体纯用shadowmask贴图渲染阴影,也能和动态物体很好的融合。但是Distance Shadowmask,它会渲染两遍,也就是实时阴影和shadowmask阴影都渲染,最后再来一个融合。
unity GI Shader 实现
上图为Distance Shadowmask的渲染阴影的批次。
unity GI Shader 实现
上图为shadowmask模式的批次。明显的看出少了几十个pass。所以,还是那句话,更好的效果需要更多的性能。
使用Distance Shadowmask,你的静态物体会进行两次阴影渲染(shadowmask贴图的和实时渲染)最后进行融合的,而且还需要shadowCasterPass渲染。
而Shadowmask,相对于Subtrative模式,解决了动态阴影和静态阴影融合的问题,代价是lightmap体积翻倍。

Baked Indirect模式

这个模式,就是将间接光以及烘焙光都烘焙到LightMap上面,直接光还是实时渲染的。我们只需要按照前面,在间接光里面去获取LightMap去替换SH即可。

实时渲染LightMap

Realtime Lighting和Mixed Lighting是可以共用的,实时的主要解决实时光源的漫反射弹射效果,而烘焙的,可以直接处理Baked光源以及区域光Area Light。
unity GI Shader 实现
如何混合呢,我们可以看到unity内置的代码,它兼容了动态LightMap和静态LightMap,通过宏,进行判断你的lightmap是否需要计算。它们之前没有重叠的部分,因为动态LightMap是存储的动态光照的漫反射信息,而静态LightMap是存储的静态光照的漫反射信息。所以它们之间相加即可。我们之前也计算了静态LightMap,那么动态LightMap的UV如何获取。

首先要获取到dynamicLightmapUV,这个需要第三套uv。

float2 dynamicLightmapUV : TEXCOORD2;

顶点着色器计算UV

output.dynamicLightmapUV = input.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;

获取动态光照LightMap:

float4 encodedIrradiance = SAMPLE_TEXTURE2D(unity_DynamicLightmap,samplerunity_DynamicLightmap,input.dynamicLightmapUV);
Irradiance = DecodeLightmap(encodedIrradiance, float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h));

这样就自己实现,如果没有自定义的要求,我推荐,还是直接使用SampleLightmap函数就行了。

这种双管齐开的方式,是最好的效果,推荐最高的效果上开启即可,毕竟用了两套烘焙系统,即耗性能又耗内存,但是效果会好很多,因为你的动态光源和静态光源都得到间接光漫反射的计算,让效果更真实。
推荐:主机和电脑高画质开启。

插件推荐

Bakery 替换内置的烘焙GI,效率还高
Magic Lightmap Switcher 可以实现LightMap贴图的切换
Magic Light Probes 可以帮你轻松快速的布置光照探测器LightProbe文章来源地址https://www.toymoban.com/news/detail-496154.html

到了这里,关于unity GI Shader 实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • unity shader 实现通用描边shader -文字描边-字体描边

    在制作游戏时,可以遇到要对字体添加描边的需求,unity 的UGUI自带的OutLine组件,描边效果不好,宽度过大会出现穿帮,顶点数量也会增加,性能不好,如果对于有几百字,顶点数量会很多,而且无法扩展功能 可以看出Outline创建了4个方向的文字 Unity5.2以前的版本要求,每一个Canvas下至多

    2024年02月03日
    浏览(106)
  • 【Unity Shader】Unity中利用GrabPass实现玻璃效果

    《入门精要》中模拟玻璃是用了Unity里的一个特殊的Pass来实现的,这个Pass就是 GrabPass ,比起上一篇博客实现镜子的方法,这个方法我认为相对复杂,因此在实现之前需要对GrabPass及实现原理做一个更加详细的介绍。 场景物体拜访和贴图完全参考《入门精要》: 以及当前场景

    2024年02月09日
    浏览(50)
  • Unity用Shader实现边缘光效果

    《自学记录》 1、先创建一个Cube,再创建两个材质球Cube、Unilt 2、再创建一个shader代码UniltShader【Project右键Create-Shader-NewSurfaceShader】把里面原来的代码删除,写入下面的代码 3、把shader UniltShader拖给材质球Unilt 4、把Cube的Mesh Renderer中Materials的Size改为2,然后把材质球Cube、Unilt分

    2024年02月08日
    浏览(46)
  • 实现窗户特效的Unity Shader解析

            本文将详细介绍一种使用Unity Shader实现窗户特效的方法。通过分析代码,我们将解释每个关键部分的作用,以及如何将其组合在一起以实现逼真的窗户效果。希望本文能为Shader编程初学者和Unity开发者提供一些有用的指导。   引言:         在游戏和虚拟现实

    2024年02月12日
    浏览(50)
  • Unity Shader学习(九)物体边缘实现

    根据前面的学习,我们了解到除了可以对点的颜色进行处理,还可以对点本身进行操作,例如我们可以改变点的位置,这样就可以实现对模型渲染的操控。物体边缘效果是我们常用的一种效果,要实现物体边缘,原理也很简单。 首先我们要了解到,模型在渲染时,有正面和背

    2024年02月16日
    浏览(50)
  • Unity 使用Shader实现序列帧动画

    序列帧动画图片使用shader逐帧播放 可以直接使用,拿走不谢。 可以挂到材质上放入Image组件的材质中使用。

    2024年02月13日
    浏览(45)
  • 【Bug解决】Unity Build GI data 卡住问题

    最近一直在忙工作项目,项目均是不适宜公开或无编写价值的内容,因此近半个月未更新博客。 本篇来记录下近日遇到的一个问题。 在使用Unity2021打包项目时,发现进度条卡在Build GI data中,挂了一晚上,发现仍然没有进展。 参考官方文档,GI(Global Illumination) data 指的是全局

    2024年02月11日
    浏览(44)
  • 【unity shader案例】如何实现一个玻璃效果

      原理:玻璃最大的特点当然就是半透明了,如何在游戏中实现这样的效果呢?我这里总体的思路就是先截取整个场景作为一张纹理,然后把玻璃区域的纹理贴到这个玻璃模型上。 下面看具体实现代码 实现效果如下    更进一步,还可以实现毛玻璃的效果,思路就是用一张

    2024年02月16日
    浏览(54)
  • Unity中Shader的遮罩的实现

    Unity中Shader的遮罩的实现 效果演示: 修改后的代码 效果:

    2024年02月06日
    浏览(46)
  • Unity中Shader实现UI流光效果

    在很多游戏的 UI 中,都有实现 一道光扫过 UI 的效果 Unity中Shader的时间_Time 注意: 因为,这是UGUI的Shader,记着修改渲染顺序为 透明层级 和 混合模式 Tags {“Queue” = “TransParent”} Blend SrcAlpha OneMinusSrcAlpha 代码: 效果: Unity3D Shader系列之UI流光效果 Unity流光shader,无需图片

    2024年02月05日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包