更复杂的光照
效果图
【多光源前向渲染】
Unity 处理这些点光源的顺序是按照它们的重要度排序的,和光源强度、颜色、距离物体的远近有关系
原顺序:
红色point light的intensity改为2:
把红光设置为not important
unity不会把该光源当成逐像素处理,由于代码的Bass Pase 中没有计算逐顶点和 SH 光源,那么红光上不会对物体造成任何影响
(消失的小红)
【阴影】
小球的Cast Shadows On ,地面Recieve Shadows On
小球的Cast Shadows Off ,或者地面Recieve Shadows Off, 或者注销FallBack
默认情况下把物体渲染到 深度图 和 阴影映射纹理 中仅考虑物体的正面
正 VS 反 VS 反Two Sided
【让物体接收阴影】
【Alpha Test 中实现阴影】
Cast Shadows On VS Cast Shadows Two Sided
【Alpha Blend 中实现阴影】
FallBack “Transparent/VertexLit” VS FallBack “VertexLit”
理论
unity的光源类型
【光源类型】:平行光、点光源(point light)、聚光灯(spot light)和面光源(area light,仅在烘焙时才可发挥作用)
【光源属性】位置、方向、颜色、强度、衰减
平行光:
属性只有方向,也没有衰减的概念,光照强度不会随着距离而发生改变
点光源:
由空间中的球体定义
聚光灯:
是3种光源类型中最复杂的 ,由空间中的一块锥形区域定义的
``
unity的渲染路径
渲染路径 (Rendering Path) 决定了光照是如何应用到 Unity Shader 中,支持的有前向渲染路径 (Forward Rendering Path) 、延迟渲染路径 (Deferred Rendering Path)。
大多数情况下,一个项目只使用一种渲染路径,默认为前向渲染路径,可以在Edit – Project Settings – Player – Other Settings – Rendering Path 中选择项目所需的渲染路径,如果当前的显卡并不支持所选择的渲染路径,会自动使用更低一级的渲染路径。然后,就可以在每个 Pass 中使用LightMode 标签来指定该 Pass 使用的渲染路径,通过 Unity提供的内置光照变量来访问相关的光照属性。
前向渲染路径
1、更新颜色缓冲区,深度缓冲区
2、如果一个物体在多个逐像素光源的影响区域内,那么该物体就需要执行多个 Pass, 每个 Pass 计算一个逐像素光源的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值。
假设场景中有N个物体,每个物体受M个光源的影响,那么渲染整个场景一共需要 N*M Pass 。
前向渲染的问题是当场景中包含大量实时光源时 ,前向渲染的性能会急速下降。如果我们在场景的某一块区域放置了多个光源,每执行一个 Pass 都需要重新渲染一遍物体,但很多计算实际上是重复的
3、前向渲染路径有3种处理光照(即照亮物体)的方式: 逐顶点处理、逐像素处理,球谐函数 (Spherical Harmonics, SH) 处理。
unity会对这些光源进行一个重要度排序,根据光源的类型和渲染模式,一定数目的光源会按逐像素的方式处理,然后最多有4个光源按逐顶点的方式处理,剩下的光源可以按SH方式处理。
1)场景中最亮的平行光总是按逐像素处理的。
2)渲染模式被设置成Important的光源, 会按逐像素处理,NotImportant的光源,会按逐顶点或者SH处理。
3)如果根据以上规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量(Pixel
Light Count), 会有更多的光源以逐像素的方式进行渲染
4、前向渲染有两种Pass: Base Pass和AdditionalPass
• 一个Unity Shader通常会定义一个Base Pass (需要双面渲染等情况除外)以及一个Additional Pass。 一个Base Pass仅会执行一次, 而每个影响该物体的其他逐像素光源都会执行一次AdditionalPass
• Base Pass
环境光和自发光是在Base Pass中计算的。如果我们在AdditionalPass中计算这两种光照, 就会造成叠加多次环境光和自发光
渲染的平行光默认是支持阴影的(如果开启了光源的阴影功能)
• Additional Pass
开启和设置了混合模式(一般是Blend One One)。 每个AdditionalPass可以与上一次的光照结果在帧缓存中进行叠加,否则会覆盖掉之前的渲染结果,看起来就好像该物体只受该光源的影响。
渲染的光源在默认情况下是没有阴影效果的, 即便我们在它的Light组件中设置了有阴影的Shadow Type。
5、内置光照变量和函数
延迟渲染路径
链接
1、延迟渲染的效率不依赖于场景的复杂度,而是和我们使用的屏幕空间的大小有关;适合在场景中光源数目很多、如果使用前向渲染会造成性能瓶颈的情况下使用。
2、延迟渲染主要包含了两个 Pass :
第一个Pass 用于渲染G缓冲
我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中(物体的漫反射颜色、高光反射颜色、平滑度 、法线、 自发光和深度等信息)
第二个Pass用于计算真正的光照模型
使用上一个Pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲中
3、缺点。
• 不支持真正的抗锯齿MSAA 功能。
• 不能处理半透明物体。
• 对显卡有一定要求。如果要使用延迟渲染的话,显卡必须支持 MRT ( Multiple Render Targets) Shader Mode 3.0 及以上、 深度渲染纹理以及双面的模板缓冲。
4、
实验
多光源
本示例处理一个平行光,三个点光源
1、Base Pass
选择最亮的平行光进行逐像素处理,其他平行光会按照逐顶点,或在 Additional Pass 中按逐像素的方式处理
计算一次环境光和自发光
方向不变,衰减为1
2、Additional Pass
其他每个光源调用一次该Pass:可以是平行光、点光源或是聚光灯(除了面光源),默认情况下1个物体可以接收除最亮的平行光外的4个逐像素光照
开启并设置混合模式
根据光源类型计算方向和衰减(利用衰减纹理)
【代码解析】
- Base Pass
Properties{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
_Specular("Specular", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
Pass { // base Pass
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma multi_compile_fwdbase,该编译指令可以保证在 Shader 中使用光照衰减等光照变量时被正确赋值
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
VS略
FS
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;// 只需要计算一次即可,后面的 Additional Pass不会再计算(环境光+自发光)
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
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); // 此处多×了一个atten
}
return fixed4(ambient + (diffuse + specular) * atten, 1.0);
环境光和自发光(虽然这里没有)算一次
atten = 1,平行光没有衰减
atten 影响 diffuse + specular
- Additional Pass
- 去掉 Base Pass 中环境光、自发光、逐顶点光照、SH 光照的部分
Pass { // Additional Pass
// Pass for other pixel lights
Tags { "LightMode" = "ForwardAdd" }
Blend One One // 开启和设置了混合模式
CGPROGRAM
#pragma multi_compile_fwdadd
开启和设置了混合模式,Additional Pass 计算得到的光照结果可在帧缓存中与之前的光照结果进行叠加
VS略
FS
光源属性:位置、方向、颜色、强度以及衰减,颜色和强度可以使用_LightColor0来得到
计算不同光源的方向
#ifdef USING_DIRECTIONAL_LIGHT // 平行光
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); // 光源位置-顶点位置
#endif
平行光:_WorldSpaceLightPos0.xyz
其他光:光源位置-顶点位置
计算不同光源的衰减
#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; // 这个坐标的模的平方对_LightTexture0进行采样,得到衰减值 // 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
使用一张纹理作为查找表 (Lookup Table, LUT)得到光源的衰减,不依赖数学公式的复杂性。 首先得到光源空间下的坐标,然后使用坐标的模的平方对衰减纹理进行采样,得到衰减值:
如果一个物体不在一个光源的光照范围内, Unity 是不会为这个物体调用Pass 来处理这个光源的
阴影
使用Shadow Map(阴影映射纹理, 本质上是深度图), 光看不到的地方就是阴影
- 在计算阴影映射纹理时,我们如何判定距离它最近的表面位置呢?
一种方法是,先把摄像机放置到光源的位置上,然后按正常的渲染流程,即调用 Base Pass、dditional Pass 来更新深度信息,得到阴影映射纹理。
但这种方法很浪费,因为我们实际上仅仅需要深度信息而已,而 Base Pass dditional Pass 中往往涉及很多复杂的光照模型计算
因此 Unity选择使用LigbtMode 标签为ShadowCaster的Pass,这个 Pass 的渲染目标不是帧缓存,而是阴影映射纹理(或深度纹理)
Unity 首先把摄像机放置到光源的位置上,然后调用该 Pass, 通过对顶点变换后得到光源空间下的位置,并据此来输出深度信息到阴影映射纹理中
因此,当开启了光源的阴影效果后,底层渲染引擎首先会在Unity Shader 里的Pass和 Fallback 中找到LigbtMode为ShadowCaster的Pass。
- 一个物体接收来自其他物体的阴影,以及它向其他物体投射阴影的原理
1)一个物体接收来自其他物体的阴影
必须在 Shader 中: 采样的阴影映射纹理 × 光照结果
2)一个物体向其他物体投射阴影
就必须把该物体加入到光源的阴影映射纹理的计算中,从而让其他物体对阴影映射纹理采样时可以得到该物体的相关信息。
- 屏幕空间的阴影映射技术 (Screenspace Shadow Map)
Unity 首先会通过调用 LigbtMode 为shadowCaster的Pass来得到可投射阴影的光源的阴影映射纹理以及摄像机的深度纹理。然后,根据光源的阴影映射纹理和摄像机的深度纹理来得到屏幕空间的阴影图
不透明物体的阴影
通过 Mesh Renderer 组件中的 Cast Shadows、Receive Shadows 属性设置某个物体投射或接收阴影
让物体接收阴影
在多光源的基础上修改Base Pass代码:
// Need these files to get built-in macros
#include "Lighting.cginc"
#include "AutoLight.cginc" // 增加
需要"AutoLight.cginc"
计算阴影中用到三个宏
输出结构体 v2f 中添加了一个内置宏 SHADOW_COORDS: 声明了 一个名为_ShadowCoord 的阴影纹理坐标变址
顶点着色器返回之前添加另一个内置宏 TRANSFER_SHADOW:调用内置的ComputeScreenPos函数来计算_ ShadowCoord
片元着色器中计算阴影值,同样使用了一个内置宏 SHADOW
ATTENUATION: 使用 _ShadowCoord对相关的纹理进行采样
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2) // 内置宏,声明一个用于对阴影纹理采样的坐标
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o); // 返回
return o;
}
VS
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
(ambient + (diffuse + specular) * atten * shadow, 1.0)
统—管理光照衰减和阴影
(ambient + (diffuse + specular) * atten * shadow, 1.0)
再来一遍这个式子,光照衰减和阴影对物体最终的渲染结果的影响本质上是相同的,unity Shader提供了这样的功能,通过内置的 UNITY_LIGHT _ATTENUATION 同时计算两个信息
UNITY _LIGHT _ATTENUATION是Unity 内置的用于计算光照衰减和阴影的宏。Unity 内置文件的魅力所在,不需要在 Base Pass 里单独处理阴影 ,也不需要在 Additional Pass 中判断光源类型来处理光照哀减
透明度物体的阴影
- 在alphaTest中实现阴影
1、FallBack “VertexLit” 中提供的 ShadowCaster 来投射阴影,并没有进行任何透明度测试的计算,而Transparent/Cutout/VertexLit 同时具有透明度测试和_Cutoff属性
2、默认情况下把物体渲染到 深度图 和 阴影映射纹理 中仅考虑物体的正面,所以要在面板中设置为two side
- 在alphaBlend中实现阴影
透明度混合需要关闭深度写入 由此也影响了阴影的生成。 在Unity 中,所有内置的半透明 Shader 是不会产生任何阴影效果的
通过把它们的 Fallback 设置为VertexLit、Diffuse来强制为半透明物体生成阴影
代码
多光源
// 本示例处理一个平行光,四个点光源
//
// base Pass,
// 处理平行光,如果有多个平行光,选择最亮的进行逐像素处理,其他平行光会按照逐顶点 或在 Additional Pass 中按逐像素的方式处理
// 计算一次环境光和自发光
// 方向不变,衰减为1
//
// Additional Pass
// 其他每个光源调用一次该Pass:可以是平行光、点光源或是聚光灯(除了面光源)
// 开启并设置混合模式
// 根据光源类型计算方向和衰减(利用衰减纹理) // 光源5属性:位置、方向、颜色、强度以衰减
//
// 认情况下1个物体可以接收除最亮的平行光外的4个逐像素光照
Shader "Custom/ForwardRenderingMat"
{
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 { // base Pass
// Pass for ambient light & first pixel light (directional light)
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdbase // 保证在Shader中使用光照衰减等光照变量
#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;// 只需要计算一次即可,后面的 Additional Pass不会再计算(环境光+自发光)
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); // 此处多×了一个atten
}
ENDCG
}
Pass { // Additional 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; // 这个坐标的模的平方对_LightTexture0进行采样,得到衰减值 // 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" // 默认有LightMode为ShadowCaster的Pass,所以可以透射阴影
}
阴影
接收阴影
// 在ForwardRenderingMat的基础上,实现阴影的接收
// 使用三个内置宏 SHADOW_COORDS,TRANSFER_SHADOW, SHADOW_ATTENUATION
Shader "Custom/shadow"
{
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
// Need these files to get built-in macros
#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;
SHADOW_COORDS(2) // 内置宏,声明一个用于对阴影纹理采样的坐标
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// Pass shadow coordinates to pixel shader
TRANSFER_SHADOW(o);
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;
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(ambient + (diffuse + specular) * atten * shadow, 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
// Use the line below to add shadows for point and spot lights
// #pragma multi_compile_fwdadd_fullshadows
#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 position : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
o.position = 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
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#endif
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
Alpha Test With Shadow
// 在alphaTest中实现阴影
// FallBack "VertexLit" 中提供的 ShadowCaster 来投射阴影,并没有进行任何透明度测试的计算
// ransparent/Cutout/VertexLit 同时具有透明度测试和_Cutoff属性
// ,默认情况下把物体渲染 深度图 和阴 影映射纹理中仅考虑物体的正面,面板中设置为two side
Shader "Unity Shaders Book/Chapter 9/Alpha Test With Shadow" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
}
SubShader {
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
SHADOW_COORDS(3) // 声明阴影纹理坐标,3代表是第四个插值寄存器
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
// Pass shadow coordinates to pixel shader
TRANSFER_SHADOW(o); // 用内置宏 TRANSFER_SHADOW 算阴影纹理坐标后传递给片元着色器
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
clip (texColor.a - _Cutoff); // 透明度测试
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
// UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); // 使用内置宏 UNITY_LIGHT_ATTENUATION 计算阴影和光照衰减:
return fixed4(ambient + diffuse * atten, 1.0);
}
ENDCG
}
}
FallBack "Transparent/Cutout/VertexLit"
// FallBack "VertexLit"
}
Alpha Blend With Shadow
// FallBack "Transparent/VertexLit" ---> FallBack "VertexLit"
Shader "Unity Shaders Book/Chapter 9/Alpha Blend With Shadow" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
SHADOW_COORDS(3)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
// Pass shadow coordinates to pixel shader
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
// UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + diffuse * atten, texColor.a * _AlphaScale);
}
ENDCG
}
}
// FallBack "Transparent/VertexLit"
// Or force to apply shadow
FallBack "VertexLit"
}