Unity中全局光照GI的总结

文章目录

  • 前言
  • 一、在编写Shader时,有一些隐蔽的Bug不会直接报错,我们需要编译一下让它显示出来,方便修改
    • 我们选择我们的Shader,点击编译并且展示编译后的Shader后的内容,隐蔽的Bug就会暴露出来了。
  • 二、我们大概回顾一下,之前实现的内容,和应用场景(然后,可以在以后的项目中按需选择取舍)
    • 1、第一个Pass是我们模型的主要效果
    • 2、第二个Pass是我们模型阴影的投射(在不需要时可以剔除该Pass)
    • 3、第三个Pass是我们模型烘焙计算(在不需要时可以剔除该Pass)


前言

Unity中全局光照GI的总结,我们对之前文章中,实现的 全局光照 GI Shader 总结一下

  • Unity中Shader的全局照明简介

  • Unity中Shader自定义cginc文件

  • Unity中Shader的GI相关数据的准备

  • Unity中Shader的烘培分支的判断

  • Unity中Shader的GI的直接光实现

  • Unity中Shader的GI的间接光实现

  • Unity中Shader再议ATTENUATION

  • Unity中Shader光照探针的支持

  • Unity中Shader的间接光的产生Meta Pass


一、在编写Shader时,有一些隐蔽的Bug不会直接报错,我们需要编译一下让它显示出来,方便修改

我们选择我们的Shader,点击编译并且展示编译后的Shader后的内容,隐蔽的Bug就会暴露出来了。

在这里插入图片描述


二、我们大概回顾一下,之前实现的内容,和应用场景(然后,可以在以后的项目中按需选择取舍)

1、第一个Pass是我们模型的主要效果

在这里插入图片描述

  • 在该Pass的片元着色器中,对于计算 GI 的这个函数,我们可以选择使用Unity自带的函数(在项目确定只用某一套 GI 方案时,可以只选择该函数中的部分功能使用)
    在这里插入图片描述
  • 在该Pass的片元着色器中,对于同时计算了 Lambert 和 Phone 光照模型的这个函数(我们可以按照公式自定义实现,也可以直接使用)
    在这里插入图片描述

2、第二个Pass是我们模型阴影的投射(在不需要时可以剔除该Pass)

在这里插入图片描述

3、第三个Pass是我们模型烘焙计算(在不需要时可以剔除该Pass)

在这里插入图片描述

该GI的最终代码:

MyGlobalIllumination.cginc

#ifndef MYGLOBALILLUMINATION_INCLUDE
#define MYGLOBALILLUMINATION_INCLUDE

//Lambert光照模型
inline fixed4 UnityLambertLight1 (SurfaceOutput s, UnityLight light)
{
    fixed diff = max (0, dot (s.Normal, light.dir));

    fixed4 c;
    c.rgb = s.Albedo * light.color * diff;
    c.a = s.Alpha;
    return diff;
}

inline fixed4 LightingLambert1 (SurfaceOutput s, UnityGI gi)
{
    fixed4 c;
    c = UnityLambertLight1 (s, gi.light);
    
    //如果是在 BackedGI 或者 RealtimeGI的情况下,进行以下计算
    #ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
    c.rgb += s.Albedo * gi.indirect.diffuse;
    #endif

    return c;
}

inline void ResetUnityLight1(out UnityLight outLight)
{
    outLight.color = half3(0, 0, 0);
    outLight.dir = half3(0, 1, 0); // Irrelevant direction, just not null
    outLight.ndotl = 0; // Not used
}

inline void ResetUnityGI1(out UnityGI outGI)
{
    ResetUnityLight1(outGI.light);
    outGI.indirect.diffuse = 0;
    outGI.indirect.specular = 0;
}

inline UnityGI UnityGI_Base1(UnityGIInput data, half occlusion, half3 normalWorld)
{
    UnityGI o_gi;
    ResetUnityGI1(o_gi);

    //计算在Distance Shadowmask 中实时阴影与烘培阴影的混合过程
    // 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

    //这个是进行静态 GI 的计算(BackedGI)
    #if defined(LIGHTMAP_ON)
        // Baked lightmaps
        //光照图的采样
        half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
        half3 bakedColor = DecodeLightmap(bakedColorTex);

        //当开启 Unity 中的 Directional 模式 (定向光模式)时,进行的计算
        #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

    //这个是进行动态 GI 的计算(RealtimeGI)
    #ifdef DYNAMICLIGHTMAP_ON
        // Dynamic lightmaps
        fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
        half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);

        #ifdef DIRLIGHTMAP_COMBINED
            half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
            o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);
        #else
            o_gi.indirect.diffuse += realtimeColor;
        #endif
    #endif
    //这里是使物体表面的颜色 乘以 环境光遮蔽,以实现环境光线被阻碍后物体表面的颜色
    o_gi.indirect.diffuse *= occlusion;
    return o_gi;
}

