Unity中Shader光照探针的支持


前言

主要写全局照明中,光照探针的支持


一、光照探针用在哪怎么用

1、光照探针的应用场景

在一个只有 Backed 模式灯光的场景中,有一个非静态的物体
即该物体在烘焙时,不会被烘焙,不会受到烘焙灯光的影响。

但是,我们此时不能修改灯光的模式 也不能修改该物体为静态物体
却需要给该动态物体受到烘焙灯光的影响
此时就需要使用光照探针了

2、我们按照以上条件,在Unity中搭建一个相同的环境

在这里插入图片描述
我们会发现,小球在烘焙后是不受烘焙光的影响的
请添加图片描述

3、创建光照探针

可以直接在一个空物体添加 Light Probe Group,也按下图直接添加光照探针
在这里插入图片描述

添加后,把光照探针的范围设置到,要让动态小球接收到烘焙光影响的范围
在光照探针中,黄色小点点在空间内越密集越多,动态物体接收到的烘焙光越精致细腻
在这里插入图片描述

然后,我们烘焙后就可以看见小球能接收烘焙光的效果了
请添加图片描述


二、在我们自己的Shader中,实现支持光照探针

我们继续使用之前的文章作为测试

我们会发现我们的 Shader在使用后是全黑的
因为我们关闭了主平行光,两个点光源又是Backed类
在这里插入图片描述

1、使用常用的 cginc

#include “AutoLight.cginc”
#include “Lighting.cginc”

2、在 v2f 中,准备如下变量

float4 worldPos : TEXCOORD;
half3 worldNormal : NORMAL;
half3 sh : TEXCOORD2;

3、在顶点着色器中,进行顶点和法线世界空间的转化后,使用如下代码

//实现 球谐 或者 环境色 和 顶点照明 的计算
//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

4、在片元着色器中,使用如下代码计算

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

然后,我们就可以看见我们的Shader也有光照探针的效果了

请添加图片描述

同时,也有了逐顶点光照的效果
请添加图片描述

最终代码

//在这里里面使用 自定义的 cginc 来实现全局GI
//GI数据的准备
//烘培分支的判断
//GI的直接光实现
//GI的间接光实现
//再议ATTENUATION
//光照探针的支持
Shader "MyShader/P1_8_8"
{
    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;
            };

            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.texcoord1.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
        }
    }
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠溪泽岸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值