【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 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以下,比如像这样:
在这里插入图片描述
在这里插入图片描述
这样虽然能接收到阴影,但显然不是正经做法,因为这样会造成半透物体的渲染顺序错乱,比如这样:
在这里插入图片描述
要真正解决这个问题,内容就超出《入门精要》的范围了(也超出我的范围了)。
这里先存本书,《实时阴影技术》,有时间开坑研究一下(应该是不会有时间了)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java类加载过程

类加载三个阶段任务: 加载和连接阶段是JVM虚拟机完成的,无法进行控制。 初始化阶段是程序员可以控制的,例如在代码块中对静态成员初始化等。 1、加载阶段: JVM在该阶段主要目的是 将字节码从不同的数据源(class文件,ja…

k8s部署calico遇到的问题

kubernetes安装calico calico官网 环境:centos7.9,calico 3.23,kuberadm 1.26 问题1:执行kubectl create -f calico.yml后报错如下 error: resource mapping not found for name: “tigera-operator” namespace: “” from “…

网络故障与排除(一)

一、Router-ID冲突导致OSPF路由环路 路由器收到相同Router-ID的两台设备发送的LSA,所以查看路由表看到的OSPF缺省路由信息就会不断变动。而当C1的缺省路由从C2中学到,C2的缺省路由又从C1中学到时,就形成了路由环路,因此出现路由不…

【AREngine BUG 解决方法】无法获取有效的相机图像尺寸

近期拿了一台 华为mate20 Pro的手机,在运行AR示例的过程中出现了黑屏。 问题排查 SDK版本:com.huawei.hms:arenginesdk:3.7.0.3 定位 经排查,发现(ARCamera对象的相机内参) getImageDimensions()返回的图像尺寸的width和height都为0。 这…

接口自动化测试之pytest 运行方式及前置后置封装

🍅 视频学习:文末有免费的配套视频可观看 🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 一、Pytest 优点认知 1.可以结合所有的自动化测试工具 2.跳过失败用例以及失败重跑 3.结合allur…

tinymce富文本编辑器使用

安卓富文本编辑器&#xff1a;npm i tinymce/tinymce-vue 当前项目中富文本是放在一个dialog中&#xff0c;因此部分样式会有层叠问题&#xff0c;该组件样式部分不添加scope。这里图片上传只是前端静态数据展示收集。 <template><div class"desc-editor"…

电脑突然提示:“failed to load steamui.dll”是什么情况?分享几种解决steamui.dll丢失的方法

相信有一些用户正在面临一个叫做“failed to load steamui.dll”的问题&#xff0c;这种情况多半发生在试图运行某个程序时&#xff0c;系统会提示一条错误消息&#xff1a;“failed to load steamui.dll”。那么&#xff0c;为何steamui.dll文件会丢失&#xff0c;又应该如何解…

【数据结构与算法 | 队列篇】力扣102, 107

1. 力扣102 : 二叉树的层序遍历 (1). 题 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3]…

从GPT-3.5到GPT-4O:探索AI的进化之旅,哪一版更懂你?

如何评价GPT-4o? 最新的GPT-4O&#xff0c;被誉为GPT-4的增强版。它在保持前代产品优秀性能的基础上&#xff0c;大幅降低了使用成本&#xff0c;使得更多的普通用户也能享受到顶尖AI的服务。GPT-4O在非英语语言处理上的强化&#xff0c;更是让其在全球范围内的适用性大大提高…

K8s集群之 存储卷 PV PVC

默写 1 如何将pod创建在指定的Node节点上 node亲和、pod亲和、pod反亲和: 调度策略 匹配标签 操作符 nodeAffinity 主机 In,NotIn,Exists,DoesNotExist&#xff0c;Gt&#xff0c;Lt podAffinity …

aws 在ecs外部实例上运行gpu负载

参考资料 https://docs.amazonaws.cn/zh_cn/AmazonECS/latest/developerguide/ecs-gpu.htmlhttps://docs.amazonaws.cn/AWSEC2/latest/UserGuide/accelerated-computing-instances.html#gpu-instanceshttps://docs.amazonaws.cn/AWSEC2/latest/UserGuide/install-nvidia-drive…

k210数字识别 笔记2 (串口通信)

这个模型识别的还可以&#xff0c;离近点 识别率高达0.9 资源&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1D4ubJGMptqop1x_Nf8KqfQ?pwd1234 提取码&#xff1a;1234 一&#xff1a;报错解决 报错的意思应该是模型文件错误 原程序可以在sd卡运行&#xff0c;但…

MFC工控项目实例之一主菜单制作

1、本项目用在WIN10下安装的vc6.0兼容版实现。创建项目名为SEAL_PRESSURE的MFC对话框。在项目res文件下添加相关256色ico格式图片。 2、项目名称&#xff1a;密封压力试验机 主菜单名称&#xff1a; 系统参数 SYS_DATA 系统测试 SYS_TEST 选择型号 TYP_CHOICE 开始试验 TES_STA…

精通Java异常机制,写出高质量代码

作为一名Java开发人员&#xff0c;异常处理是一个无法回避的话题。无论你是初学者还是老手&#xff0c;精通异常处理对于写出高质量、可维护的代码至关重要。今天&#xff0c;我将与大家分享关于Java异常处理的一切&#xff0c;助你在代码质量的道路上突飞猛进! 一、什么是异常…

在线等!3damx渲染爆内存怎么办?

在使用V-Ray进行CPU渲染时&#xff0c;复杂场景和高渲染设置可能会导致内存消耗过高&#xff0c;进而影响渲染速度&#xff0c;导致处理异常、机器停滞、应用程序崩溃等情况。 为机器配置更大的 RAM 始终是解决问题的最有效办法&#xff0c;但如果出于预算等原因无法实现&…

devicemotion 或者 deviceorientation在window.addEventListener 事件中不生效,没有输出内容

问题&#xff1a;devicemotion 或者 deviceorientation 在window.addEventListener 事件中不生效&#xff0c;没有输出内容 原因&#xff1a; 1、必须在Https协议下才可使用 2、必须用户手动点击click事件中调用 &#xff0c;进行权限申请 源码&#xff1a; <!DOCTYPE h…

【Linux 网络编程】协议的分层知识!

文章目录 1. 计算机网络背景2. 认识 "协议"3. 协议分层 1. 计算机网络背景 网络互联: 多台计算机连接在一起, 完成数据共享; &#x1f34e;局域网&#xff08;LAN----Local Area Network&#xff09;: 计算机数量更多了, 通过交换机和路由器连接。 &#x1f34e; 广…

每日一题《leetcode--117.填充每个结点的下一个右侧结点指针||》

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/ 这道题与我之前发布的题目116是一样的解题过程&#xff0c;只是本题所给的数组大小与116不同&#xff0c;这是需要注意的。 116题目链接&#xff1a; http://t.csdnimg.cn/3Ub02 struct Node* c…

基于 BERT 对 IMDB 电影评论进行情感分类

前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记…

3.4 移动机器人工作空间(摘自自主移动机器人导论2)

对于一个机器人来说&#xff0c;机动性等效于它的控制自由度。但是&#xff0c;机器人是处于某种环境的&#xff0c;因而下一个问题是把我们的分析放到环境之中。 我们关心机器人用它可控制的自由度在环境中定位它本身的方法。例如&#xff0c;考虑 Ackerman 车辆或汽车&#…