首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

Unity中的光源类型(向前渲染路径进行光照计算)

编程知识
2024年09月23日 07:06

Unity中的光源类型

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

  • 平行光
  • 点光源
  • 聚光灯
  • 面光源(在光照烘焙时才可以发挥作用)

光源的属性:

  • 位置
  • 方向(到某个点的方向)
  • 颜色
  • 强度
  • 衰减(到某个点的衰减)
  1. 平行光

    平行光的几何定义是最简单的,平行光可以照亮的范围是无限远的,且对与场景中的各个点的方向和强度都是一致的。在场景中作为太阳这样的角色出现。

    img

  2. 点光源

    点光源照亮的空间是有限的,它是由空间中的一个球体定义的。其可以表示由一个点发出的、向所有方向延伸的光。

    img

    需要注意的是点光源的方向属性是由某个点减去点光源位置所得出的向量,表示点光源在该点的光照方向。点光源会衰减,随着物体逐渐原理点光源,其接收到的光照强度也会逐渐减小。

  3. 聚光灯

    聚光灯是这3种光源类型中最复杂的一种。它的照亮空间同样是有限的,但不再是简单的球体,而是由空间中的一块锥形区域定义的。聚光灯可以用于表示由一个特定位置出发、向特定方向延伸的光。

    img

​ 这块锥形区域的半径由面板中的Range属性决定,而锥体的张开角度由Spot Angle属性决定。我们同样也可以在 Scene视图中直接拖拉聚光灯的线框(如中间的黄色控制点以及四周的黄色控制点)来修改它的属性。聚光灯的位置同样是由Transform组件中的Position属性定义的。对于方向属性,我们需要用聚光灯的位置减去某点的位置来得到它到该点的方向。聚光灯的衰减也是随着物体逐渐远离点光源而逐渐减小,在锥形的顶点处光照强度最强,在锥形的边界处强度为0。其中间的衰减值可以由一个函数定义,这个函数相对于点光源衰减计算公式要更加复杂,因为我们需要判断一个点是否在锥体的范围内。

在向前渲染中处理不同的光照类型

Shader "Custom/ForwardRanderingLearn"
{
    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
        {
            //设置渲染模式
            Tags{ "LightMode"="ForwardBase" }
            
            CGPROGRAM
            //添加宏引用
            #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
        {
            Tags {"LightMode" = "ForwardAdd"}
            
            //开启混合模式
            Blend One One
            
            CGPROGRAM
            
            #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.0)).xyz;
                         fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
                    #elif defined (SPOT)
                         float4 lightCoord = mul(unity_WorldToLight,float4(i.worldPos,1.0)).xyz;
                         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"
   
}

在此shader中,在Base Pass中处理场景中最重要的平行光。

本场景中只有一个平行光,因此Base Pass只会执行一次。如果场景中包含多个平行光,Unity则会选择最亮的平行光传递给Base Pass进行逐像素处理,其它平行光会按照逐顶点或在Additional Pass中按照逐像素方式处理。

如果场景中没有任何平行光,那么Base Pass会当成全黑的光源处理。

对于Base Pass来说,它处理的逐像素光源类型一定是平行光。我们可以使用__WorldSpaceLightPos0来得到这个平行光的方向(位置对平行光来说没有意义),使用_LightColor0来得到它的颜色和强度(_LightColor0已经是颜色和强度相乘后的结果),由于平行光可以认为是没有衰减的,因此这里我们直接令衰减值为1.0。相关代码如下:

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

        ...

        //  Compute  specular  term
        fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)),
        _Gloss);

        //  The  attenuation  of  directional  light  is  always  1
        fixed  atten  =  1.0;

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

接下来,我们需要为场景中其他逐像素光源定义Additional Pass。为此,我们首先需要设置Pass的渲染路径标签:

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

            Blend  One  One

            CGPROGRAM

            //  Apparently  need  to  add  this  declaration
            #pragma  multi_compile_fwdadd

与Base Pass不同的是,我们还使用Blend命令开启和设置了混合模式。这是因为,我们希望Additional Pass计算得到的光照结果可以在帧缓存中与之前的光照结果进行叠加。如果没有使用Blend命令的话,Additional Pass会直接覆盖掉之前的光照结果。在本例中,我们选择的混合系数是Blend One One,这不是必需的,我们可以设置成Unity支持的任何混合系数。常见的还有Blend SrcAlpha One。

通常来说,Additional Pass的光照处理和Base Pass的处理方式是一样的,因此我们只需要把Base Pass的顶点和片元着色器代码粘贴到Additional Pass中,然后再稍微修改一下即可。这些修改往往是为了去掉Base Pass中环境光、自发光、逐顶点光照、SH光照的部分,并添加一些对不同光源类型的支持。因此,在Additional Pass的片元着色器中,我们没有再计算场景中的环境光。

