Unity Standard Shader 解析(五)之ShadowCaster

一、ShadowCaster

// ------------------------------------------------------------------//  Shadow rendering passPass {Name "ShadowCaster"Tags { "LightMode" = "ShadowCaster" }ZWrite On ZTest LEqualCGPROGRAM#pragma target 3.0// -------------------------------------#pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON#pragma shader_feature_local _METALLICGLOSSMAP#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A#pragma shader_feature_local _PARALLAXMAP#pragma multi_compile_shadowcaster#pragma multi_compile_instancing// Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.//#pragma multi_compile _ LOD_FADE_CROSSFADE#pragma vertex vertShadowCaster#pragma fragment fragShadowCaster#include "UnityStandardShadow.cginc"ENDCG}

引用了UnityStandardShadow.cginc中的vertShadowCasterfragShadowCaster

以下是UnityStandardCoreForward.cginc的源码,

二、vertShadowCaster

// 我们必须分别在顶点着色器中输出 SV_POSITION,并在像素着色器中输入 VPOS,
// 因为它们在某些平台上都映射到 "POSITION" 语义,这样会导致问题。void vertShadowCaster (VertexInput v, out float4 opos : SV_POSITION#ifdef UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT, out VertexOutputShadowCaster o#endif#ifdef UNITY_STANDARD_USE_STEREO_SHADOW_OUTPUT_STRUCT, out VertexOutputStereoShadowCaster os#endif
)
{// 设置实例ID,以便在多实例渲染时正确处理每个实例的数据UNITY_SETUP_INSTANCE_ID(v);// 如果启用了立体阴影输出结构,则初始化立体阴影输出结构#ifdef UNITY_STANDARD_USE_STEREO_SHADOW_OUTPUT_STRUCTUNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(os);#endif// 将顶点数据转换为阴影投射所需的格式,并输出 SV_POSITIONTRANSFER_SHADOW_CASTER_NOPOS(o, opos)// 如果启用了阴影UVs#if defined(UNITY_STANDARD_USE_SHADOW_UVS)// 将纹理坐标从对象空间转换到纹理空间o.tex = TRANSFORM_TEX(v.uv0, _MainTex);// 如果启用了视差贴图#ifdef _PARALLAXMAP// 计算切线空间旋转矩阵TANGENT_SPACE_ROTATION;// 计算视差贴图所需的视图方向o.viewDirForParallax = mul(rotation, ObjSpaceViewDir(v.vertex));#endif#endif
}

1.VertexInput


struct VertexInput
{float4 vertex   : POSITION;half3 normal    : NORMAL;float2 uv0      : TEXCOORD0;float2 uv1      : TEXCOORD1;
#if defined(DYNAMICLIGHTMAP_ON) || defined(UNITY_PASS_META)float2 uv2      : TEXCOORD2;
#endif
#ifdef _TANGENT_TO_WORLDhalf4 tangent   : TANGENT;
#endifUNITY_VERTEX_INPUT_INSTANCE_ID
};

2.VertexOutputShadowCaster

#ifdef UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT
struct VertexOutputShadowCaster
{V2F_SHADOW_CASTER_NOPOS#if defined(UNITY_STANDARD_USE_SHADOW_UVS)float2 tex : TEXCOORD1;#if defined(_PARALLAXMAP)half3 viewDirForParallax : TEXCOORD2;#endif#endif
};
#endif

3.VertexOutputStereoShadowCaster

#ifdef UNITY_STANDARD_USE_STEREO_SHADOW_OUTPUT_STRUCT
struct VertexOutputStereoShadowCaster
{UNITY_VERTEX_OUTPUT_STEREO
};
#endif

4.TRANSFER_SHADOW_CASTER_NOPOS

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
#else#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \opos = UnityApplyLinearShadowBias(opos);
#endif

5.UnityClipSpaceShadowCasterPos

