【Unity Shader学习笔记】Unity光照-光照衰减

Unity —共支持 4 种光源类型:

  • 平行光
  • 点光源(Point Light)
  • 聚光灯(Spot Light)
  • 面光源(area light)

面光源仅在烘焙时才可发挥作用, 因此不在本节讨论范围内。

本节中, 我们学习如何处理点光源(pointlight) 和聚光灯(spotlight)。

1.1、光源类型

平行光的几何属性只有方向,不会衰减。

由空间的一个球体定义的光源。

点光源会存在光线衰减,中心点为1,边缘点为0;中间的衰减可以由一个函数定义。

这三种中最复杂的一种。照亮空间内的一个锥形区域。

中间的衰减同样可以通过函数定义,只是更加复杂。

Unity 使用一张纹理作为査找表(Lookup Table, LUT),在片元着色器中计算逐像素光照的衰减。
这样做减轻了计算压力,但是也有缺点:

  • 需要预处理得到采样纹理, 而且纹理的大小也会影响衰减的精度。
  • 不直观, 同时也不方便, 因此一旦把数据存储到査找表中, 我们就无法使用其他数学公式来计算衰减。

2.1、用于光照衰减的纹理

Unity 在内部使用一张名为 _LightTexture0 的纹理来计算光源衰减。

如果我们对该光源使用了 cookie,那么衰减査找纹理是 _LightTextureB0

我们通常只关心 _LightTexture0 对角线上的纹理颜色值。
例如:(0,0)点表明了与光源位置重合的点的衰减值, 而(1,1)点表明了在光源空间中所关心的距离最远的点的衰减值。

2.2、使用纹理采样

使用 unity_WorldToLight 矩阵可以把顶点从世界空间变换到光源空间。
将其与世界空间下的顶点坐标相乘即可。

float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;

使用这个坐标的模的平方对衰减纹理进行采样, 得到衰减值:

fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;

使用了光源空间中顶点距离的平方( 通过 dot 函数来得到)来对纹理采样, 之所以没有使用距离值来采样是因为这种方法可以避免开方操作。

然后, 我们使用宏 UNITY_ATTEN_CHANNEL 来得到衰减纹理中衰减值所在的分量, 以得到最终的衰减值。

2.3、使用公式采样

例如:

float distance = length(_WorldSpaceLightPosO.xyz - i.worldPosition.xyz);
atten = 1.0 / distance;

Unity 没有在文档中给出内置衰减计算的相关说明,也没有开放相关接口(如聚光灯的朝向、 张开角度等)。

当然,我们可以利用脚本将相关信息传递给 Shader,但是效率、灵活性就很差了。

3.1、在前向渲染中处理光源

首先定义第一个 Pass — BasePass 。

使用 #pragma multi_compile_fwdbase指令,保证光照衰减等光照变量可以被正确赋值。

在 BasePass 中计算场景中的环境光。

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

Base Pass 会在计算逐像素平行光时调用一次。
因为环境光、自发光只需要计算一次,因此放在 Base Pass 中计算。

如果场景中有多个平行光,Unity 会选择最亮的平行光传递给 Base Pass 进行逐像素处理。
其他平行光会按照逐顶点或在 Additional Pass 中按逐像素的方式处理。
如果场景中没有任何平行光, 那么 Base Pass 会当成全黑的光源处理。

使用 _WorldSpaceLightPos0 来得到这个平行光的方向。
使用 _LightColor0 来得到它的颜色和强度( _LightColor0 已经是颜色和强度相乘后的结果)。

随后定义 Addition Pass 。

使用 #pragma multi_compile_fwdadd指令,

使用 Blend 指令: Blend One One声明混合模式(因为我们不希望覆盖)。

使用判断来获得光源方向、处理衰减。

最终代码如下:

Shader "Unity Shaders Book/Chapter 9/ForwardRendering" {
    Properties {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader {
        Tags { "RenderType"="Opaque" }

        Pass {
            // Pass for ambient light & first pixel light (directional light)
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM

            // Apparently need to add this declaration
            #pragma multi_compile_fwdbase

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

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

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                fixed atten = 1.0;//光照衰减

                return fixed4(ambient + (diffuse + specular) * atten, 1.0);
            }

            ENDCG
        }

        Pass {
            // Pass for other pixel lights
            Tags { "LightMode"="ForwardAdd" }

            Blend One One

            CGPROGRAM

            // Apparently need to add this declaration
            #pragma multi_compile_fwdadd

            #pragma vertex vert
            #pragma fragment frag

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

            fixed4 _Diffuse;
            fixed4 _Specular;
            float _Gloss;

            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

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

                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);

                //判断是否是平行光,获得世界坐标下光线方向
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
                #else
                    fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
                #endif

                fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                //判断是否是平行光,处理衰减
                #ifdef USING_DIRECTIONAL_LIGHT
                    fixed atten = 1.0;
                #else
                    #if defined (POINT)
                        float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
                        fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #elif defined (SPOT)
                        float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
                        fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #else
                        fixed atten = 1.0;
                    #endif
                #endif

                return fixed4((diffuse + specular) * atten, 1.0);
            }

            ENDCG
        }
    }
    FallBack "Specular"
}

