目录
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Unlit/ForwardAdd"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_DiffuseCol("DiffuseCol", Color) = (1,1,1,1)
[Toggle(ENABLE_SHADOW)] _EnableShadow("EnableShadow", Float) = 0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
Tags{
"LightMode" = "ForwardBase" //定义该Pass在Unity的流水线中的角色 (只有定义它才能获取到一些Unity内置的光照变量如_LightColor0
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc" //这个命名空间中包含_LightColor0
#include "AutoLight.cginc"//阴影和光源衰减值等信息需要
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float4 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _DiffuseCol;
//顶点着色器没有对任何逐顶点光源进行处理
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.normal = UnityObjectToWorldNormal(v.normal);
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 normal = normalize(i.normal);
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
#endif
//注意这是宏不是if条件判断,所以判断是非平行光的逐像素光源会进行另一种计算衰减值方法(从光照贴图中获取)
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#endif
fixed3 diffCol = _LightColor0.rgb * _DiffuseCol.rgb * saturate(dot(lightDir, normal));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(ambient + diffCol * atten * shadow, 1); //考虑平行光和环境光、自发光影响,不考虑其他逐像素光源
}
ENDCG
}
Pass
{
Tags{
"LightMode" = "ForwardAdd" //另一个前向渲染的光照模式,支持所有逐像素光源的计算
}
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//#pragma multi_compile_fwdadd //不考虑阴影的ForwardAdd模式
#pragma multi_compile_fwdadd_fullshadows //考虑阴影影响
#include "UnityCG.cginc"
#include "Lighting.cginc" //这个命名空间中包含_LightColor0
#include "AutoLight.cginc"//这个中包含世界转光源空间的空间转换矩阵unity_WorldToLight
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float4 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _DiffuseCol;
//顶点着色器没有对任何逐顶点光源进行处理
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.normal = UnityObjectToWorldNormal(v.normal);
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 normal = normalize(i.normal);
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
//注意这是宏不是if条件判断,所以判断是非平行光的逐像素光源会进行另一种计算衰减值方法(从光照贴图中获取)
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
#ifdef POINT
//针对Point光源(点光源)
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos.xyz, 1)).xyz; //世界空间转光源空间
//用上方获取到的坐标的长度平方作为纹理坐标值进行采样光照贴图,取其衰减值(其中一个分量)
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#elif SPOT
//针对Spot光源(聚光灯)
float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos.xyz, 1)); //世界空间转光源空间
fixed atten = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz);
#endif
#endif
fixed3 diffCol = _LightColor0.rgb * saturate(dot(lightDir, normal));//只考虑逐像素光源
fixed shadow = SHADOW_ATTENUATION(i);
//为什么只需要考虑逐像素光源,而不考虑环境光、自发光的影响?博客有说明
return fixed4(diffCol * atten * shadow,1);
}
ENDCG
}
}
}
简洁版
//计算阴影和衰减值的积存入atten变量(fixed类型),变量由宏来定义
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos) //不要写分号
UNITY_LIGHT_ATTENUATION(fixed, v2f, v2f.世界坐标点)
//下面这句话是错误的,2020年4月21日23:30:36更正
***其中,注意v2f结构体的裁剪坐标(SV_POSITION语义修饰的变量)必须命名为"pos"
如果你没有命名为pos,那么将会报错。***错误!
//2020年4月21日23:30:44更正:
第三个参数世界坐标点 必须是float3类型的,即i.worldPos.xyz传入,如果传入的是float4则报错
这个报错就是因为v2f结构体的裁剪坐标命名没有写为"pos"的缘故,猜测是宏定义更深层的会使用到pos命名的裁剪坐标(坑爹玩意)
阴影三剑客还有其他约束,如:
SHADOW_COORDS(n) 不要写分号在后面,n代表第几个TEXCOORDn
TRANSFER_SHADOW(o) 不要写分号在后面,o代表v2f结构体,而且顶点着色器输入结构必须命名为"v",且输入结构体中包含有命名为"vertex"的模型空间顶点坐标(蛋疼)不过默认基本上是v 以及 vertex ,所以这里一般都不会出错。
SHADOW_ATTENUATION(i); 要写分号在后面, i代表v2f输入结构体,这里就没啥约束了
---------------------------------------------
使用UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); 要写分号在后面,且v2f结构体的裁剪坐标命名为"pos",其他要求暂时未发现。
跳转---->更正文章部分
-------------------2020年4月21日23:28:56----------------------更正
报错是由于传入的UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)的第三个参数,不是float4,是float3类型的!是float3类型的!wdnmd
--------------------2020年4月24日22:00:49---------------------------更新
LightMode为ForwardAdd的Pass,在没有任何非平行光的逐像素光源是不会进行渲染的!可自行试验测试(注意:测试时需要满足一个SubShader且它只有一个ForwardAdd的Pass,且没有FallBack)