float4 UnityClipSpaceShadowCasterPos(float4 vertex, float3 normal)
{// 将顶点从对象空间转换到世界空间float4 wPos = mul(unity_ObjectToWorld, vertex);// 如果启用了法线偏置(normal bias)if (unity_LightShadowBias.z != 0.0){// 将法线从对象空间转换到世界空间float3 wNormal = UnityObjectToWorldNormal(normal);// 获取世界空间中的光照方向float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));// 应用法线偏置(沿法线向内偏移位置)// 偏置需要按法线和光照方向之间的正弦值进行缩放// (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/)//// unity_LightShadowBias.z 包含用户指定的法线偏置量,// 已按世界空间纹理像素大小进行了缩放。// 计算法线和光照方向之间的余弦值float shadowCos = dot(wNormal, wLight);// 计算法线和光照方向之间的正弦值float shadowSine = sqrt(1 - shadowCos * shadowCos);// 计算法线偏置float normalBias = unity_LightShadowBias.z * shadowSine;// 沿法线方向偏移世界空间位置wPos.xyz -= wNormal * normalBias;}// 将世界空间位置转换到裁剪空间return mul(UNITY_MATRIX_VP, wPos);
}// Legacy, not used anymore; kept around to not break existing user shaders
float4 UnityClipSpaceShadowCasterPos(float3 vertex, float3 normal)
{// 调用带 float4 参数的重载函数return UnityClipSpaceShadowCasterPos(float4(vertex, 1), normal);
}

6.UnityApplyLinearShadowBias

float4 UnityApplyLinearShadowBias(float4 clipPos)
{// 对于支持深度立方体贴图的点光源,偏置在采样阴影贴图的片段着色器中应用。// 这是因为传统行为的点光源阴影贴图无法通过在生成阴影贴图的顶点着色器中偏移顶点位置来实现。
#if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))#if defined(UNITY_REVERSED_Z)// 使用 max/min 而不是 clamp 以确保正确处理极少数情况下分子和分母都为零的情况,// 此时分数会变成 NaN。clipPos.z += max(-1, min(unity_LightShadowBias.x / clipPos.w, 0));#elseclipPos.z += saturate(unity_LightShadowBias.x / clipPos.w);#endif
#endif#if defined(UNITY_REVERSED_Z)float clamped = min(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
#elsefloat clamped = max(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
#endifclipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);return clipPos;
}

三、fragShadowCaster

half4 fragShadowCaster (UNITY_POSITION(vpos)
#ifdef UNITY_STANDARD_USE_SHADOW_OUTPUT_STRUCT, VertexOutputShadowCaster i
#endif
) : SV_Target
{// 如果启用了阴影UVs#if defined(UNITY_STANDARD_USE_SHADOW_UVS)// 如果启用了视差贴图且着色器目标版本大于等于3.0#if defined(_PARALLAXMAP) && (SHADER_TARGET >= 30)// 归一化视差贴图的视图方向half3 viewDirForParallax = normalize(i.viewDirForParallax);// 获取视差贴图的高度值(绿色通道)fixed h = tex2D(_ParallaxMap, i.tex.xy).g;// 计算视差偏移量half2 offset = ParallaxOffset1Step(h, _Parallax, viewDirForParallax);// 应用视差偏移量到纹理坐标i.tex.xy += offset;#endif// 如果平滑度存储在Albedo通道A中#if defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A)half alpha = _Color.a;#else// 否则,从主纹理中获取Alpha值并乘以颜色的Alpha值half alpha = tex2D(_MainTex, i.tex.xy).a * _Color.a;#endif// 如果启用了Alpha测试#if defined(_ALPHATEST_ON)clip(alpha - _Cutoff); // 裁剪掉透明度低于阈值的部分#endif// 如果启用了Alpha混合或Alpha预乘#if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)// 如果启用了Alpha预乘#if defined(_ALPHAPREMULTIPLY_ON)half outModifiedAlpha;// 预乘Alpha值PreMultiplyAlpha(half3(0, 0, 0), alpha, SHADOW_ONEMINUSREFLECTIVITY(i.tex), outModifiedAlpha);alpha = outModifiedAlpha;#endif// 如果启用了抖动掩码进行Alpha混合阴影#if defined(UNITY_STANDARD_USE_DITHER_MASK)// 基于像素位置xy和alpha级别使用抖动掩码// 我们的抖动纹理是4x4x16。#ifdef LOD_FADE_CROSSFADE#define _LOD_FADE_ON_ALPHAalpha *= unity_LODFade.y; // 应用LOD淡入因子#endif// 计算alpha参考值half alphaRef = tex3D(_DitherMaskLOD, float3(vpos.xy * 0.25, alpha * 0.9375)).a;clip(alphaRef - 0.01); // 裁剪掉不满足条件的部分#elseclip(alpha - _Cutoff); // 裁剪掉透明度低于阈值的部分#endif#endif#endif // #if defined(UNITY_STANDARD_USE_SHADOW_UVS)// 如果启用了LOD交叉渐变#ifdef LOD_FADE_CROSSFADE#ifdef _LOD_FADE_ON_ALPHA#undef _LOD_FADE_ON_ALPHA#else// 应用LOD交叉渐变UnityApplyDitherCrossFade(vpos.xy);#endif#endif// 输出阴影片段SHADOW_CASTER_FRAGMENT(i)
}

