欢迎光临散文网 会员登陆 & 注册

Unity——UI光照(模拟光照)

2023-04-16 23:18 作者:叁拾柒渡伍  | 我要投稿

前言

        没错,这是UI光照不是2D光照。是Image不是SpriteRenderer。

        本文的内容更偏向于花活骚操作,为了不耽误读者的时间,请提前知晓!

        不管是Buind-In管线还是URP管线下,可能都没有特别提供针对UI的光照功能,可能也确实没有必要,但是本着“玩”技术的心理,我在这里给出一个UI模拟光照的“魔道功法”。

        其实就是Shader 实现经验型光照模型的衍生,因为本文属于Shader光照部分的衍生,所以本文并不会非常详细的讲解光照相关的知识,那部分知识可能会单独做一期,详细的讲解知识点以及计算过程。

        老规矩!此方案是否具有实用性,各位读者请自行衡量。

        作者也认为实用性较小,所以当做一个骚操作来写。

         先来看效果:

平行光
点光源
聚光灯

        如果部分读者不太了解Shader方面的知识也没有关系,因为是个花活,用的的东西也比较简单,阅读完本文也是可以实现的。

        最后会给出完整的Shader代码。

思路

        单纯让Image模拟光照的思路其实比较简单,就是将3D模型实现光照的那一套挪用到UI上即可,修改的地方相当少。比如我示例中使用的就是【兰伯特(Lambert)光照模型】。

        至于为什么采用兰伯特光照模型,那是因为兰伯特光照模型比较简单。

实现

        兰伯特光照模型是一个经验光照模型。他不符合真正的物理,只是看起来可能是对的,根据计算机图形学第一定律:(⊙o⊙)…。

计算机图形学第一定理

兰伯特光照模型: Diffuse=Ambient+Kd*LightColor*Dot(N,L)

Diffuse:最终漫反射光强(颜色)            Ambient:环境光强(环境色)

Kd:材质对光的反射系数                       LightColor:灯光的颜色

N:顶点法线向量                                   L:顶点到光源位置的单位向量

示意图

        如果对光照这部分不太了解的读者,可以先理解为L与N形成的夹角越小,那就表示更亮,反之就更暗,在90°及以上的时候我们认为当前点不接受光照。

        知道了思路,实现起来就很简单了,我们直接动手开始写Shader。

        我们这里只写最简的功能,把贴图渲染出来,支持光照。其他的功能比如什么调整Color,图片透明,还有因为引入cginc导致变体变多等问题因为与本文无关就不处理了。

        我们要用到编辑器为我们提供的一些数据,需要引用 #include "Lighting.cginc"

        环境色:fixed4 Ambient=unity_AmbientSky;

        反射系数:这里用1,half Kd=1;

        灯光颜色:fixed4 LightColor=_LightColor0;

        N法线:这里只考虑最简单情况,让法线面向【前】fixed3 N=fixed3(0,0,-1);

        L光源单位向量:fixed3 L=_WorldSpaceLightPos0;

        然后只需要将以上数值带入公式,就可以了。

        fixed4 Diffuse=Ambient+Kd*LightColor*max(0,dot(N,L));

        为了避免一些因为负值导致的问题,我们使用Max()限制一下范围。

部分代码

    Properties

    {

        //为了与UI的Sprite对接,图片的名字尽量不要动

       _MainTex ("Texture", 2D) = "white" {}

    }

    SubShader

    {

        Tags { "RenderType"="Transparent" }

        Pass

        {

            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM

          略 ......

            #include "Lighting.cginc"

            略......

            fixed4 frag (v2f i) : SV_Target

            {

                fixed4 col = tex2D(_MainTex, i.uv);

                fixed4 Ambient=unity_AmbientSky;

                half Kd=1;

                fixed4 LightColor=_LightColor0;

                fixed3 N=fixed3(0,0,-1);

                fixed3 L=_WorldSpaceLightPos0;

                fixed4 Diffuse=Ambient+Kd*LightColor*max(0,dot(N,L));

                col*=Diffuse;

                return col;

            }

            ENDCG

        }

    }

现在平行光就已经实现了。

