Unity中全局光照GI的总结

news2024/11/27 9:47:41

文章目录

  • 前言
  • 一、在编写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"
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创新,无处不在的便利体验——基于智能视频技术的安防监控系统EasyCVR

随着科技的迅猛发展,基于智能视频和语音技术的EasyCVR智能安防监控系统正以惊人的速度改变我们的生活。EasyCVR通过结合先进的视频分析、人工智能和大数据技术,为用户提供了更加智能、便利的安全保护体验,大大提升了安全性和便利性。本文将介…

常见产品结构四大类型 优劣势比较

一般,我们通过产品架构来构建用户体验,这样可以提供更清晰的导航和组织、优化用户流程和交互、增强产品的可扩展性和可维护性,提升用户的满意度和忠诚度。如果没有明确的产品结构,可能会导致功能冗余或功能缺失、交互流程混乱等问…

super() 和 super(props) 有什么区别?

一、ES6 类 在 ES6 中,通过 extends 关键字实现类的继承,方式如下: class sup { constructor(name) { this.name name; } printName() { console.log(this.name); }}class sub extends sup { constructor(name, age) { …

Ubuntu2004字体不清晰,排查流程

昨天一早来发现平时用的Ubuntu2004物理机的字体变得很模糊,之前还是好好的,这里记录一下解决方案。 解决方案 通过显示器物理按键设置“自适应”解决,我的显示器是长城的,“自适应”按钮是右边从下往上数第二个。 排查流程 我先…

springboot和spring对比

spring的出现 大家都知道spring是大概2003年左右开始出现流行的,是一个轻量级的Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可…

2023最新electron 进程间通讯的几种方法

数据传递(旧) 渲染进程发数据到主进程 // 按钮事件 const handleWebRootPathClick () > {ipcRenderer.send(open_dir) }// main.ts中接收 ipcMain.on(open_dir, () > {console.log(recv ok) }) 主进程发数据到渲染进程 // main.ts中发送数据 …

python实现全向轮EKF_SLAM

python实现全向轮EKF_SLAM 代码地址及效果运动预测观测修正参考算法 代码地址及效果 代码地址 运动预测 简化控制量 u t u_t ut​ 分别定义为 v x Δ t v_x \Delta t vx​Δt, v y Δ t v_y \Delta t vy​Δt,和 ω z Δ t \omega_z \Delta t ωz…

2024 年天津专升本招生实施办法(天津专升本文化报名考试时间)

2024 年天津市高职升本科招生实施办法 为做好2024年天津市高职升本科招生工作,天津市招生委员会高等学校招生办公室(以下简称“市高招办”)依据教育部、天津市有关规定,制定本实施办法。 一、招生章程 1.招生学校要制…

RFID携手制造业升级,为锂电池生产带来前所未有的可靠性

应用背景 随着科技的发展和全球化的推进,产品的生产过程越来越复杂,且对品质的要求也越来越高。在锂电池生产领域,由于其高能量密度、长寿命和环保特性,已被广泛应用于电动汽车、储能系统等领域。然而,锂电池的安全性和…

包教包会:Mysql主从复制搭建

笑小枫的专属目录 一、无聊的理论知识1. 主从复制原理2. 主从复制的工作过程3. MySQL四种同步方式 二、docker下安装、启动mysql1. 安装主库2. 安装从库 三、配置Master(主)四、配置Slave(从)五、链接Master(主)和Slave(从)六、主从复制排错1. 错误:error connectin…

说说React的事件机制?

一、是什么 React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等 在React中这套事件机制被称之为合成事件 合成事件(SyntheticEvent) 合成事件是 React模拟原生 DOM事件所有能力的一个事件…

Spring Cloud学习(三)【Nacos注册中心】

文章目录 认识 NacosNacos 安装使用 Nacos 完成服务注册Nacos 服务分级存储模型集群负载均衡策略 NacosRule根据权重负载均衡Nacos 环境隔离Nacos 和 Eureka 的区别 认识 Nacos Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件。相比Eureka 功能更加丰富&…

同立海源携CGT核心试剂与技术服务整体解决方案亮相SITC 2023年会

2023年11月1日至5日,备受瞩目的第38届癌症免疫治疗学会(SITC)在美国圣地亚哥会议中心盛大召开,作为世界上最大的专注于癌症免疫治疗的国际盛会,本届SITC年会专注于探讨和分享最新的癌症免疫治疗技术和应用研究成果&…

Libra R-CNN: Towards Balanced Learning for Object Detection(2019.4)

文章目录 AbstractIntroduction引入问题1) Sample level imbalance2) Feature level imbalance3) Objective level imbalance进行解决贡献 Related Work(他人的work,捎带与我们的对比)Model architectures for object detection&a…

2023长三角G60科创走廊高质量发展要素对接大会举行

11月8日,第六届中国国际进口博览会2023长三角G60科创走廊高质量发展要素对接大会在国家会展中心隆重举行。会上,G60“一廊九城”同向同行、共赴未来,立足“三先走廊”战略定位,围绕“服务‘一带一路’建设,赋能一体化高…

Java进阶API第二章

Java进阶API第二章 一. 抛出企业问题,脱离main测试,模块化编程 1.学校里如何测试的 //学校教的测试方法 public static void main(String[] args) {//2.本地测试//3.调用函数//4.看输出,查看结果是否符合预期//5.预期结果和测试结果是通过人工…

【开源】基于Vue和SpringBoot的大学兼职教师管理系统

项目编号: S 004 ,文末获取源码。 \color{red}{项目编号:S004,文末获取源码。} 项目编号:S004,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容三、界面展示3.1 登录注册3.2 学生教师管…

电动汽车多时段动态充电价格及网损策略研究

摘要:电动汽车以无序充电方式接入配电网时与网内基础用电负荷叠加,会形成峰上加峰的现象,不利于配电网的稳定运行。针对上述问题,首先对私家车充电负荷进行建模,采用蒙特卡罗抽样模拟电动汽车无序行为下的充电负荷曲线…

灵活运用Vue指令:探究v-if和v-for的使用技巧和注意事项

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 ⭐ 专栏简介 📘 文章引言 一、作…

10道高频React面试题快问快答

※其他的快问快答,看这里! 10道高频Qiankun微前端面试题快问快答 10道高频webpack面试题快问快答 20道高频CSS面试题快问快答 20道高频JavaScript面试题快问快答 30道高频Vue面试题快问快答 面试中的快问快答 快问快答的情景在面试中非常常见。 在面试…