1.ParallaxOffset1Step

// Same as ParallaxOffset in Unity CG, except:
//  *) precision - half instead of float
half2 ParallaxOffset1Step (half h, half height, half3 viewDir)
{h = h * height - height/2.0;half3 v = normalize(viewDir);v.z += 0.42;return h * (v.xy / v.z);
}

2.SHADOW_ONEMINUSREFLECTIVITY

#define SHADOW_ONEMINUSREFLECTIVITY SHADOW_JOIN(UNITY_SETUP_BRDF_INPUT, _ShadowGetOneMinusReflectivity)
#define SHADOW_JOIN(a, b) SHADOW_JOIN2(a,b)
#define SHADOW_JOIN2(a, b) a##b

3.UnityApplyDitherCrossFade

// 定义一个采样器,用于访问4x4的抖动遮罩纹理
sampler2D unity_DitherMask;/*** 应用基于抖动遮罩的交叉淡入淡出效果* @param vpos 输入顶点位置,通常是屏幕空间坐标或裁剪空间坐标*/
void UnityApplyDitherCrossFade(float2 vpos)
{// 将输入的位置除以4,因为抖动遮罩纹理是4x4的分辨率// 这一步是为了将输入坐标映射到遮罩纹理的UV坐标空间vpos /= 4.0;// 使用tex2D函数从抖动遮罩纹理中采样,并获取alpha通道的值// mask的值范围在[0, 1]之间float mask = tex2D(unity_DitherMask, vpos).a;// 根据unity_LODFade.x的值确定sgn的符号// 如果unity_LODFade.x大于0,则sgn为1.0f,否则为-1.0f// unity_LODFade.x通常用于表示LOD级别的变化方向float sgn = unity_LODFade.x > 0 ? 1.0f : -1.0f;// 使用clip函数进行像素裁剪// 如果unity_LODFade.x - mask * sgn小于0,则该像素会被裁剪掉(即不会被渲染)// 这一步实现了基于抖动遮罩的交叉淡入淡出效果clip(unity_LODFade.x - mask * sgn);
}

4.SHADOW_CASTER_FRAGMENT

//_LightPositionRange 是 float4 类型的内置变量:
//xyz分量‌:表示光源在世界空间中的坐标位置。
//w分量‌:表示光源的影响范围(如点光源的半径)。
//unity_LightShadowBias 是 float4 类型的内置变量,‌//x 分量‌:shadowBias,表示常量偏移值,用于调整顶点沿光源方向的偏移距离。
//y 分量‌:shadowNormalBias,表示基于法线的偏移值,用于沿顶点法线方向内推顶点位置。
//z和w 分量‌:通常与光源类型或特定计算需求相关(如点光源的归一化参数)‌。
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)#define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
#else#define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif

5.UnityEncodeCubeShadowDepth

float4 UnityEncodeCubeShadowDepth (float z)
{#ifdef UNITY_USE_RGBA_FOR_POINT_SHADOWSreturn EncodeFloatRGBA (min(z, 0.999));#elsereturn z;#endif
}

6.EncodeFloatRGBA

