首页 > 其他分享 >Unity中的光源类型(向前渲染路径进行光照计算)

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

时间:2024-09-25 12:49:02浏览次数:7  
标签:渲染 光源 xyz fixed3 Unity worldNormal Pass 衰减 光照

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

合集 - Unity Shader(2)   1.Unity中的三种渲染路径09-16 2.Unity中的光源类型(向前渲染路径进行光照计算)09-23 收起  

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入门精要读书笔记》

标签:渲染,光源,xyz,fixed3,Unity,worldNormal,Pass,衰减,光照
From: https://www.cnblogs.com/sexintercourse/p/18431097

相关文章

  • 『不走弯路』Unity学习路线(主程方向)
    『不走弯路』Unity学习路线(主程方向)大智Unity技术探路者/VX:zhz11235关注阅读6034 2023年4月24日 不走弯路,学Unity从小白到大师的路线是什么?本学习路线由大智(vx:zhz11235)凭个人经验编写,仅供参考,欢迎找大智交流(......
  • ⭐ Unity 对象池的应用 Cube流星落
    此次Demo里生成一些Cube从天上往下掉,并且当它们掉到特定高度(例如y轴小于0)时销毁。为了优化性能,避免频繁创建和销毁物体,使用对象池(ObjectPooling)技术来复用这些Cube。先看一下效果观察Cube的生成Cube对象池 对象池管理器脚本usingSystem.Collections.Generi......
  • 【Unity】绘制折线图和柱状图
    绘制折线图和柱状图,主要包括如下效果:背景网格的绘制;折线和拐点的绘制;长方形柱的绘制(柱宽可以修改);X/Y轴的标签绘制(标签的单位可以修改、X轴的间距可以修改);鼠标移动到折线拐点/长方形柱是显示对应数值Tooltip;成果展示Scene部分脚本部分定义折线图和柱状图的接口//折线......
  • Python实现图形学光照和着色的Gouraud着色算法
    目录使用Python实现图形学光照和着色的Gouraud着色算法引言1.Gouraud着色算法概述1.1算法原理2.Python实现Gouraud着色算法2.1向量类2.2光源类2.3材质类2.4Gouraud着色器类2.5使用示例3.实例分析4.Gouraud着色算法的优缺点4.1优点4.2缺点5.改进方向6.应......
  • blender设置背景图怎么添加?blender云渲染选择
    Blender是一款功能强大的3D建模软件,它以流畅的操作体验和直观的用户界面而闻名。使用Blender,你可以轻松地为你的3D模型添加背景图片。以下是具体的操作步骤:1、启动Blender:首先,打开Blender软件。访问添加菜单:在Blender的界面顶部,找到并点击“添加”菜单。2、选择背景图像:在“......
  • 【unity进阶知识1】最详细的单例模式的设计和应用,继承和不继承MonoBehaviour的单例模
    文章目录前言一、不使用单例二、普通单例模式1、单例模式介绍实现步骤:单例模式分为饿汉式和懒汉式两种。2、不继承MonoBehaviour的单例模式2.1、基本实现2.2、防止外部实例化对象2.3、最终代码3、继承MonoBehaviour的单例模式3.1、基本实现3.2、自动创建和挂载单例脚本......
  • 【Unity】 HTFramework框架(五十六)MarkdownText:支持运行时解析并显示Markdown文本
    更新日期:2024年9月15日。Github源码:[点我获取源码]Gitee源码:[点我获取源码]索引MarkdownText支持的Markdown语法标题强调文本表格嵌入图像超链接使用MarkdownText设置项运行时属性解析使用ID模式嵌入图像MarkdownTextMarkdownText为UGUIText的扩展加强版,支持在运行时解析并显......
  • [干货] [非基础警告] Unity 发布-订阅模式下的事件中心设计
    本文师承于唐老师,但是修改了一些代码,采用更加方便理解的方式设计 1.什么是事件中心2.可以比喻成冒险者工会的任务板子 任务板子上面有发布任务和任务完成两种基础情况2.1发布任务来个冒险者发布一个任务,就执行Pulishtask方法这个方法中包含了任务名字和完成后的需要......
  • 【Unity精品源码】JU TPS 3 – 完整的第三人称射击游戏模板
    ......
  • C++和OpenGL实现3D游戏编程【连载11】——光照效果进阶
    1、本节要实现的内容我们在前面的章节里内容简单的介绍了一下光照,随着后期对纹理内容的增加,我们需要了解更多的光照知识,本节我们回顾一下光照相关内容,并了解一下怎样实现纹理的光照效果。下面这个图就是我们借助于纹理文字产生的半透明光照效果。半透明纹理文字光照演......