复杂的光照与更复杂的阴影实现过程——ShaderCP9
——20.8.28
这章的内容看了很久,也有很多复杂的内容。中途还有事情耽搁了一会。开学后就继续好好记录努力。
我们在游戏中能看到的让人觉得真实感的来源之一就是真实的光照以及光照所产生的阴影。下面的内容分为两个部分一个是光照的部分一个是阴影的生成部分。
说到光照在Unity中渲染路径就是决定光照是以何种方式应用到Shader中的。所以要通过指定Pass通道的渲染路径类型 “LightMode” 来进行Shader的光照计算。简单地说就是设定渲染路径后Unity便会提供对应的光照信息给用户进行光照计算。
目前有三种渲染路径 1)前向渲染 2)延迟渲染(有新的) 3)顶点照明渲染(被抛弃)
当GPU不支持所选择的渲染路径则会降级
设置渲染路径便是在Tags设定 “LightMode” 有以下支持的的标签
如果没有指定渲染路径可能会出错,因为此时当作为顶点照明渲染路径,当中的一些光照变量可能就不会被赋值。
一、前向渲染路径
每进行一次完整的前向渲染路径1.我们需要渲染该对象的渲染图元2.计算两个缓冲区的信息 a.深度缓冲区(决定是否可见)b.颜色缓冲区(若可见,更新颜色缓冲区)对灯光范围内的物品每一个灯光处理一次执行一次Pass
三种处理光照的方式 1)逐顶点处理 2)逐像素处理 3)球谐函数(SH)
一种光源的处理方式由类型和渲染模式(是否重要的,RenderMode)渲染一个物体要对光源进行排序(对物体的影响程度)要遵循以下规则 1)场景中最亮的平行光总是按逐像素处理2)渲染模式被设置为Not Important按逐顶点或是SH 3)渲染模式被设置为Important按逐像素处理 4)如果按以上规则得到的逐像素光源数量小于Quality Setting(PixelLight Count)将会由更多的光源按照逐像素处理
注意事项:1.BasePass支持的光照特性 2.BasePass渲染的平行光默认有灯光 AddtivePass渲染的光源默认情况下没有阴影(即使设置了ShadowType)可以通过 #pragma multi_compile_fwdadd_fullshadows 代替 #pragma multi_compile_fwdadd 为点光源和聚光灯开启阴影 3.环境光(环境光和平行光不同)和自发光在BasePass计算因为只计算一次在AdditivePass会导致多次叠加 4.AdditivePass还要开启混合不然只有一个光源的影响 5.一个BassPass可定义多次(比如双面渲染)但只运行一遍而AdditivePass会根据影响该物体的逐像素光源多次调用
以上是一些内置的函数和变量
二、顶点照明渲染
顶点照明可在前向渲染中完成,一个顶点照明的Pass最多8个逐顶点光源
三、延迟渲染路径
G缓冲 存储了离摄像机最近的表面的其他信息 包含两个单元第一个Pass计算哪些片元可见,第二个Pass利用G缓冲区中的信息光照计算。效率取决于场景的复杂度,而取决于屏幕空间大小。
优点 1)场景中的光源数目多,用前向渲染会造成性能瓶颈 2)每个光源采用逐像素处理 缺点 1)不支持抗锯齿 2)不能处理半透明 3)对显卡由需求
分析完渲染路径之后我们简单过一下基础的灯光类型在unity中存在的灯光类型以及它所具有的特点以及属性 1)平行光(不存在衰减 面向全局)2)点光源(边缘的强度为0,中间的衰减值可以用函数表示)3)聚光灯(同上,衰减计算更加复杂)4)面光源(只有烘培有用) 灯光所具有的基本特性 1)位置 2)方向 3)颜色 4)强度 5)衰减(到某点的衰减与距离有关)
UnityShader这本书只有对前向渲染有具体的内容,这里留一个延迟渲染路径的小坑(?)
主要就是注意要添加一个库“AutoLight.cginc” 其中有对之前对应渲染路径的内置变量定义,否则就找不到 比如_LightMatrix0之类
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
这部分是在AdditivePass中的中区分平行光以及其他光源的光源方向。
#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;
//atten = 1.0;
#endif
这里部分是最重要的这部分是在AdditivePass中对衰减的定义。在BasePass因为主要逐像素处理一个最重要的平行光 而平行光不存在衰减所以atten为1 而在AdditivePass需要区分平行光和其他光源 点光源采用对应函数进行衰减。在Unity中为了方便使用一张衰减纹理_LightTexture0来为光源计算衰减,首先要把对应距离灯光的点通过坐标运算从世界到光源空间。然后通过点乘也就是顶点距离的平方来进行纹理取样。来避免开方计算。上面的".rr"相当于是取x并定义float2(x,x)用于取样。UNITY_ATTEN_CHANNEL是衰减值所在的衰减通道。我们不妨看一下这张_LightTexture0是什么样的图,就能理解为什么要平方之后再取样。
这张图就是按照平方值设计的。可以放到画图工具中发现它不是线性的。实际相当于一维坐标。因为只有x。
实际上这么分其实是不够的因为其他灯光的聚光灯的衰减是不一样的。这里暂时留一下(?)
// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight' Shader "Unlit/9-1ForwardRendering"
{
Properties
{
_Diffuse("Diffuse", Color)= (1,1,1,1)
_Specular("Specular", Color)= (1,1,1,1)
_Gloss("Gloss", Range(8.0, 256))= 8
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "Lighting.cginc"
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
}; fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(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 vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "Lighting.cginc"
#include "UnityCG.cginc"
#include "AutoLight.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
}; fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(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;
//atten = 1.0;
#endif
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}FallBack "Specular"
}
主要就是在之前的光照模型上区分了BasePass以及AdditivePass,需要指定渲染路径以及添加对应的#pragma。最后最主要的是增加灯光的衰减。我们可以看一下实际效果。通过改变点光源的atten。足以看出衰减的真实感。衰减也可以用公式不用取样。一样后面继续研究(?)老留坑专家了。
接下来便是最令人头大的阴影了。但又是最不可获取的部分。书中的理解部分会居多。
我们先看一个概念ShadowMap。就是把摄像机放到光源重合的位置然后场景中的阴影就是摄像机看不见的位置。 (本质上是为了得到光源空间的深度图)
在前向渲染中,平行光开启阴影就会计算它的阴影映射纹理(shadowmap)。实际上他是一张深度图(mark一下后面的仔细研究一下深度的应用?)记录了从光源位置出发能看到离他位置最近的表面位置也就是深度信息。那具体是怎么判断距离光源最近的表面呢?有以下两种方法。1)就是用上面的这个ShadowMap方法,把摄像机放到光源位置然后通过前向渲染的BasePass/AdditivePass更新深度信息。但是使用这种正常渲染方式其中涉及狠毒复杂的光照模型计算所以很浪费 2)使用额外的Pass “LightMode”=“ShadowCaster” 渲染目标是阴影映射纹理不是帧缓存。首先一样是把摄像机放到光源位置然后调用该Pass(此事获得的深度信息是光源空间,因为和光源重合)。介绍了上述两个获得表面的方法,在unity中首先会a.找该Pass b.然后找FallBake(因为FallBack定义的默认Shader会有对阴影定义的ShadowCaster通道) c.如果都没有就没有阴影。
然后到了最后一步就是获得了对应的深度怎么把它应用到阴影上。阴影映射技术就是我们需要看的 主要是由两种方法 1.传统方法,把正常的顶点坐标转换到光源空间,然后用xy分量对阴影映射纹理取样。如果深度小于改顶点的深度值也就是z分量则在阴影里。 2.屏幕空间的阴影投射技术。用阴影映射纹理以及摄像机(相机摆放位置即Game视图)的深度纹理通过这两张图得到屏幕空间的阴影图。摄像机记录的表面深度深度>其转换到阴影纹理的深度即为可见,但在该灯源的阴影中。然后对阴影图采样。
所以总结一下 两个过程 1.我们想要让物体接受其他物体的阴影我们就要对阴影图采样然后于光照结果相乘。2.我们想要一个物体向其他物体投射阴影则需要把该物体加入阴影映射纹理的计算中。我们下面进入实践过程。
我们可以看到这张图发现有一点奇怪,物品命名有阴影但是这个平面却没有。主要原因是平面在unity中只有一面渲染另一面不渲染。所以解决方法便是改变平面的MeshRenderer中的CastShadow为TwoSides才能得到正确的阴影。
我们看一下这张图发现这个方块没有接受到平面的阴影但自己却有阴影而且在Unity设置是正确的即有开启CastShadow 实际上之所以方块有阴影是因为采用了FallBack中的“ShadowCaster”的通道但是我们可以从上图得知平面想要投射阴影需要加入阴影投射纹理。实际上是输出片元的时候没有采样阴影映射纹理。我们可以通过FrameBugger观察它的深度图。
上面两张图是一样的阴影图。但是在渲染方块的时候却不一样。
SHADOW_COORDS(2)//在v2f中的定义
TRANSFER_SHADOW(o);//在vert中
//在frag中
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
首先SHADOW_COORDS(2)用于对阴影采样纹理的坐标里面的索引值是2也就对应的是TEXCOORD2.在片元函数中的就是进行阴影映射技术把信息存储到_ShadowCrood。SHADOW_ATTENUATION对纹理进行采样。要注意结构体的命名可能会导致定义的宏不可用。
// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-2Shadow"
{
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 "AutoLight.cginc"
#include "Lighting.cginc"
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNoraml : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
}; fixed4 _Specular;
fixed4 _Diffuse;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNoraml = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNoraml = normalize(i.worldNoraml);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNoraml, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(worldNoraml, halfDir)), _Gloss);
fixed atten = 1.0;
fixed shadow = SHADOW_ATTENUATION(i);
return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
}
ENDCG
} Pass
{
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma multi_compile_fwdadd_fullshadows
#pragma vertex vert
#pragma fragment frag
#include "AutoLight.cginc"
#include "Lighting.cginc"
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNoraml : TEXCOORD0;
float3 worldPos : TEXCOORD1;
}; fixed4 _Specular;
fixed4 _Diffuse;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNoraml = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNoraml = normalize(i.worldNoraml);
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNoraml, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(worldNoraml, 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"
}
现在我们就有平面的投影了。
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
这个函数是可以直接统一管理衰减以及阴影。不需要定义atten可以直接用。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-3AttenuationAndShadowUseBuildInFunc"
{
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"
#include "AutoLight.cginc"
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
}; fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + (diffuse + specular) * atten, 1.0);
}
ENDCG
} Pass
{
Tags { "LightMode"="ForwardAdd" }
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma multi_compile_fwdadd_fullshadows
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
}; fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
TRANSFER_SHADOW(o);
return o;
} fixed4 frag (v2f i) : SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
}
这样BasePass和AdditivePass就可以统一。
最后就是上一节的一个小坑也是填上了。就是半透明物体的阴影关系。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' Shader "Unlit/9-4AlphaTestWithShadow"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
}
SubShader
{
Tags { "RenderType"="TransparentCutOut" "IgnoreProjector"="True" "Queue"="AlphaTest" }
Pass
{
Tags{ "LightMode"="ForwardBase" } Cull Off CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityCG.cginc" struct appdata
{
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) //TEXCOORD3
}; sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _Cutoff; v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
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);
clip(texColor.a - _Cutoff);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(ambient + diffuse * atten, 1.0);
}
ENDCG
}
}
//FallBack "VertexLit"
FallBack "Transparent/Cutout/VertexLit"
}
首先是透明测试的投影。效果图如下。图一是表示能够正常的接受阴影。图二图三分别是采用不同的FallBack的效果后者相对于前者是比较正确的。最后一张图是是否打开CastShadow中的TwoSides左边是打开的可以看出正方形的内部投影是和左边相比体现了处理。
最后是透明度混合的阴影。同样的增加FallBack
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Unlit/9-5AlphaBlendWithShadow"
{
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"
}
采用FallBack "VertexLit"会强制投影同时接受阴影。"Transparent/VertexLit"两者都不会。主要是半透明物体由于关闭了深度写入也会影响对应阴影的生成。
这就是最后了。感谢您读到这里。Cheers!
PS:这章总算是看完了。还留了很多坑。慢慢补把。
复杂的光照与更复杂的阴影实现过程——ShaderCP9的更多相关文章
- 编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程
建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...
- 【转】编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程
建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...
- sedsed 一个更好理解sed执行过程的工具
官网:http://aurelio.net/projects/sedsed/ 这个上面好多学习的资料 嘿嘿 留着慢慢看 昨晚在看sed的具体执行过程,看到有hold space,pattern ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- 【Unity Shader】(七) ------ 复杂的光照(下)
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Sha ...
- 在Unity中实现屏幕空间阴影(2)
参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...
- Unity 5.6中的混合光照(下)
https://mp.weixin.qq.com/s/DNQFsWpZm-ybIlF3DTAk2A 在<Unity 5.6中的混合光照(上)>中,我们介绍了混合模式,以及Subtracti ...
- 渲染路径-Deferred Lighting 延时光照
http://blog.csdn.net/heyuchang666/article/details/51564954 注意: 最后3个步骤注意下 延时光照是有着最高保真度的光照和阴影的渲染路径.如果你 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十章:阴影贴图
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十章:阴影贴图 本章介绍一种在游戏和应用中,模拟动态阴影的基本阴影 ...
- DirectX11 With Windows SDK--38 级联阴影映射(CSM)
前言 在31章我们曾经实现过阴影映射,但是受到阴影贴图精度的限制,只能在场景中相当有限的范围内投射阴影.本章我们将以微软提供的例子和博客作为切入点,学习如何解决阴影中出现的Atrifacts: 边缘闪 ...
随机推荐
- windows 链接 MySQL8.0.28 报错: SSL connection error: unknown error number 解决办法
找到 My.ini文件,以管理员身份打开并在 [mysqld] 节点下 增加 skip_ssl 选项并保存,重启 MySQL 服务 执行SQL语句 SHOW VARIABLES L ...
- TCP三次握手四次挥手内容及步骤
TCP特性 1.工作在传输层 2.面向连接的协议 3.全双工协议 4.半关闭 5.错误检查 6.将数据打包成段,排序 7.确认机制 8.数据恢复.重传 9.流量控制.滑动窗口 10.拥塞控制,慢启动和 ...
- pycharm、pyqt5、pyuic、anaconda配置界面
转载一篇很棒的文档,讲解的是如何在pycharm里面使用QT desiger勾画界面并且将相应的界面转化成py文件 https://www.jianshu.com/p/8b992e47a0e4 个人 ...
- go 的形参
注意 记录一个 go 语言编程中,可能不小心忽略的一个点, 当函数的 出参 如果是 数组.结构体.字典 类型时,是不需要声明的,可以直接使用. 但是如果 出参 是 指针 类型,则必须要显示的声明. 代 ...
- create_base_x.txt
create table g_temp.test_base( field_date date, field_str varchar(100) , field_int integer ) drop ta ...
- 掌控安全学院SQL注入靶场-布尔盲注(一)
靶场地址:http://inject2.lab.aqlab.cn/Pass-10/index.php?id=1 判断注入点: http://inject2.lab.aqlab.cn/Pass-10/i ...
- js引入样式资源报错
如上图,import这几个样式资源为什么会报错,怎么解决呢,而且那个jquery-ui之前也会报错但是现在刷新一遍他又不报错了其他的css文件报错 图片转代码服务由CSDN问答提供 功能建议 im ...
- boolean布尔型盲注
mysql中limit的详细用法 1.用于强制返回指定的记录行数 在查询中,经常要返回前几条或者中间某几行数据时,用到limit 语法如下 select * from table_name limit ...
- 配置代码片段问题 Invalid characters in string. Control characters must be escaped.
在使用代码片段时报错 Invalid characters in string. Control characters must be escaped. " somethings" ...
- C# Winform 多线程更新界面UI控件,解决界面卡顿问题(转)
前言 多线程刷新界面主要用到多线程,委托,线程安全.事件等一系列高难度的C#操作. 1.使用timer控件对要刷新的控件进行定时刷新 对刷新频率要求不高的时候,可以使用该方法. 2.刷新UI控件 在开 ...