效果如下:能够实现非平行光源的光照衰减。

3.2、Base Pass 与 Additional Pass 的调用

Unity 中,我们可以使用帧调试器(Frame Debugger)工具来査看场景的绘制过程。

Window -> Analysis -> Frame Debugger

可以看到, Unity 首先清除颜色、深度和模板缓冲,为后面的渲染做准备;

随后 Unity 利用 Base Pass ,将平行光的光照渲染到帧缓存中;

最后调用 Additional Pass ,将点光源依次应用到物体上。

Unity 处理这些点光源的顺序是按照它们的重要度排序的。 重要度与颜色、强度、距离物体远近都有关系。

Original: https://www.cnblogs.com/Wu-765279087/p/16489234.html
Author: IDEA_W
Title: 【Unity Shader学习笔记】Unity光照-光照衰减

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590581/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 如何下载网页上的背景图片?

    如何下载网页上的背景图片?(以谷歌浏览器为例) 具体步骤(以研控为例) 一、登录网址 我们这里以网站 研控 为例,网址放在这里:https://www.yankong.org/ 我…

    Java 2023年6月5日
    0206
  • CSharp: Prototype Pattern in donet core 3

    调用: 输出: Original: https://www.cnblogs.com/geovindu/p/16750695.htmlAuthor: ®Geovin Du Dream…

    Java 2023年6月16日
    080
  • Mybatis逆向工程所生成的mapper接口时出现错误

    调用mybatis的mapper接口中的 selectByExample()方法抛出异常: java.lang.NoSuchMethodException: com&#x2…

    Java 2023年6月7日
    0112
  • wordpress固定链接+宝塔nginx配置伪静态访问URL

    一、站点设置 打开站点设置,选择伪静态,选择wordpress 二、wordpress设置 打开wordpress后台,选择 设置 —》固定链接 选择一个你喜欢的格式点…

    Java 2023年6月15日
    089
  • 双路快速排序以及三路排序算法

    目录 双路快速排序 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 三路排序算法 一、概念及其介绍 二、适用说明 三、过程图示 四、Java 实例代码 双路快…

    Java 2023年6月5日
    0103
  • 全网最新最详细最明白教程之Spring源码搭建,没有之一,超详细

    相关帖子有很多但是都不是最新的 Gradle,我在使用Gradle最新版编译的时候简直坑死我了,弄了好久。接下来给大家详细说一下这个安装过程,以及相关的软件版本号。 相关软件、依赖…

    Java 2023年6月7日
    093
  • pyton对Quoted-printable进行编码和解码

    前言 利用的是python的 quopri模块进行操作 一.加密 import quopri str = "你好世界" a = quopri.encodestr…

    Java 2023年6月13日
    0102
  • 数据结构与算法之线性查找

    线性查找 在一个无顺序的数组中找到第k大的元素是几. 这个问题最简单的解法是先将数组进行排序,然后返回下标k上的元素.如果使用上一节的归并排序则时间复杂度是O(nlogn) 那是否…

    Java 2023年6月8日
    086
  • jdk1.8 stream 求和

    BigDecimal:BigDecimal bb =list.stream().map(Plan::getAmount).reduce(BigDecimal.ZERO,BigDec…

    Java 2023年5月30日
    056
  • H2-Table CATALOGS not found

    在使用 IntelliJ IDEA 2021.1.3 版本,使用默认配置连接 H2 数据库的时候,出现下面错误,项目里 H2 使用的版本为 2.0.202 。 [42S02][42…

    Java 2023年6月8日
    074
  • SQL表的创建

    1.使用鼠标创建表 1,进入SQL进行连接 2,在左边会有一个对象资源管理器,右键数据库,在弹出的窗口中选择新建数据库 3,给这个包取个名字,在这个界面可以给这个表选择存储地方,如…

    Java 2023年6月8日
    075
  • springboot定时任务线程阻塞踩坑

    场景描述 在使用 Springboot整合定时任务,发现当某个定时任务执行出现执行时间过长的情况时会阻塞其他定时任务的执行。 问题定位 后续通过翻查 Springboot的文档以及…

    Java 2023年5月30日
    0110
  • 2.搭建一个spring-boot项目(持续更新)

    很多同学在搭建一个springboot项目的时候会遇到很多问题,闲来无事我就自己搭建了一个基础的框架,大家可以自己看看。 框架主要包括: 初始化配置 数据库配置 Mysql myb…

    Java 2023年6月9日
    092
  • idea中无法使用EL表达式怎么处理

    posted @2022-08-05 13:39 红酒人生 阅读(33 ) 评论() 编辑 Original: https://www.cnblogs.com/holly8/p/1…

    Java 2023年6月13日
    073
  • spring 动态设置 RequestMapping

    原理:自定义 HandlerMapping ,把请求都集中到一个controller中 定义自己的 HandlerMapping 定义controller 添加到容器中 根据bea…

    Java 2023年5月30日
    075
  • 69.修炼爱情

    dfsd posted @2022-09-28 08:46 随遇而安== 阅读(17 ) 评论() 编辑 Original: https://www.cnblogs.com/55z…

    Java 2023年6月7日
    068
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球