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

news/2024/11/17 22:43:37/文章来源:https://www.cnblogs.com/TonyCode/p/18426236

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 OneCGPROGRAM#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_LIGHTfixed 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;#elsefixed atten = 1.0;#endif#endifreturn 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  termfixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));...//  Compute  specular  termfixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)),_Gloss);//  The  attenuation  of  directional  light  is  always  1fixed  atten  =  1.0;return  fixed4(ambient  +  (diffuse  +  specular)  *  atten,  1.0);

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

        Pass  {//  Pass  for  other  pixel  lightsTags  {  "LightMode"="ForwardAdd"  }Blend  One  OneCGPROGRAM//  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_LIGHTfixed3  worldLightDir  =  normalize(_WorldSpaceLightPos0.xyz);#elsefixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);#endif

处理不同光源的衰减:

        #ifdef  USING_DIRECTIONAL_LIGHTfixed  atten  =  1.0;#elsefloat3  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入门精要读书笔记》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/801978.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Adobe Photoshop 2024 v25.12 (macOS, Windows) 发布下载 - 照片和设计软件

Adobe Photoshop 2024 v25.12 (macOS, Windows) 发布下载 - 照片和设计软件Adobe Photoshop 2024 v25.12 (macOS, Windows) - 照片和设计软件 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesig…

C#/.NET/.NET Core技术前沿周刊 | 第 6 期(2024年9.16-9.22)

前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。欢迎投稿,推荐或自荐优质文章/项目/学习资源等。…

基础数据结构之数组

数组 1) 概述 定义 在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识In computer science, an array is a data structure consisting of a collection of elements (values or variables), each identified by at least one ar…

网站搭建

第一步,服务器申请选择一:免费云服务器,免费虚拟主机 如:阿贝云阿贝云提供了免费的云服务器和免费的云虚拟主机,可根据自己的实际应用情况选择。 首先注册一个账户,然后需要支付0.3元做一个实名认证,如果实名认证成功了大概率会开通成功。如果失败了可能是服务器资源池不…

读构建可扩展分布式系统:方法与实践12分布式数据库案例

分布式数据库案例1. Redis 1.1. 2009年首次发布1.1.1. 更注重原始性能和简单性,而不是数据安全性和一致性1.2. 主要吸引力在于它能够同时充当分布式缓存和数据存储 1.3. 维护一个内存中的数据存储,也称为数据结构存储(data structure store) 1.4. 配置Redis将每个命令记录到一…

Java代码审计篇 - ofcms系统审计思路讲解 - 篇2 - SQL注入漏洞审计

Java代码审计篇 | ofcms系统审计思路讲解 - 篇2 | SQL注入漏洞审计 1. 前言 我发现很多文章包括教程,大概套路是:只说有漏洞的点,将有漏洞的点指出,然后分析代码;或者黑盒测试出漏洞之后,然后分析代码。 我认为这是在分析漏洞代码,而非代码审计。代码审计文章或教程应该…

Java代码审计篇 - ofcms系统审计思路讲解 - 篇3 - 文件上传漏洞审计

Java代码审计篇 | ofcms系统审计思路讲解 - 篇3 | 文件上传漏洞审计 1 文件上传代码审计【有1处】 文件上传漏洞我们需要着重关注的是文件在被java代码解析到保存下来之间有无验证过滤,因此什么样的上传方式,什么样的保存方式都不重要,大家着重关注代码对文件的验证过滤手段…

Java代码审计篇 - ofcms系统审计思路讲解 - 篇4 - XXE漏洞审计

Java代码审计篇 | ofcms系统审计思路讲解 - 篇4 | XXE漏洞审计 1. XXE代码审计【有1处】 XXE代码审计常搜索的关键字如下: XMLReader SAXBuilder SAXReader SAXParserFactory Digester DocumentBuilderFactory ...还有一个特殊的,用于加载.jrxml 文件,这是 JasperReports 特…

Java反序列化利用链篇 | CC6链分析(通用版CC链)

CC6 CC6和CC1之间的区别 在CC1的LazyMap链中,调用链如下: AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() LazyMap.get() ChainedTransformer.transform() InvokerTransformer.transform() Runtime.exec()而在CC1链中,对CommonsCollections和jdk版本是有限…

Java反序列化利用链篇 | CC3链分析、TemplatesImpl类中的调用链、TrAXFilter、InstantiateTransformer类的transform()【本系列文章的分析重点】

CC3链分析 1. CC3链背景 前面介绍了CC1和CC6,这两条链子虽然前面的入口类不同CC1入口类是AnnotationInvocationHandler CC6入口类是HashMap但是其触发恶意代码的方式是相同的,都是InvokerTransformer.transform()触发Runtime.getRuntime().exec()实现命令执行。而在很多情况下…

VUE学习day one

学习来源:【前端最新Vue2+Vue3基础入门到实战项目全套教程,自学前端vue就选黑马程序员,一套全通关!】https://www.bilibili.com/video/BV1HV4y1a7n4?vd_source=6dac49feb8d7fd76b147c8cf8c0c2b5a Vue是什么?Vue是一个用于构建用户界面(基于数据动态渲染出用户看到的页面)…