// 函数:将 [0, 1) 范围内的浮点数编码为 8 位每通道的 RGBA 颜色值
// 注意:1.0 值不会被正确编码
inline float4 EncodeFloatRGBA(float v)
{// 定义乘法因子,用于将浮点数分解到四个通道中// kEncodeMul 的值分别是:// 1.0       -> 第一个通道(R)// 255.0     -> 第二个通道(G)// 65025.0   -> 第三个通道(B),即 255^2// 16581375.0-> 第四个通道(A),即 255^3float4 kEncodeMul = float4(1.0, 255.0, 65025.0, 16581375.0);// 定义用于减去溢出部分的常量,即每个通道的最大值(255)的倒数float kEncodeBit = 1.0 / 255.0;// 将输入浮点数 v 分解到四个通道中float4 enc = kEncodeMul * v;// 取每个分量的小数部分,确保每个分量都在 [0, 1) 范围内enc = frac(enc);// 减去高位通道对低位通道的溢出影响// enc.yzww 表示取 G、B 和 A 通道的值,并分别乘以 kEncodeBit// 这样可以避免高位通道的值影响低位通道enc -= enc.yzww * kEncodeBit;// 返回编码后的 RGBA 颜色值return enc;
}

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

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

相关文章

[MRCTF2020]Ez_bypass

BUUCTF在线评测BUUCTF 是一个 CTF 竞赛和训练平台,为各位 CTF 选手提供真实赛题在线复现等服务。https://buuoj.cn/challenges#[MRCTF2020]Ez_bypass启动靶机 有提示F12,那查看一下源码。和页面显示的代码一样的,就是格式更规范而已 include…

C/C++关键字——union

1.介绍union是一种特殊的数据类型,它允许你在同一块内存区域中存储不同的数据类型。它的主要目的是节省内存,尤其是在处理多种可能的数据类型,但一次只使用其中一种的场景。2.特点与 struct(结构体)不同,结…

2024 arXiv Cost-Efficient Prompt Engineering for Unsupervised Entity Resolution

论文基本信息 题目: Cost-Efficient Prompt Engineering for Unsupervised Entity Resolution 作者: Navapat Nananukul, Khanin Sisaengsuwanchai, Mayank Kejriwal 机构: University of Southern California, Information Sciences Institu…

【XR技术概念科普】什么是注视点渲染(Foveated Rendering)?为什么Vision Pro离不开它?

一、前言2023 年,苹果推出了 Vision Pro 头显,把“空间计算”概念推向大众。与以往的 XR 设备不同,Vision Pro 强调高分辨率、真实感与沉浸感。然而,这种体验背后隐藏着一个巨大的技术挑战:如何在有限的计算与能耗条件…

Qt 系统相关 - 1

虽然 Qt 是跨平台的 C 开发框架,Qt 有很多能力其实是操作系统提供的,只不过 Qt 封装了系统的 API程序时运行在操作系统上的,需要系统给我们提供支撑!事件文件操作多线程编程网络编程多媒体(音频,视频&#…

“12306”有多牛逼?从架构师的角度详细的告诉你

12306铁路票务系统架构深度解析 📚 目录 系统概述业务特点与技术挑战整体架构设计核心技术架构高并发处理策略数据存储与管理缓存体系设计分布式系统架构安全防护体系性能优化策略监控与运维技术演进历程总结与展望 每到春节、国庆这种全民迁徙的时刻,…

数据采集机器人哪家好?2025 年实测推荐:千里聆 RPA 凭什么成企业首选?

在数字化转型加速的今天,数据采集已成为企业运营的核心环节,数据采集机器人正在重构企业的效率边界。2025 年中国 RPA 市场排名显示,泛微旗下的千里聆 RPA 已跻身行业前五,成为中大型国央企的首选品牌。本文将通过三维评估体系&am…

基础crud项目(前端部分+总结)

本人根据自己对前端微不足道的理解和 AI 老师的指导下,艰难地完成了基础crud代码的全栈开发,算是自己的第一个 Java 项目,对此做个简单总结。 后端部分 在前后端分离开发中,前端负责页面交互与数据展示,后端提供接口支…

MATLAB矩阵及其运算(二)函数

函数分为MATLAB内置函数及用户自定义函数,用户可以直接调用内置函数进行数据处理。内置函数的使用函数由三部分组成:名称、输入和输出。内置函数示例:单输入单输出函数:sqrt(x);单输入多输出函数:size(x)&a…

自动化运维-ansible中对于大项目的管理

自动化运维-ansible中对于大项目的管理 一、引用主机清单 在Playbook中引用主机时,hosts 字段指定的目标必须与Ansible主机清单中定义的标识符完全匹配。如果清单中配置的是主机名,则在Playbook中使用IP地址或其他别名将无法匹配,导致任务被跳…

59_基于深度学习的麦穗计数统计系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)