inline UnityGI UnityGlobalIllumination1 (UnityGIInput data, half occlusion, half3 normalWorld)
{
    return UnityGI_Base1(data, occlusion, normalWorld);
}

inline void LightingLambert_GI1 (SurfaceOutput s,UnityGIInput data,inout UnityGI gi)
{
    gi = UnityGlobalIllumination1 (data, 1.0, s.Normal);
}

#endif


GI Shader 代码:

//在这里里面使用 自定义的 cginc 来实现全局GI
//GI数据的准备
//烘培分支的判断
//GI的直接光实现
//GI的间接光实现
//再议ATTENUATION
//光照探针的支持
//间接光的产生Meta Pass
Shader "MyShader/P1_8_9"
{
    Properties
    {
        _Color("Color",Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }
        Pass
        {
            Tags
            {
                "LightMode"="ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #include "Lighting.cginc"

            #include "CGIncludes/MyGlobalIllumination.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                //定义第二套 UV ,appdata 对应的固定语义为 TEXCOORD1
                #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
                float4 texcoord1 : TEXCOORD1;
                #endif
                half3 normal : NORMAL;
                float4 texcoord2 : TEXCOORD2;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;

                float4 worldPos : TEXCOORD;
                //定义第二套UV
                #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
                float4 lightmapUV : TEXCOORD1;
                #endif
                half3 worldNormal : NORMAL;

                half3 sh : TEXCOORD2;
                //1、使用 阴影采样 和 光照衰减的方案的 第一步
                //同时定义灯光衰减以及实时阴影采样所需的插值器
                UNITY_LIGHTING_COORDS(3, 4)
                //UNITY_SHADOW_COORDS(2)
            };

            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                //对第二套UV进行纹理采样
                #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
                    o.lightmapUV.xy = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw;
                #endif

                //实现 球谐 或者 环境色 和 顶点照明 的计算
                //SH/ambient and vertex lights
                #ifndef LIGHTMAP_ON //当此对象没有开启静态烘焙时
                #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
                    o.sh = 0;
                    //近似模拟非重要级别的点光在逐顶点上的光照效果
                #ifdef VERTEXLIGHT_ON
                        o.sh += Shade4PointLights(
                        unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                        unity_LightColor[0].rgb,unity_LightColor[1].rgb,unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                        unity_4LightAtten0,o.worldPos,o.worldNormal);
                #endif
                    o.sh = ShadeSHPerVertex(o.worldNormal,o.sh);
                #endif
                #endif


                //2、使用 阴影采样 和 光照衰减的方案的 第二步
                UNITY_TRANSFER_LIGHTING(o, v.texcoord2.xy)
                //TRANSFER_SHADOW(o)
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                //1、准备 SurfaceOutput 的数据
                SurfaceOutput o;
                //目前先初始化为0,使用Unity自带的方法,把结构体中的内容初始化为0
                    UNITY_INITIALIZE_OUTPUT(SurfaceOutput, o)
                o.Albedo = 1;
                o.Normal = i.worldNormal;

                //1、代表灯光的衰减效果
                //2、实时阴影的采样
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);


                //2、准备 UnityGIInput 的数据
                UnityGIInput giInput;
                //初始化
                    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
                //修改用到的数据
                giInput.light.color = _LightColor0;
                giInput.light.dir = _WorldSpaceLightPos0;
                giInput.worldPos = i.worldPos;
                giInput.worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
                giInput.atten = atten;
                giInput.ambient = 0;

                #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
                    giInput.ambient = i.sh;
                #else
                giInput.ambient = 0.0;
                #endif


                #if defined(DYNAMICLIGHTMAP_ON) || defined(LIGHTMAP_ON)
                giInput.lightmapUV = i.lightmapUV;
                #endif

                //3、准备 UnityGI 的数据
                UnityGI gi;
                //直接光照数据(主平行光)
                gi.light.color = _LightColor0;
                gi.light.dir = _WorldSpaceLightPos0;
                //间接光照数据(目前先给0)
                gi.indirect.diffuse = 0;
                gi.indirect.specular = 0;

                //GI的间接光照的计算 
                LightingLambert_GI1(o, giInput, gi);
                //查看Unity源码可知,计算间接光照最主要的函数就是
                //inline UnityGI UnityGI_Base1(UnityGIInput data, half occlusion, half3 normalWorld)
                //所以我们直接给 gi 赋值,可以不使用 LightingLambert_GI1
                gi = UnityGI_Base1(giInput, 1, o.Normal);

                //GI的直接光照的计算
                //我们在得到GI的数据后,对其进行Lambert光照模型计算,即可得到结果
                fixed4 c = LightingLambert1(o, gi);

                return c;
                //return fixed4(gi.indirect.diffuse,1);
                //return 1;
            }
            ENDCG
        }

        //阴影的投射
        Pass
        {
            //1、设置 "LightMode" = "ShadowCaster"
            Tags
            {
                "LightMode" = "ShadowCaster"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            //需要添加一个 Unity变体
            #pragma multi_compile_shadowcaster

            #include "UnityCG.cginc"

            //声明消融使用的变量
            float _Clip;
            sampler2D _DissolveTex;
            float4 _DissolveTex_ST;

            //2、appdata中声明float4 vertex:POSITION;和half3 normal:NORMAL;这是生成阴影所需要的语义.
            //注意:在appdata部分,我们几乎不要去修改名字 和 对应的类型。
            //因为,在Unity中封装好的很多方法都是使用这些标准的名字
            struct appdata
            {
                float4 vertex:POSITION;
                half3 normal:NORMAL;
                float4 uv:TEXCOORD;
            };

            //3、v2f中添加V2F_SHADOW_CASTER;用于声明需要传送到片断的数据.
            struct v2f
            {
                float4 uv : TEXCOORD;
                V2F_SHADOW_CASTER;
            };

            //4、在顶点着色器中添加TRANSFER_SHADOW_CASTER_NORMALOFFSET(o),主要是计算阴影的偏移以解决不正确的Shadow Acne和Peter Panning现象.
            v2f vert(appdata v)
            {
                v2f o;
                o.uv.zw = TRANSFORM_TEX(v.uv, _DissolveTex);
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }

            //5、在片断着色器中添加SHADOW_CASTER_FRAGMENT(i)

            fixed4 frag(v2f i) : SV_Target
            {
                //外部获取的 纹理 ,使用前都需要采样
                fixed4 dissolveTex = tex2D(_DissolveTex, i.uv.zw);

                //片段的取舍
                clip(dissolveTex.r - _Clip);

                SHADOW_CASTER_FRAGMENT(i);
            }
            ENDCG
        }
        //在常规的渲染时,是不会被使用的。一般使用时,是在烘焙贴图
        // Extracts information for lightmapping, GI (emission, albedo, ...)
        // This pass it not used during regular rendering.
        Pass
        {
            Name "META"
            Tags
            {
                "LightMode" = "Meta"
            }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 2.0
            #include "UnityCG.cginc"
            #include "UnityMetaPass.cginc"
            fixed4 _Color;
            
            struct v2f
            {
                float4 pos : SV_POSITION;
            };

            v2f vert(appdata_full v)
            {
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f,o)
                
                o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST,
         unity_DynamicLightmapST);
                
                return o;
            }
            
            half4 frag(v2f i) : SV_Target
            {
                UnityMetaInput metaIN;
                UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN);
                metaIN.Albedo = 1;
                metaIN.Emission = _Color;
                return UnityMetaFragment(metaIN);
            }
            ENDCG
        }
    }
    CustomEditor "LegacyIlluminShaderGUI"
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/130725.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧畜牧小程序开发流程