点光源与聚光灯

        平行光和点光源与聚光灯不同,点光源与聚光灯发出的光会随着距离做出衰减,距离光源越远,光强越弱,这个衰减并不是一个线性的。这个衰减可以自己计算也可以使用Unity提供的方法UNITY_LIGHT_ATTENUATION(“返回的衰减值”,“顶点世界坐标”)

我们以点光源为例,看一下返回的衰减值是个什么样子的。

代码
图像

        我们要做的就是把这个衰减值*灯光颜色附加到图像上。就可以得到最后的结果。

        我们在第二Pass中实现效果。此处要用到#include "AutoLight.cginc"空间中的方法。

代码

完整代码

Shader "UI/UILight"

{

    Properties

    {

        [PerRendererData]_MainTex ("Texture", 2D) = "white" {}

    }

    SubShader

    {

        Tags { "RenderType"="Transparent" }

        Pass

        {

            Tags{"LightMode"="ForwardBase"}

            CGPROGRAM

            #pragma vertex vert

            #pragma fragment frag

            #include "UnityCG.cginc"

            #include "Lighting.cginc"

            struct appdata

            {

                float4 vertex : POSITION;

                float2 uv : TEXCOORD0;

            };

            struct v2f

            {

                float2 uv : TEXCOORD0;

                float4 vertex : SV_POSITION;

            };

            sampler2D _MainTex;

            float4 _MainTex_ST;

            v2f vert (appdata v)

            {

                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                return o;

            }

            fixed4 frag (v2f i) : SV_Target

            {

                return 0;

                fixed4 col = tex2D(_MainTex, i.uv);

                clip(col.w-0.01);

                fixed4 Ambient=unity_AmbientSky;

                half Kd=1;

                fixed4 LightColor=_LightColor0;

                fixed3 N=fixed3(0,0,-1);

                fixed3 L=_WorldSpaceLightPos0;

                fixed4 Diffuse=Ambient+Kd*LightColor*max(0,dot(N,L));

                col*=Diffuse;

                return col;

            }

            ENDCG

        }

           Pass

        {

            Tags{"LightMode"="ForwardAdd"}

            Blend One One

            CGPROGRAM

           #pragma vertex vert

            #pragma fragment frag

            #pragma multi_compile_fwdadd

            #include "UnityCG.cginc"

            #include "Lighting.cginc"

            #include "AutoLight.cginc"

            struct appdata

            {

                float4 vertex : POSITION;

                float2 uv : TEXCOORD0;

                float3 normal:NORMAL;

            };

            struct v2f

            {

                 float2 uv : TEXCOORD0;

                float4 vertex : SV_POSITION;

                float3 worldPos:TEXCOORD2;

            };

            sampler2D _MainTex;

            float4 _MainTex_ST;

            v2f vert (appdata v)

            {

                v2f o;

                o.vertex = UnityObjectToClipPos(v.vertex);

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                o.worldPos=mul(unity_ObjectToWorld,v.vertex);

                return o;

            }

            fixed4 frag (v2f i) : SV_Target

            {

                fixed4 col = tex2D(_MainTex, i.uv);

                UNITY_LIGHT_ATTENUATION(atten,0,i.worldPos)

                fixed4 LightColor=_LightColor0*atten;

                return col*LightColor;

            }

            ENDCG

        }

    }

}

现在就已经实现了我一开始那三张图的效果了。

        最后可能会出现一些Scene和Game窗口显示不一样的情况。比如在平行光是正常的,但是点光源和聚光灯不正常。还有就是这种方式的开销相对来说会变大。

问题1
问题2

可以将Canvas的渲染模式(Render Mode)改为Screen Space-Camera。新建一个相机,让他只渲染UI层,为了让UI显示在模型之前,相机的Depth调高,并且将Clear Flags设置为Depth Only。

设置Canvas
设置相机

       

         到这里文章的内容就全部结束。

        本期的内容是个骚操作,目的是与各位读者分享一下有趣的操作。因为本期的定位不是[教程]系列,可能会有些不是仔细,希望各位读者理解。

        如果本文对您有一丝丝的帮助请赏给作者一个赞好吗。

        最后想说一点,以后我打算每期专栏配一个演示视频,可能会更好的帮助有需要的读者,能让有需要的读者更直观的看到我的操作过程。

        我们下期再见。


Unity——UI光照(模拟光照)的评论 (共 条)

分享到微博请遵守国家法律