Unity中全局光照GI的总结

2023-11-10 17:45
文章标签 总结 全局 unity 光照 gi

本文主要是介绍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_INDIRECTc.rgb += s.Albedo * gi.indirect.diffuse;#endifreturn c;
}inline void ResetUnityLight1(out UnityLight outLight)
{outLight.color = half3(0, 0, 0);outLight.dir = half3(0, 1, 0); // Irrelevant direction, just not nulloutLight.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_SHo_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_COMBINEDfixed4 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 lightmapo_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 lightmapsfixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);#ifdef DIRLIGHTMAP_COMBINEDhalf4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);#elseo_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;#endifhalf3 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;#endifhalf3 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_PIXELo.sh = 0;//近似模拟非重要级别的点光在逐顶点上的光照效果#ifdef VERTEXLIGHT_ONo.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);#endifo.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自带的方法,把结构体中的内容初始化为0UNITY_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_PIXELgiInput.ambient = i.sh;#elsegiInput.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_GI1gi = 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"
}

这篇关于Unity中全局光照GI的总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/384281

相关文章

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

MySQL中的锁机制详解之全局锁,表级锁,行级锁

《MySQL中的锁机制详解之全局锁,表级锁,行级锁》MySQL锁机制通过全局、表级、行级锁控制并发,保障数据一致性与隔离性,全局锁适用于全库备份,表级锁适合读多写少场景,行级锁(InnoDB)实现高并... 目录一、锁机制基础:从并发问题到锁分类1.1 并发访问的三大问题1.2 锁的核心作用1.3 锁粒度分

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义