本文将详细介绍智慧畜牧小程序的开发流程,包括需求分析、设计、开发、测试和上线等环节。同时,将深入思考智慧畜牧小程序的发展趋势和未来挑战,为读者提供有深度的思考和逻辑性的分析。 一、需求分析 1.明确目标用户:首先…

Bond配置文件配置

1、选择2个自己需要的网口,查看有哪些网口 [roothostname ~]# ifconfig -a [roothostname ~]#systemctl disable NetworkManager 开机不启动图形化网络服务 2、编辑网口的配置文件 [roothostname ~]# cd /etc/sysconfig/network-scripts [roothostname n…

实操创建属于自己的亚马逊云科技VPS服务:Amazon Lightsail

本文主要讲述如何独立创建自己的亚马逊云科技VPS服务,希望此文能帮助你对亚马逊云科技VPS服务也就是Amazon Lightsail,有个新的认识,对所使用的VPS有所帮助。 Amazon Lightsail是一款无论云计算的新手还是专家,都可通过其快速启动…

Sagemaker基础操作指南

简介 Amazon SageMaker是亚马逊AWS提供的一项托管式机器学习服务,旨在简化和加速机器学习开发的整个生命周期。它为机器学习工程师和数据科学家提供了一套完整的工具和功能,用于构建、训练、调优和部署机器学习模型。本文将会通过一个简单的例子&#x…

Conda executable is not found 三种问题解决

如果在PyCharm中配置Python解释器时显示“conda executable is not found”错误消息,这意味着PyCharm无法找到您的Conda可执行文件。您可以按照以下步骤解决此问题: 1.方法一 确认Conda已正确安装。请确保您已经正确安装了Anaconda或Miniconda&#xff…

演示文稿制作软件 Deckset mac中文版介绍

