【Unity Shader入门精要 第9章】更复杂的光照(四)

news2024/10/7 14:31:43

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 Front
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase
            
            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;
            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 Back
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase
            
            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;
            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的Pass
        Pass
        {
            Tags { "LightMode" = "ShadowCaster" }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma multi_compile_shadowcaster
            
            struct 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 Front
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #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 Back
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
        
            CGPROGRAM
            #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_shadowcaster
            
            struct 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以下,比如像这样:
在这里插入图片描述
在这里插入图片描述
这样虽然能接收到阴影,但显然不是正经做法,因为这样会造成半透物体的渲染顺序错乱,比如这样:
在这里插入图片描述
要真正解决这个问题,内容就超出《入门精要》的范围了(也超出我的范围了)。
这里先存本书,《实时阴影技术》,有时间开坑研究一下(应该是不会有时间了)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1686775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FTP文件传输议

FTP是一种文件传输协议:用来上传和下载,实现远程共享文件,和统一管理文件 工作原理:用于互联网上的控制文件的双向传输是一个应用程序。工作在TCP/IP协议簇的,其传输协议是TCP协议提高文件传输的共享性和可靠性&#…

阅读笔记——《AFLNeTrans:状态间关系感知的网络协议模糊测试》

【参考文献】洪玄泉,贾鹏,刘嘉勇.AFLNeTrans:状态间关系感知的网络协议模糊测试[J].信息网络安全,2024,24(01):121-132.【注】本文仅为作者个人学习笔记,如有冒犯,请联系作者删除。 目录 摘要 1、引言 2、背景及动机 2.1、网络协议实现程…

正点原子LWIP学习笔记(二)MAC简介

MAC简介 一、MAC简介(了解)二级目录三级目录 二、ST的ETH框架(了解)三、SMI站管理接口(熟悉)四、介质接口MII、RMII(熟悉) 一、MAC简介(了解) STM32 的 MAC …

Ubuntu24.04设置静态IP地址

Ubuntu24.04设置静态IP地址 前言:vm17.5的动态IP问题 第一个是设置的静态IP我们可以看到是forever,第二个则是动态IP则是一天的时间。 如果我们不设置静态IP的话,那么可能在本地测试项目的时候,第二天发现一些服务不能用了&#…

13.js对象

定义 一种复杂数据类型,是无序的(不保留键的插入顺序),以键值对({key:value})形式存放的数据集合 对象的创建 (1)字面量创建 var 对象名{ } (2)内部构造函数创建 v…

VirtualBox安装ubuntu22.04记录

一,VirtualBox 软件安装 虚拟机(Virtual Machine)指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。在实体计算机中能够完成的工作在虚拟机中都能够实现。 常见的虚拟机软件主要有两款 VMware 和 VirtualBox 。VMwar…

争议湖北消费金融2023年业绩,营收下滑or财务内控重大缺陷?

近日,湖北消费金融股份有限公司(下称“湖北消费金融”)披露了2023年度相关信息,包括股权结构、关联方、董事会、分支机构、资产负债情况等信息。 据介绍,湖北消费金融的注册资本为10.058亿元,法定代表人为…

linux---线程控制

线程和进程 以前我们要同时跑多个程序,可以通过fork()多个子进程,然后通过系统函数进行程序的替换,但是创建进程代价大,不仅要拷贝一份父进程的地址空间,页表,文件表述符表等。但是线程不需要因为是进程的…

使用JavaScript日历小部件和DHTMLX Gantt的应用场景(三)

DHTMLX Suite UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件,这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足项目管理应用…

【调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件】

调试笔记-系列文章目录 调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件 文章目录 调试笔记-系列文章目录调试笔记-20240520-Linux-在 WSL2 / Ubuntu 20.04 中编译 QEMU 可运行的 OVMF 固件 前言一、调试环境操作系统:Windows 10 …

作业-day-240523

思维导图 知识点问答 1、IO多路复用的原理 1、创建一个检测文件描述符的容器 fd_set fds; 2、将需要检测的文件描述符放入容器中 FD_SET(文件描述符,&fds); 3、通过一个阻塞函数阻塞等待容器中是否有事件产生,如果有一个或多个事件产生&#xff0c…

C++设计模式|结构型 适配器模式

1.什么是适配器模式? 可以将⼀个类的接⼝转换成客户希望的另⼀个接⼝,主要⽬的是 充当两个不同接⼝之间的桥梁,使得原本接⼝不兼容的类能够⼀起⼯作。 2. 适配器模式的组成 (1)接口类,给客户端调用&…

软件设计师-上午题-计算题汇总

一、存储系统 - 存储容量计算(字节编址、位编址、芯片个数) 内存地址是16进制 内存地址编址的单位是Byte,1K1024B 1B 8 bit 1.计算存储单元个数 存储单元个数 末地址 - 首地址 1 eg. 按字节编址,地址从 A4000H 到 CBFFFH&…

汽车IVI中控开发入门及进阶(十八):显示技术之Frame Buffer帧缓冲器

Frame Buffer帧缓冲器(帧缓冲器,有时是帧存储器)是随机存取存储器(RAM)的一部分,包含驱动视频显示器的位图。它是一个内存缓冲区,包含表示完整视频帧中所有像素的数据。现代视频卡的核心包含帧缓冲电路。该电路将内存中的位图转换为可以在计算机监视器上显示的视频信号。…

Redis未授权访问漏洞复现 CNVD-2019-21763 CNVD-2015-07557

CNVD-2019-21763 漏洞描述 Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 由于在Reids 4.x及以上版本中新增了模块功能,攻击者可通过外部拓展,在Redis中实现一个新…

redis集群不允许操作多个key解决方案、redis key负载均衡方案

前提 在cluster redis 中进行同一个命令处理不同的key会报错:CROSSSLOT Keys in request dont hash to the same slot,例如: 此示例使用sdiff 命令对pool_1与pool_2进行diff操作。 那么我们在业务场景中就需要将集群redis中的不同key进行操作,我们该如何处理呢? 本次的…

多线程(八)

一、wait和notify 等待 通知 机制 和join的用途类似,多个线程之间随机调度,引入 wait notify 就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序.( 这里说的干预,不是影响系统的线程调度策略 内核里的线程调度,仍然是无序的. 相当于是在应用程序…

探秘QT 5软件开发:从零到实战的旅程

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、QT 5软件开发的启航 二、GUI框架比较与QT 5的选择 三、QT 5环境搭建与软件设计实战 四…

GPT-SoVITS语音克隆部署与使用

GPT-SoVITS是一款强大的少量样本语音转换与语音合成开源工具。当前,GPT-SoVITS实现了如下几个方面的功能: 由参考音频的情感、音色、语速控制合成音频的情感、音色、语速可以少量语音微调训练,也可不训练直接推理可以跨语种生成,…

Python魔法学院:PySpider篇——网络世界的探险与征服

Hi,我是阿佑,迎来到Python魔法学院,今天阿佑要带大家学习的是PySpider篇——一门让你在网络世界中探险与征服的魔法课程。从环境搭建到高级功能应用,再到性能优化,每一个章节都是成为数据大师的必经之路! 文…