目录 项目介绍🎯 功能展示🌟 一、环境安装🎆 环境配置说明📘 安装指南说明🎥 环境安装教学视频 🌟 二、数据集介绍🌟 三、系统环境(框架/依赖库)说明🧱 系统环…

面试问题详解十六:Qt 内存管理机制

在 Qt 开发过程中,很多初学者(包括不少有经验的 C 程序员)经常会产生这样的疑问:“我在 Qt 中 new 出来的控件好像都没有 delete,那内存不会泄漏吗?”比如下面这段代码: void Widget::createLef…

Pycharm 试用

Ubuntu 重置Pycharm试用期限(30 天) 先关闭Pycharm删除系统缓存 rm -rf ~/.config/JetBrains/ && rm -rf ~/.local/share/JetBrains/ && rm -rf ~/.cache/JetBrains/删除已经安装的 Pycharm 软件运行目录去官网下载新的 就行了

C++ Qt 开发核心知识

Qt 框架概述Qt 是一个跨平台的 C 应用程序开发框架,广泛用于开发图形用户界面程序。其核心特性包括跨平台能力、丰富的功能模块和强大的工具集。核心概念与机制元对象系统Qt 扩展了标准 C,通过元对象系统提供信号与槽机制、运行时类型信息和动态属性系统…

net9 aspose.cell 自定义公式AbstractCalculationEngine,带超链接excel转html后背景色丢失

AbstractCalculationEngine 是 Aspose.Cells 中一个强大的抽象类,允许您自定义公式计算逻辑。当您需要覆盖默认计算行为或实现自定义函数时非常有用。直接上代码1. 创建自定义计算引擎using Aspose.Cells; using System;// 创建自定义计算引擎 public class CustomC…

如何监控员工的电脑?7款实用的员工电脑管理软件,探索高效管理捷径!

当销售团队在淘宝刷单、设计师用公司电脑挖矿、程序员频繁访问代码托管网站时,企业损失的不仅是带宽——低效、泄密、合规风险正成为隐形利润杀手。 传统管理依赖“人盯人”或抽查日志,但面对分布式办公与远程协作趋势,这些方法早已力不从心…

机器视觉软件--VisionPro、Visual Master,Halcon 和 OpenCV 的学习路线

Halcon 和 OpenCV区别 Halcon 和 OpenCV 都是计算机视觉领域的重要工具,但它们的设计理念、功能侧重和适用场景有显著不同。下面这个表格汇总了它们的核心区别,方便你快速了解: 开发模式与体验​​:Halcon 配备了强大的​​图形化…

算法-根据前序+中序遍历打印树的右视图

题目请根据二叉树的前序遍历,中序遍历恢复二叉树,并打印出二叉树的右视图数据范围: 0≤n≤100000≤n≤10000 要求: 空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)如输入[1,2,4,5,3],[4,2,5,1,3]时,通过前序遍历…

Kafka面试精讲 Day 7:消息序列化与压缩策略

【Kafka面试精讲 Day 7】消息序列化与压缩策略 在Kafka的高性能消息系统中,消息序列化与压缩是影响吞吐量、延迟和网络开销的核心环节。作为“Kafka面试精讲”系列的第7天,本文聚焦于这一关键主题,深入剖析其原理、实现方式、配置策略及常见…

Xterminal软件下载_Xterminal ssh远程链接工具下载__Xterminal安装包 网盘下载_Xterminal ssh远程链接工具安装包

Xterminal 作为一款国产 SSH 工具,专为开发人员量身打造。它支持 SSH 和 Telnet 协议连接远程服务器与虚拟机,无论是进行代码部署,还是服务器运维,都能轻松胜任。软件界面采用极简设计,黑色背景搭配白色文字&#xff0…