Deckset mac是一款Mac上的演示文稿制作软件,它可以让你使用Markdown语言快速地创建演示文稿。与传统的演示文稿制作软件相比,Deckset采用了全新的设计理念,旨在让用户更加专注于内容的创作,而不是花费过多的时间在排版和设计上。 …

vivo 数据库降本实践:探索成本效益最优的数据库解决方案

vivo 自 2022 年开始调研、测试 OceanBase 至今,现已上线 17 个业务系统,涵盖日志类、分析类、交易类业务,实现了总资源节省 80%,开发、运维工作大幅简化。vivo 体系与流程 IT 部门数据库高级工程师廖光明在本文中,详细…

Antd G6实现自定义工具栏

在使用g6实现知识图谱可视化中,产品经理提出了有关图谱操作的不少功能,需要放置在工具栏中,其中有些功能不在g6自带的功能里,且工具栏样式、交互效果也和官方自定义工具栏不同。那我们怎么去实现呢? g6官方的工具栏案例…

香港和美国节点服务器的测试IP哪里有?

在选择服务器时,我们通常需要进行一些测试来评估其性能和稳定性。当然,这其中一个重要的测试指标就是服务器的 IP 地址。通过测试 IP 地址,我们可以了解到服务器所在地区以及网络连接质量等信息。作为香港及亚太数据中心领先服务商恒创科技&a…

解决Python并发访问共享资源引起的竞态条件、死锁、饥饿问题的策略

目录 一、概述 二、竞态条件 三、死锁 四、饥饿 五、总结 一、概述 在Python中,多线程和多进程可以有效地提高程序的并发性能。然而,当多个线程或进程需要访问共享资源时,可能会引发竞态条件、死锁和饥饿等问题。这些问题可能会导致程序…

敏捷战略实施方法-资深组织发展专家实践秘笈

要怎样才能生成敏捷战略呢?作者基于多年的组织发展实践,总结出如下公式:敏捷战略 战略共创 迭代进化 即要得到一个好的敏捷战略,首先要做好战略共创,并在战略实施过程中对战略进行持续迭代,两者不可偏废…

机器学习——奇异值分解案例(图片压缩-代码简洁版)

本想大迈步进入前馈神经网络 但是…唉…瞅了几眼,头晕 然后想到之前梳理的奇异值分解、主成分分析、CBOW都没有实战 如果没有实际操作,会有一种浮在云端的虚无感 但是如果要实际操作,我又不想直接调用库包 可是…如果不直接调包,感…

一种优雅的调用第三方接口的思路及实现

之前的项目调用第三方接口时,往往用HttpUtils类似的静态方法调用。比较丑,不通用。如下,这是截取项目中某人调用的一段代码,非常不雅: 经改进后,采用了动态代理技术来实现,效果如下&#xff1a…

RabbitMQ的 五种工作模型

RabbitMQ 其实一共有六种工作模式: 简单模式(Simple)、工作队列模式(Work Queue)、 发布订阅模式(Publish/Subscribe)、路由模式(Routing)、通配符模式(Topi…

网络安全-黑客技术-小白学习

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高; 二、则是发展相对成熟…

VScode + opencv(cmake编译) + c++ + win配置教程

1、下载opencv 2、下载CMake 3、下载MinGW 放到一个文件夹中 并解压另外两个文件 4、cmake编译opencv 新建文件夹mingw-build 双击cmake-gui 程序会开始自动生成Makefiles等文件配置,需要耐心等待一段时间。 简单总结下:finish->configuring …

【图论实战】 Boost学习 03:dijkstra_shortest_paths

文章目录 示例代码 示例 最短路径: A -> C -> D -> F -> E -> G 长度 16 代码 #include <iostream> #include <boost/graph/adjacency_list.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/graph/graphviz.h…

状态机实现RGB灯跳变

1.项目功能梗概 因为原本使用的为for循环进行遍历&#xff0c;然后依次执行代码&#xff0c;但是由于看门狗的存在&#xff0c;不能使用delay_ms这种死延时。所以现在打算定时器回调函数控制状态机状态这种方法。 2.状态机 作用 当系统需要执行某个任务时&#xff0c;可以根据…

力扣字符串--总结篇

前言 字符串学了三天&#xff0c;七道题。初窥kmp&#xff0c;已经感受到算法的博大精深了。 内容 对字符串的操作可以归结为以下几类&#xff1a; 字符串的比较、连接操作&#xff08;不同编程语言实现方式有所不同&#xff09;&#xff1b; 涉及子串的操作&#xff0c;比…

Python数据结构: 列表(List)详解

在Python中&#xff0c;列表&#xff08;List&#xff09;是一种有序、可变的数据类型&#xff0c;被广泛用于存储和处理多个元素。列表是一种容器&#xff0c;可以包含任意数据类型的元素&#xff0c;包括数字、字符串、列表、字典等。本文将深入讨论列表的各个方面&#xff0…