【Unity Shader入门精要 第9章】更复杂的光照(四)
1. 透明度测试物体的阴影
对于物体有片元丢弃的情况,比如透明度测试或者后边会讲到的消融效果,使用默认的 ShadowCaster Pass 会产生问题,这是因为该Pass在生成阴影映射纹理时,没有考虑被丢弃的片元,而是使用完整的模型计算深度,因此镂空的部分也会向外投射阴影。
要解决这个问题,可以使用Unity内置的Pass:
UsePass "Legacy Shaders/Transparent/Cutout/VertexLit/CASTER"
由于该Pass内部在计算阴影时需要用到特定名称的变量,因此在使用时,必须声明如下属性:
Properties
{_MainTex("MainTex", 2D) = "white"{}_Color("Color", Color) = (1, 1, 1, 1)_Cutoff("Cutoff", range(0, 1)) = 0}
除此以外,我们也可以自己实现一个 ShadowCaster 的 Pass,代码与上一篇【Unity Shader入门精要 第9章】更复杂的光照(三)中手动实现的 ShadowCaster 基本一样,只是在片元着色器中加入和正常渲染 Pass 中一样的剔除代码即可。
另外,由于镂空物体的内部也可能产生阴影,因此根据需要可以将物体的Cast Shadows选项选为Two Sided

测试Shader如下:
Shader "MyShader/Chapter_9/Chapter_9_Shadow_AlphaTest_Shader"
{Properties{_MainTex("MainTex", 2D) = "white"{}_Color("Color", Color) = (1, 1, 1, 1)_Cutoff("Cutoff", range(0, 1)) = 0}SubShader{Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}Pass{Tags {"LightMode" = "ForwardBase"}Cull FrontCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"#pragma multi_compile_fwdbasestruct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)};sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;v2f vert(a2v i){v2f o;o.pos = UnityObjectToClipPos(i.vertex);o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);o.uv = TRANSFORM_TEX(i.uv, _MainTex);o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;TRANSFER_SHADOW(o);return o;}fixed4 frag(v2f i) : SV_Target{float3 _worldNormal = normalize(i.worldNormal);float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);fixed4 _sampledColor = tex2D(_MainTex, i.uv);clip(_sampledColor.w - _Cutoff);fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;fixed3 _diffuse = _LightColor0.rgb * _sampledColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);return fixed4(_ambient + _diffuse * _atten, 1);}ENDCG}Pass{Tags {"LightMode" = "ForwardBase"}Cull BackCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"#pragma multi_compile_fwdbasestruct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)};sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;v2f vert(a2v i){v2f o;o.pos = UnityObjectToClipPos(i.vertex);o.worldNormal = mul(i.normal, (float3x3)unity_WorldToObject);o.uv = TRANSFORM_TEX(i.uv, _MainTex);o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;TRANSFER_SHADOW(o);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;float3 _worldNormal = normalize(i.worldNormal);float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);fixed4 _sampledColor = tex2D(_MainTex, i.uv);clip(_sampledColor.w - _Cutoff);fixed3 _diffuse = _LightColor0.rgb * _sampledColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);return fixed4(_ambient + _diffuse * _atten, 1);}ENDCG}//使用内置的Pass处理透明度测试的物体向外投射阴影//UsePass "Legacy Shaders/Transparent/Cutout/VertexLit/CASTER"//或者根据渲染逻辑手动实现符合当前效果的ShadowCaster的PassPass{Tags { "LightMode" = "ShadowCaster" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#pragma multi_compile_shadowcasterstruct a2v{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL;};struct v2f{V2F_SHADOW_CASTER;float2 uv : TEXCOORD0;};sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;v2f vert(a2v v){v2f o;o.uv = TRANSFORM_TEX(v.uv, _MainTex);TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);return o;}fixed4 frag(v2f i) : SV_Target{fixed4 _sampledColor = tex2D(_MainTex, i.uv);clip(_sampledColor.w - _Cutoff);SHADOW_CASTER_FRAGMENT(i);}ENDCG }}}
效果如下:

2. 透明度混合物体的阴影
2.1 投射阴影
透明度混合的Shader中关闭了深度写入,不会参与深度纹理的计算,因此不会向其他物体投射阴影。要使透明度测试的物体能够向外投射阴影方法有很多,比如放一个直接算阴影但不渲染自身的代替物体,或者像书中说的添加一个非Transparent的FallBack的Shader,再或者自己手写一个,都可以实现。
下面的例子就是将之前的阴影投射的Pass复制过来,也可以正常产生阴影。
Shader "MyShader/Chapter_9/Chapter_9_Shadow_AlphaBlend_Shader"
{Properties{_MainTex("MainTex", 2D) = "white"{}}SubShader{Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}Pass{Tags {"LightMode" = "ForwardBase"}Cull FrontZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)};sampler2D _MainTex;float4 _MainTex_ST;v2f vert(a2v i){v2f o;o.pos = UnityObjectToClipPos(i.vertex);o.worldNormal = UnityObjectToWorldNormal(i.normal);o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;o.uv = TRANSFORM_TEX(i.uv, _MainTex);TRANSFER_SHADOW(o);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;float3 _worldNormal = normalize(i.worldNormal);float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);float4 _mainColor = tex2D(_MainTex, i.uv);fixed3 _diffuse = _LightColor0.rgb * _mainColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);return fixed4(_ambient + _diffuse * _atten, _mainColor.w);}ENDCG}Pass{Tags {"LightMode" = "ForwardBase"}Cull BackZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;SHADOW_COORDS(3)};sampler2D _MainTex;float4 _MainTex_ST;v2f vert(a2v i){v2f o;o.pos = UnityObjectToClipPos(i.vertex);o.worldNormal = UnityObjectToWorldNormal(i.normal);o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;o.uv = TRANSFORM_TEX(i.uv, _MainTex);TRANSFER_SHADOW(o);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 _ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;float3 _worldNormal = normalize(i.worldNormal);float3 _worldLight = normalize(_WorldSpaceLightPos0.xyz);float4 _mainColor = tex2D(_MainTex, i.uv);fixed3 _diffuse = _LightColor0.rgb * _mainColor.xyz * (0.5 * dot(_worldLight, _worldNormal) + 0.5);UNITY_LIGHT_ATTENUATION(_atten, i, i.worldPos);return fixed4(_ambient + _diffuse * _atten, _mainColor.w);}ENDCG}Pass{Tags { "LightMode" = "ShadowCaster" }CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#pragma multi_compile_shadowcasterstruct v2f{V2F_SHADOW_CASTER;};v2f vert(appdata_base v){v2f o;TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);return o;}fixed4 frag(v2f i) : SV_Target{SHADOW_CASTER_FRAGMENT(i);}ENDCG}}// FallBack "VertexLit"
// FallBack "Diffuse"
}
效果:

2.2 接收阴影
半透物体接收阴影就比较麻烦了,即使像书中说的将FallBack设置为 VertexLit,实际也是无法接收到阴影的(可以将上面测试shader中的注释放开试一下)。
这是因为正常半透物体的渲染队列为3000(Transparent),而在2500以后就没有阴影映射纹理的信息了,因此即使shader里有相应代码,也无法正常接收到阴影。
从下面 FrameDebug 的截图中也可以看出来:

网上最多的说法是将渲染队列强制调到2500以下,比如像这样:


这样虽然能接收到阴影,但显然不是正经做法,因为这样会造成半透物体的渲染顺序错乱,比如这样:

要真正解决这个问题,内容就超出《入门精要》的范围了(也超出我的范围了)。
这里先存本书,《实时阴影技术》,有时间开坑研究一下(应该是不会有时间了)。
相关文章:
【Unity Shader入门精要 第9章】更复杂的光照(四)
1. 透明度测试物体的阴影 对于物体有片元丢弃的情况,比如透明度测试或者后边会讲到的消融效果,使用默认的 ShadowCaster Pass 会产生问题,这是因为该Pass在生成阴影映射纹理时,没有考虑被丢弃的片元,而是使用完整的模…...
【软件工程】【23.10】p2
关键字: 软件复用技术、过程途径、特定需求是文档核心、数据字典条目、高内聚低耦合独立性、数据流图映射模块结构图、UML依赖、用例图关系、RUB迭代、程序规格说明等价类划分、有效性测试的目标、喷泉模型面向对象、软件验证过程、CMMI...
WPF--几种常用定时器Timer汇总
1.WPF中常用4种Timer: System.Windows.Threading.DispatcherTimer(UI操作线程) 这是一个基于WPF Dispatcher的计时器。它可以在指定的时间间隔内触发Tick事件,并在UI线程上执行回调函数,方便进行UI更新操作。 System.Timers.Timer 这是一个基…...
在vue中实现下载文件功能
实际操作为,在表格中 我们可以获取到文件的id,通过插槽就可以实现 <template #default"scope"><el-button type"text" click"handleDown(scope.row)"><span>下载</span></el-button> </…...
文件中海量数据的排序
文件中海量数据的排序 题目: 跟之前堆排序可以解决TopK问题一样,我们来看看归并排序会用来解决什么问题? 思路: 我们说归并排序是外排序。其实就是将数据分成一个个小段,在内存中进行排序,再拿出内存&am…...
java项目之视频网站系统源码(springboot+vue+mysql)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的视频网站系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 视频网站系统的主要使用者管…...
262 基于matlab的一级倒立摆仿真
基于matlab的一级倒立摆仿真,在对一级倒立摆进行数学建模的基础上,对模型进行线性化,得到其状态空间模型,利用二次型最优控制方法得出控制率。输出角度和位置优化曲线。程序已调通,可直接运行。 262 一级倒立摆仿真 状…...
智能无网远控再升级 向日葵Q2Pro升级版发布
无网或者内网设备也想要进行远程控制,是不是听上去有些天方夜谭了?其实这类特种设备的远程控制需求是非常强的,比如医疗/工控设备的远程运维、使用指导教学等等。 实际上,只要这类设备有屏幕,支持可视化的桌面操作&am…...
2024电工杯A题详细思路代码分析数学建模:园区微电网风光储协调优化配置
题目分析:园区微电网风光储协调优化配置 我们会先给出三个问题总体的分析,最后会详细分析问题一的建模和详细内容。 背景: 园区微电网由风光发电和主电网联合为负荷供电,为了尽量提高风光电量的负荷占比,需配置较高比…...
Docker搭建mysql性能测试环境
OpenEuler使用Docker搭建mysql性能测试环境 一、安装Docker二、docker安装mysql三、测试mysql连接 一、安装Docker 建立源文件vim /etc/yum.repos.d/docker-ce.repo增加内容[docker-ce-stable] nameDocker CE Stable - $basearch baseurlhttps://repo.huaweicloud.com/docker…...
关于开启直连v2rayn代理Fiddler Charles bp抓包失败问题
Fiddler 使用插件:proxy switchyomega 配置代理8888端口为fiddler && charles的监听端口 此时fiddler提示代理已更改点击变更捕获,这时不需要进行点击只需要开启上述插件即可抓到包并且国外代理,如果点击的话回自动更新为原来的ip 即…...
Python 爬虫编写入门
一、爬虫概述 网络爬虫(Web Crawler)或称为网络蜘蛛(Web Spider),是一种按照一定规则,自动抓取互联网信息的程序或者脚本。它们可以自动化地浏览网络中的信息,通过解析网页内容,提取…...
Linux网络编程(socket)
1. 概念 局域网和广域网 局域网:局域网将一定区域内的各种计算机、外部设备和数据库连接起来形成计算机通信的私有网络。广域网:又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程公共网络。 IP(Internet Protocol&a…...
以太坊(3)——智能合约
智能合约 首先明确一下几个说法(说法不严谨,为了介绍清晰才说的): 全节点矿工 节点账户 智能合约是基于Solidity语言编写的 学习Solidity语言可以到WFT学院官网(Hello from WTF Academy | WTF Academy)…...
【Python设计模式03】简单工厂模式
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它通过专门定义一个工厂类来负责创建其他类的实例,而不是在客户端代码中直接实例化对象。这样可以将对象创建的过程与使用对象的过程分离,提高代码的可维护…...
java中的Collections类+可变参数
一、概述 Collections类是集合类的工具类,与数组的工具类Arrays类似 二、可变参数(变:数量) 格式:参数类型名...参数,可变参数就是一个数组 注意:可变参数必须放在参数列表的最后并且一个参数列表只能有一个可变参…...
SpringBoot集成腾讯云敏感词校验API流程
1.pom.xml中引入腾讯云jar配置信息 <dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId><version>4.0.11</version> </dependency> 2.application.yaml中添加配置 tencent…...
android 避免混淆类名和方法名,但是方法内容需要被混淆
要避免在使用 ProGuard 或 R8 进行代码混淆时混淆特定类名和方法名的同时让方法内容被混淆,你需要在 ProGuard 配置文件中使用 -keepclassmembers 或 -keep 规则。这些规则允许你指定保留类名和方法名的同时允许方法内部代码被混淆以减小体积和提高安全性。 以下是…...
通过ELRepo修改CentOS 7内核版本的详细步骤
简介: 在Linux系统中,内核版本决定了硬件支持和系统性能。有时,为了获得更好的性能或新特性,我们需要升级或更换内核。本文将详细说明如何在CentOS 7系统上通过ELRepo仓库安装更新的内核版本。 环境准备: CentOS 7系…...
C++开源库glog使用封装--自定义日志输出格式,设置日志保留时间
glog下载和编译 glog开源地址 https://github.com/google/glog glog静态库编译 cd /home/wangz/3rdParty/hldglog/glogmkdir out mkdir build && cd buildcmake .. -DCMAKE_INSTALL_PREFIX../out -DCMAKE_BUILD_TYPERelease -DBUILD_SHARED_LIBSOFF本文选择的glo…...
ComfyUI-Impact-Pack V8:AI图像细节增强的终极指南
ComfyUI-Impact-Pack V8:AI图像细节增强的终极指南 【免费下载链接】ComfyUI-Impact-Pack Custom nodes pack for ComfyUI This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more. 项目地址: https://git…...
洛雪音乐音源配置完全指南:免费搭建个人音乐库的终极方案
洛雪音乐音源配置完全指南:免费搭建个人音乐库的终极方案 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 洛雪音乐作为一款强大的音乐播放工具,提供了全网最新最全的音源资…...
用正点原子Nano开发板,5分钟搞定RT-Thread Nano的MDK5工程配置(附串口调试技巧)
正点原子Nano开发板极速上手RT-Thread实战指南 1. 开箱即用的开发环境搭建 刚拿到正点原子Nano开发板时,最令人兴奋的莫过于快速验证硬件是否正常工作。这款基于STM32F103RBT6的开发板,以其72MHz主频和丰富的外设资源,成为嵌入式入门学习的…...
免费开源AMD Ryzen调试工具终极指南:从零掌握SMUDebugTool完整使用教程
免费开源AMD Ryzen调试工具终极指南:从零掌握SMUDebugTool完整使用教程 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目…...
免费商用音乐下载网站TOP5 | 基于版权合规与素材复用效率的横评
引言 2026年,国内内容营销市场持续扩张,企业短视频、直播切片、线下活动音视频等场景对背景音乐的需求量同比增长37%(根据《2026中国数字内容版权白皮书》)。然而,创作者在实际选曲过程中普遍存在三类矛盾:…...
告别格式转换烦恼:用Blender3mfFormat插件打通3D打印最后一公里
告别格式转换烦恼:用Blender3mfFormat插件打通3D打印最后一公里 【免费下载链接】Blender3mfFormat Blender add-on to import/export 3MF files 项目地址: https://gitcode.com/gh_mirrors/bl/Blender3mfFormat 你是否曾在Blender中精心设计了色彩斑斓的3D模…...
从零开始写扫雷游戏:C语言完整实现教程
# 从零开始写扫雷游戏:C语言完整实现教程## 写在前面还记得Windows XP时代那个经典的小游戏吗?每一次点击都让人心跳加速,生怕触发那颗隐藏的地雷。今天,让我们一起用C语言重新实现这个经典游戏。通过这个项目,你将学到…...
电力市场再调度成本飙升:高比例可再生能源与简化市场设计的结构性矛盾
1. 项目概述:当低净需求成为常态,电力市场再调度成本为何飙升?作为一名长期关注电力市场与能源转型的从业者,我一直在思考一个问题:当风电和光伏成为电力系统的主力军,我们的市场机制真的准备好了吗&#x…...
AI 大模型未来技术演进方向与应用发展趋势预判
引言:AI 技术快速迭代,未来已来AI 大模型技术正以超乎想象的速度迭代演进,从参数规模扩张到能力提升、从技术架构创新到应用场景拓展、从成本高企到普惠落地,每一次技术突破都在重塑产业格局、改变商业逻辑、影响生活方式。2026 年…...
STM32H7 QSPI Flash程序调试全攻略:从MDK配置到单步调试,解决‘算法加载失败’的常见问题
STM32H7 QSPI Flash程序调试实战:破解算法加载失败的终极指南 当你第一次看到MDK弹窗提示"Download Algorithm Failed"时,那种挫败感我深有体会。作为使用STM32H7系列开发过多个量产项目的工程师,我曾在QSPI Flash调试过程中踩过所…...