因此在计算光源的5个属性——位置、方向、颜色、强度以及衰减时,颜色和强度我们仍然可以使用_LightColor0来得到,但对于位置、方向和衰减属性,我们就需要根据光源类型分别计算。首先,我们来看如何计算不同光源的方向:

        #ifdef  USING_DIRECTIONAL_LIGHT
              fixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);
        #else
              fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);
        #endif

处理不同光源的衰减:

        #ifdef  USING_DIRECTIONAL_LIGHT
            fixed  atten  =  1.0;
        #else
            float3  lightCoord  =  mul(_LightMatrix0,  float4(i.worldPosition,  1)).xyz;
            fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
        #endif

我们同样通过判断是否定义了USING_DIRECTIONAL_LIGHT来决定当前处理的光源类型。如果是平行光的话,衰减值为1.0。如果是其他光源类型,那么处理更复杂一些。尽管我们可以使用数学表达式来计算给定点相对于点光源和聚光灯的衰减,但这些计算往往涉及开根号、除法等计算量相对较大的操作,因此Unity选择了使用一张纹理作为查找表(Lookup Table, LUT),以在片元着色器中得到光源的衰减。我们首先得到光源空间下的坐标,然后使用该坐标对衰减纹理进行采样得到衰减值。

image-20240923080110029

注:本文为冯乐乐《Unity Shader入门精要读书笔记》

From:https://www.cnblogs.com/TonyCode/p/18426236
本文地址: http://www.shuzixingkong.net/article/2219
0评论
提交 加载更多评论
其他文章 记一次 RabbitMQ 消费者莫名消失问题的排查
开心一刻 今天好哥们找我借钱哥们:兄弟,我最近手头紧,能不能借我点...我:我手头也不宽裕,要不你试试银行贷款或者花呗?哥们:不行,那个借了要还的我:... 问题回顾 某天下午,生产监控告警:消息积压,队列 xxx 消息数超过 100;我第一时间想到的是应用服务是不是停了,但应用服务存活监控又没有告
记一次 RabbitMQ 消费者莫名消失问题的排查 记一次 RabbitMQ 消费者莫名消失问题的排查 记一次 RabbitMQ 消费者莫名消失问题的排查
大模型应用开发初探 : 快速直观感受RAG
检索增强生成(Retrieval Augmented Generation),简称 RAG,它是根据用户输入的提示词,通过自有垂域数据库检索相关信息,然后合并成为一个新的“完备的“提示词,最后再给大模型生成较为准确的回答。这一篇,我们来快速了解下RAG(检索增强生成)并通过一个简单的DEMO来直观感
大模型应用开发初探 : 快速直观感受RAG 大模型应用开发初探 : 快速直观感受RAG 大模型应用开发初探 : 快速直观感受RAG
SQL Server的Descending Indexes降序索引
SQL Server的Descending Indexes降序索引 背景索引是关系型数据库中优化查询性能的重要手段之一。对于需要处理大量数据的场景,合理的索引策略能够显著减少查询时间。 特别是在涉及多字段排序的复杂查询中,选择合适的索引类型(如降序索引)显得尤为重要。本文将探讨如何在SQL Serv
SQL Server的Descending Indexes降序索引 SQL Server的Descending Indexes降序索引 SQL Server的Descending Indexes降序索引
【解决方案】Java 互联网项目中常见的 Redis 缓存应用场景
本文梳理总结了一些 Java 互联网项目中常见的 Redis 缓存应用场景,例如常见的 String 类型 Key-Value、对时效性要求高的场景、Hash 结构的场景以及对实时性要求高的场景等,全面涵盖了 Redis 中所有的 5 种基本类型。
Redis 内存突增时,如何定量分析其内存使用情况
背景 最近碰到一个 case,一个 Redis 实例的内存突增,used_memory最大时达到了 78.9G,而该实例的maxmemory配置却只有 16G,最终导致实例中的数据被大量驱逐。 以下是问题发生时INFO MEMORY的部分输出内容。 # Memoryused_memory:
反问面试官3个ThreadLocal的问题
接下来,我想先说说ThreadLocal的用法和使用场景,然后反问面试官3个关于ThreadLocal的话题。
反问面试官3个ThreadLocal的问题 反问面试官3个ThreadLocal的问题 反问面试官3个ThreadLocal的问题
使用 fabric.js 开发移动端 H5 图片编辑器
移动端 H5 图片图片编辑编辑器开发实践细节,包括自定义字体、模板切换、特效字体、文字排版等功能,手把手教你开发图片编辑器。
使用 fabric.js 开发移动端 H5 图片编辑器 使用 fabric.js 开发移动端 H5 图片编辑器 使用 fabric.js 开发移动端 H5 图片编辑器
GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复
前言 时隔大半年,终于抽出空来可以更新这个组件了 (边缘化了,大概要被裁员了) 2.7.0终于发布了~ 更新内容: 1.添加API类任务的超时时间,可以通过全局配置也可以单个任务设置 2.设置定时任务日志查看默认按开始时间倒序 3.添加是否显示控制台日志的全局配置 目前支持两个参数 Sho
GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复 GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复 GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复