Unity中的光源类型(向前渲染路径进行光照计算)
Unity中的光源类型
Unity中共支持4种光源类型:
- 平行光
- 点光源
- 聚光灯
- 面光源(在光照烘焙时才可以发挥作用)
光源的属性:
- 位置
- 方向(到某个点的方向)
- 颜色
- 强度
- 衰减(到某个点的衰减)
平行光
平行光的几何定义是最简单的,平行光可以照亮的范围是无限远的,且对与场景中的各个点的方向和强度都是一致的。在场景中作为太阳这样的角色出现。
点光源
点光源照亮的空间是有限的,它是由空间中的一个球体定义的。其可以表示由一个点发出的、向所有方向延伸的光。
需要注意的是点光源的方向属性是由某个点减去点光源位置所得出的向量,表示点光源在该点的光照方向。点光源会衰减,随着物体逐渐原理点光源,其接收到的光照强度也会逐渐减小。
聚光灯
聚光灯是这3种光源类型中最复杂的一种。它的照亮空间同样是有限的,但不再是简单的球体,而是由空间中的一块锥形区域定义的。聚光灯可以用于表示由一个特定位置出发、向特定方向延伸的光。
这块锥形区域的半径由面板中的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),以在片元着色器中得到光源的衰减。我们首先得到光源空间下的坐标,然后使用该坐标对衰减纹理进行采样得到衰减值。
注:本文为冯乐乐《Unity Shader入门精要读书笔记》
Unity中的光源类型(向前渲染路径进行光照计算)的更多相关文章
- 关于Unity中的光照(四)
渲染路径和颜色空间 1:Unity光影效果可以通过设置 渲染路径和颜色空间;2: 渲染路径: 光照到物体表面,物体着色的时候,算上光的颜色的时候有这么几种光照的着色方式,计算着色的方式 forward ...
- 用体渲染的方法在Unity中渲染云(18/4/4更新)
github: https://github.com/yangrc1234/VolumeCloud 更新的内容在底部 最近在知乎上看到一篇文章讲云层的渲染(https://zhuanlan.zhihu ...
- [Unity Shader笔记]渲染路径--Forward渲染路径
[Unity Shader笔记]渲染路径--Forward渲染路径 (2014-04-22 20:08:25) 转载▼ 标签: shader unity renderingpath forward 游 ...
- Unity Lighting - Choosing a Rendering Path 选择渲染路径(三)
Choosing a Rendering Path 选择渲染路径 Unity supports a number of rendering techniques, or ‘paths’. An i ...
- 在Unity中渲染一个黑洞
在Unity中渲染一个黑洞 前言 N年前观看<星际穿越>时,被其中的"卡冈图雅"黑洞所震撼.制作团队表示这是一个最贴近实际的黑洞效果,因为它是通过各种科学理论实现的.当 ...
- unity渲染路径
(1) deferred shading:有最佳的光照和阴影效果,在场景中存在许多的实时光照时,使用deferred shading也是最佳的方案,之所以叫做deferred(延迟),是因为 ...
- 关于Unity中混合模式、Alpha测试、深度测试、通道遮罩、面剔除的使用----渲染通道通用指令(二)
混合模式 着色完成后,需要把颜色混合到帧缓冲区里面,涉及到源和目标. 1:在所有计算完成后,决定当前的计算结果输出到帧缓冲区时,如何混合源和目标,通常用来绘制半透明的物体;2: Blend Off 关 ...
- Unity中几个特殊路径在各个平台的访问方式
1.文件路径Resources:Unity在发布成移动端项目后,其他文件路径都将不存在,但是如果有一些必要的资源,可以放在Resources文件夹下,因为这个文件夹下的所有资源是由Unity内部进行调 ...
- Unite 2018 | 《崩坏3》:在Unity中实现高品质的卡通渲染(上)
http://forum.china.unity3d.com/thread-32271-1-1.html 我们已经发布了Unite 2018 江毅冰的<发条乐师>.Hit-Point的&l ...
- Unite 2018 | 《崩坏3》:在Unity中实现高品质的卡通渲染(下)
http://forum.china.unity3d.com/thread-32273-1-1.html 今天我们继续分享米哈游技术总监贺甲在Unite Beijing 2018大会上的演讲<在 ...
随机推荐
- 学习笔记--Java 运算符
Java 运算符 算术运算符 关系运算符 逻辑运算符 位运算[略] 赋值运算符 字符串连接符 三元运算符 Java 运算符 按照功能划分: 功能 运算符 算术运算符 +.-.*./.++.--.% 关 ...
- 游戏开发进行中UE5引擎打不开后续
游戏每次启动都有个问题: 之前我实现了插件里的接口,但是已启动,关于接口这一块的就消失了,有些函数还在但是却是自定义事件,不是接口里的,Class Settings里面也提了 然后我把他改成了新的ch ...
- 在Django REST framework (DRF) 中,`request.query_params` 和 `request.data` 区别
在Django REST framework (DRF) 中,request.query_params 和 request.data 都是用来获取请求中的数据,但是它们之间有一些关键的区别: requ ...
- 5/15课下作业:评价一下steam软件
用户界面: 登录后会弹出特惠广告,广告内容可能不常用.主界面简洁方便,启动游戏,购买游戏,浏览社区,浏览自己内容一目了然 记住用户选择: 登录一次后会记住用户的账户密码,可以直接进行用户间的切换,会记 ...
- Net8将Serilog日志推送ES,附视频
这是一个Serilog的实践Demo,包括了区别记录存放,AOP 日志记录,EF 执行记录,并且将日志推送到Elastic Search. 说在前面的话 自从AI出来之后,学习的曲线瞬间变缓了,学习的 ...
- Fiddler篡改请求和响应数据
Fiddler标记断点后,我们可以通过篡改请求或响应数据,来模拟客户端请求和服务器响应. 一.打断点的方式 1.1 工具栏设置断点 工具栏勾选断点类型进行断点,路径:Rules->Automat ...
- 对比python学julia(第三章:游戏编程)--(第四节)捕鱼达人(5)
4.3. 编程实现 (续上) 3. 实现射击捕鱼 在第 3 个阶段,将按照"编程思路"中介绍的射击捕鱼的算法进行编程,实现让玩家操控大炮射击捕鱼.在"bydr&qu ...
- 【Java】Reflection 反射机制 02获取类的一切
先创建一个可演示的类 注解类 package cn.dai.Reflection.demo; import java.lang.annotation.ElementType; import java. ...
- 【JS】07 JS对象
所有事物都是对象 JavaScript 提供多个内建对象,比如 String.Date.Array 等等. 对象只是带有属性和方法的特殊数据类型. 布尔型可以是一个对象. 数字型可以是一个对象. 字符 ...
- 【UEditor】富文本编辑器 简单上手
富文本编辑器是一个使用前端组件渲染的文本编辑器 功能强大,交互友好,我们写评论,写文章,一些文本编辑的地方就会使用这种编辑器插件 另外在官方文档的下面还提到了我们这个插件的一些后端软件[指Java]要 ...