解读unity内置的软阴影处理方式

解读unity内置的软阴影处理方式:
参考网址:
https://blog.csdn.net/cgy56191948/article/details/105726682
https://blog.csdn.net/weixin_45776473/article/details/119582218
https://tajourney.games/5482/
上面的博客已经论述了,为何出现锯齿,并从连续性角度,论述了pcf解决方案,使得阴影能够软。
本文主要是针对算法的实现上,剖析其实现的细节。

* PCF tent shadowmap filtering based on a 3x3 kernel (optimized with 4 taps)
*/
half UnitySampleShadowmap_PCF3x3Tent(float4 coord, float3 receiverPlaneDepthBias)
{half shadow = 1;#ifdef SHADOWMAPSAMPLER_AND_TEXELSIZE_DEFINED#ifndef SHADOWS_NATIVE// when we don't have hardware PCF sampling, fallback to a simple 3x3 sampling with averaged results.return UnitySampleShadowmap_PCF3x3NoHardwareSupport(coord, receiverPlaneDepthBias);#endif// tent base is 3x3 base thus covering from 9 to 12 texels, thus we need 4 bilinear PCF fetchesfloat2 tentCenterInTexelSpace = coord.xy * _ShadowMapTexture_TexelSize.zw;float2 centerOfFetchesInTexelSpace = floor(tentCenterInTexelSpace + 0.5);float2 offsetFromTentCenterToCenterOfFetches = tentCenterInTexelSpace - centerOfFetchesInTexelSpace;// find the weight of each texel basedfloat4 texelsWeightsU, texelsWeightsV;_UnityInternalGetWeightPerTexel_3TexelsWideTriangleFilter(offsetFromTentCenterToCenterOfFetches.x, texelsWeightsU);_UnityInternalGetWeightPerTexel_3TexelsWideTriangleFilter(offsetFromTentCenterToCenterOfFetches.y, texelsWeightsV);// each fetch will cover a group of 2x2 texels, the weight of each group is the sum of the weights of the texelsfloat2 fetchesWeightsU = texelsWeightsU.xz + texelsWeightsU.yw;float2 fetchesWeightsV = texelsWeightsV.xz + texelsWeightsV.yw;// move the PCF bilinear fetches to respect texels weightsfloat2 fetchesOffsetsU = texelsWeightsU.yw / fetchesWeightsU.xy + float2(-1.5,0.5);float2 fetchesOffsetsV = texelsWeightsV.yw / fetchesWeightsV.xy + float2(-1.5,0.5);fetchesOffsetsU *= _ShadowMapTexture_TexelSize.xx;fetchesOffsetsV *= _ShadowMapTexture_TexelSize.yy;// fetch !float2 bilinearFetchOrigin = centerOfFetchesInTexelSpace * _ShadowMapTexture_TexelSize.xy;shadow =  fetchesWeightsU.x * fetchesWeightsV.x * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.x, fetchesOffsetsV.x), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.y * fetchesWeightsV.x * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.y, fetchesOffsetsV.x), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.x * fetchesWeightsV.y * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.x, fetchesOffsetsV.y), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.y * fetchesWeightsV.y * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.y, fetchesOffsetsV.y), coord.z, receiverPlaneDepthBias));
#endifreturn shadow;
}

传入的参数:float4 coord是(0,1)阴影贴图的纹理坐标值。
接下来说明三角形面积的计算方式:
在这里插入图片描述
如上图所示,红色的p点为阴影处的点。我们要对它进行软阴影处理。
情况1)三角形偏移为0时,三角形面积被分为4个部分的面积情况。
三角形的底为3,高为1.5,之所以使用这样的三角形,因为计算简单。
红色面积:1/8
橙色面积:1
绿色面积:1
蓝色面积:1/8
情况2)三角形偏移为-0.5时,三角形面积被分为4个部分的面积情况。
在这里插入图片描述
红色面积:0.5
橙色面积:1.25
绿色面积:0.5
蓝色面积:0
情况3)三角形偏移为0.5时,三角形面积被分为4个部分的面积情况
在这里插入图片描述
红色面积:0
橙色面积:0.5
绿色面积:1.25
蓝色面积:0.5
然后推广到更一般的情况,给定偏移offset,计算三角形被分割的四个部分的面积是多少,公式为:
在这里插入图片描述
对应的这段代码是:

// ------------------------------------------------------------------
//  PCF Filtering helpers
// ------------------------------------------------------------------/**
* Assuming a isoceles rectangle triangle of height "triangleHeight" (as drawn below).
* This function return the area of the triangle above the first texel.
*
* |\      <-- 45 degree slop isosceles rectangle triangle
* | \
* ----    <-- length of this side is "triangleHeight"
* _ _ _ _ <-- texels
*/
float _UnityInternalGetAreaAboveFirstTexelUnderAIsocelesRectangleTriangle(float triangleHeight)
{return triangleHeight - 0.5;
}/**
* Assuming a isoceles triangle of 1.5 texels height and 3 texels wide lying on 4 texels.
* This function return the area of the triangle above each of those texels.
*    |    <-- offset from -0.5 to 0.5, 0 meaning triangle is exactly in the center
*   / \   <-- 45 degree slop isosceles triangle (ie tent projected in 2D)
*  /   \
* _ _ _ _ <-- texels
* X Y Z W <-- result indices (in computedArea.xyzw and computedAreaUncut.xyzw)
*/
void _UnityInternalGetAreaPerTexel_3TexelsWideTriangleFilter(float offset, out float4 computedArea, out float4 computedAreaUncut)
{//Compute the exterior areasfloat offset01SquaredHalved = (offset + 0.5) * (offset + 0.5) * 0.5;computedAreaUncut.x = computedArea.x = offset01SquaredHalved - offset;computedAreaUncut.w = computedArea.w = offset01SquaredHalved;//Compute the middle areas//For Y : We find the area in Y of as if the left section of the isoceles triangle would//intersect the axis between Y and Z (ie where offset = 0).computedAreaUncut.y = _UnityInternalGetAreaAboveFirstTexelUnderAIsocelesRectangleTriangle(1.5 - offset);//This area is superior to the one we are looking for if (offset < 0) thus we need to//subtract the area of the triangle defined by (0,1.5-offset), (0,1.5+offset), (-offset,1.5).float clampedOffsetLeft = min(offset,0);float areaOfSmallLeftTriangle = clampedOffsetLeft * clampedOffsetLeft;computedArea.y = computedAreaUncut.y - areaOfSmallLeftTriangle;//We do the same for the Z but with the right part of the isoceles trianglecomputedAreaUncut.z = _UnityInternalGetAreaAboveFirstTexelUnderAIsocelesRectangleTriangle(1.5 + offset);float clampedOffsetRight = max(offset,0);float areaOfSmallRightTriangle = clampedOffsetRight * clampedOffsetRight;computedArea.z = computedAreaUncut.z - areaOfSmallRightTriangle;
}

这个公式后面会给出详细的证明。
然后计算四个划分的面积占总面积的多少,由于等腰直角三角形的底是3,高为1.5,所以面积是9/4,求权重比,对应的代码是:

/*** Assuming a isoceles triangle of 1.5 texels height and 3 texels wide lying on 4 texels.* This function return the weight of each texels area relative to the full triangle area.*/
void _UnityInternalGetWeightPerTexel_3TexelsWideTriangleFilter(float offset, out float4 computedWeight)
{float4 dummy;_UnityInternalGetAreaPerTexel_3TexelsWideTriangleFilter(offset, computedWeight, dummy);computedWeight *= 0.44444;//0.44 == 1/(the triangle area)
}

此时得到了水平方向的四个权重,然后我们同样的方法得到垂直方向的四个权重,然后咋办?
在这里插入图片描述
我们可以把x和y两个看成连续的块,然后把z和w看成连续的块,然后对应代码:

    // each fetch will cover a group of 2x2 texels, the weight of each group is the sum of the weights of the texelsfloat2 fetchesWeightsU = texelsWeightsU.xz + texelsWeightsU.yw;

于是fetchesWeightU.x = texelsWeightsU.x + texelsWeightsU.y
fetchesWeightU.y = texelsWeightsU.z + texelsWeightsU.w
然后,就是计算y占(x+y)的百分比,w占(z+w)的百分比

    // move the PCF bilinear fetches to respect texels weightsfloat2 fetchesOffsetsU = texelsWeightsU.yw / fetchesWeightsU.xy + float2(-1.5,0.5);

fetchesOffsetsU.x = texelsWeightsU.y / (texelsWeightsU.x + texelsWeightsU.y)
fetchesOffsetsU.y = texelsWeightsU.w / (texelsWeightsU.z + texelsWeightsU.w)
这样就得到的fetchesOffsetsU,是大于等于0的偏移,所以为了得到相对的偏移得归一化到起点。
在这里插入图片描述
如上图粉色框框起来的x和y点,其水平起点为三角形的底/2,取负,所以是-1.5
在这里插入图片描述
而又因为是相邻每两个像素的归一化,所以绿色框住的起点是-1.5+2=0.5,于是得到上面的偏移:+float2(-1.5,0.5);
同样的垂直方向上也是类似的计算方式,最终我们得到了:

// move the PCF bilinear fetches to respect texels weightsfloat2 fetchesOffsetsU = texelsWeightsU.yw / fetchesWeightsU.xy + float2(-1.5,0.5);float2 fetchesOffsetsV = texelsWeightsV.yw / fetchesWeightsV.xy + float2(-1.5,0.5);

然后我们将转换到(0,1)的范围,直接乘以:

    fetchesOffsetsU *= _ShadowMapTexture_TexelSize.xx;fetchesOffsetsV *= _ShadowMapTexture_TexelSize.yy;

这里的_ShadowMapTexture_TexelSize的x、y、z、w分量分别是阴影贴图的水平像素个数倒数,垂直像素个数倒数,水平像素个数,垂直像素个数。
然后就是去采样了:

    // fetch !float2 bilinearFetchOrigin = centerOfFetchesInTexelSpace * _ShadowMapTexture_TexelSize.xy;shadow =  fetchesWeightsU.x * fetchesWeightsV.x * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.x, fetchesOffsetsV.x), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.y * fetchesWeightsV.x * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.y, fetchesOffsetsV.x), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.x * fetchesWeightsV.y * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.x, fetchesOffsetsV.y), coord.z, receiverPlaneDepthBias));shadow += fetchesWeightsU.y * fetchesWeightsV.y * UNITY_SAMPLE_SHADOW(_ShadowMapTexture, UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.y, fetchesOffsetsV.y), coord.z, receiverPlaneDepthBias));
#endif

float2 bilinearFetchOrigin = centerOfFetchesInTexelSpace * _ShadowMapTexture_TexelSize.xy;
是p点,就是要处理的点。
然后就是:
UnityCombineShadowcoordComponents(bilinearFetchOrigin, float2(fetchesOffsetsU.x, fetchesOffsetsV.x), coord.z, receiverPlaneDepthBias)
这个函数

/**
* Combines the different components of a shadow coordinate and returns the final coordinate.
* See UnityGetReceiverPlaneDepthBias
*/
float3 UnityCombineShadowcoordComponents(float2 baseUV, float2 deltaUV, float depth, float3 receiverPlaneDepthBias)
{float3 uv = float3(baseUV + deltaUV, depth + receiverPlaneDepthBias.z);uv.z += dot(deltaUV, receiverPlaneDepthBias.xy);return uv;
}

baseUV + deltaUV,就是进行uv的偏移。然后z值,可以uv和阴影bias点乘,加到z上,最终得到一个三维的采样坐标。
最终:fetchesWeightsU.x * fetchesWeightsV.x * UNITY_SAMPLE_SHADOW
得到一个点的权重,然后分别做四次即可,得到四个方向上的阴影贡献值,即可得到最后软阴影效果了。

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

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

相关文章

5个免费、跨平台的SQLite数据库可视化工具

前言 SQLite是一个轻量级的嵌入式关系型数据库&#xff0c;目前最新的版本是 SQLite3。今天推荐5个实用的SQLite数据库可视化工具(GUI)&#xff0c;帮助大家更好的管理SQLite数据库。 什么是SQLite&#xff1f; SQLite是一个轻量级的嵌入式关系型数据库&#xff0c;它以一个…

风速预测(五)基于Pytorch的EMD-CNN-LSTM模型

目录 前言 1 风速数据EMD分解与可视化 1.1 导入数据 1.2 EMD分解 2 数据集制作与预处理 2.1 先划分数据集&#xff0c;按照8&#xff1a;2划分训练集和测试集 2.2 设置滑动窗口大小为96&#xff0c;制作数据集 3 基于Pytorch的EMD-CNN-LSTM模型预测 3.1 数据加载&…

C++软件调试与异常排查技术从入门到精通学习路线分享

目录 1、概述 2、全面了解引发C软件异常的常见原因 3、熟练掌握排查C软件异常的常见手段与方法 3.1、IDE调试 3.2、添加打印日志 3.3、分块注释代码 3.4、数据断点 3.5、历史版本比对法 3.6、Windbg静态分析与动态调试 3.7、使用IDA查看汇编代码 3.8、使用常用工具分…

2023年度佳作:AIGC、AGI、GhatGPT、人工智能大语言模型的崛起与挑战

目录 前言 01 《ChatGPT 驱动软件开发》 内容简介 02 《ChatGPT原理与实战》 内容简介 03 《神经网络与深度学习》 04 《AIGC重塑教育》 内容简介 05 《通用人工智能》 目  录 前言 2023年是人工智能大语言模型大爆发的一年&#xff0c;一些概念和英文缩写也在这一…

2020年第九届数学建模国际赛小美赛D题石头剪刀游戏与合作解题全过程文档及程序

2020年第九届数学建模国际赛小美赛 D题 石头剪刀游戏与合作 原题再现&#xff1a; 小时候你可能至少玩过几次石头剪刀游戏。在这个游戏中&#xff0c;你几乎有三个选择&#xff0c;每一个都有一个项目要打败&#xff0c;一个项目输给。石头打败剪刀&#xff0c;剪刀剪纸和布覆…

windows电脑半夜突然睡眠自动唤醒的问题查找与治理

遇见几次了&#xff0c;半夜起来上厕所&#xff0c;发现休眠的电脑居然自己开了&#xff0c;还得跑过去把电脑再休眠&#xff0c;很烦。昨天晚上居然自动唤醒两次&#xff0c;忍无可忍了&#xff0c;于是开始查找原因。 查询原因如下&#xff0c;解决方面也在后面。 固件 S3 计…

网络(九)三层路由、DHCP以及VRRP协议介绍

目录 一、三层路由 1. 定义 2. 交换原理 3. 操作演示 3.1 图示 3.2 LSW1新建vlan10、20、30&#xff0c;分别对应123接口均为access类型&#xff0c;接口4为trunkl类型&#xff0c;允许所有vlan通过 3.3 LSW2新建vlan10、20、30&#xff0c;配置接口1为trunk类型&…

一天吃透MySQL面试八股文

目录 事务的四大特性&#xff1f;数据库的三大范式事务隔离级别有哪些&#xff1f;生产环境数据库一般用的什么隔离级别呢&#xff1f;编码和字符集的关系utf8和utf8mb4的区别什么是索引&#xff1f;索引的优缺点&#xff1f;索引的作用&#xff1f;什么情况下需要建索引&…

Python 全栈体系【四阶】(六)

第四章 机器学习 五、线性模型 1. 概述 线性模型是自然界最简单的模型之一&#xff0c;它描述了一个&#xff08;或多个&#xff09;自变量对另一个因变量的影响是呈简单的比例、线性关系。例如&#xff1a; 住房每平米单价为 1 万元&#xff0c;100 平米住房价格为 100 万…

Python计算圆的面积,几何学技法大解析!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python计算圆的面积&#xff0c;几何学技法大解析&#xff0c;全文3800字&#xff0c;阅读大约15分钟。 在本文中&#xff0c;将深入探讨如何使用 Python 计算圆的面积&…

数字化转型导师坚鹏:中国工商银行人工智能与金融数字化转型培训

中国工商银行打造D-ICBC数字化转型战略&#xff0c;围绕“数字生态、数字资产、数字技术、数字基建、数字基因”五维布局&#xff0c;深入推进数字化转型&#xff0c;加快形成体系化、生态化实施路径&#xff0c;促进科技与业务加速融合&#xff0c;以“数字工行”建设推动“GB…

basic_pentesting_1

信息收集 # nmap -sn 192.168.1.0/24 -oN live.nmap Starting Nmap 7.94 ( https://nmap.org ) at 2023-12-11 15:19 CST Nmap scan report for 192.168.1.1 Host is up (0.00023s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report f…

〖大前端 - 基础入门三大核心之JS篇(56)〗- 内置构造函数

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…

排序-归并排序与计数排序

文章目录 一、归并排序1、概念2、过程3、代码实现4、复杂度5、稳定性 二、 计数排序1、思路2、代码实现3、复杂度&#xff1a;4、稳定性 一、归并排序 1、概念 是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已…

黑色翻页时钟HTML源码-倒计时单页翻页时钟

黑色翻页时钟HTML源码-倒计时单页翻页时钟这是一个类似fliqlo的黑色翻页时钟HTML源码&#xff0c;它仅包含一个HTML文件&#xff0c;上传到网站后即可使用。该时钟具有查看当前时间、秒表和倒计时功能&#xff0c;并且可以在页面的右下角进行设置。 红色动态炫酷数字时钟html网…

智能优化算法应用:基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.和声算法4.实验参数设定5.算法结果6.参考文献7.MA…

大一python题库刷题训练,大一python填空题题库

大家好&#xff0c;给大家分享一下大一python题库及答案和分析&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 这篇文章主要介绍了大一python上机题库及答案&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完…

化妆刷适合用超声波清洗机洗吗?超声波清洗机可以洗什么物品?

随着科技发展&#xff0c;现在有很多物品都可以交给机器去清洗了&#xff0c;是越来越智能化&#xff01;其中&#xff0c;超声波清洗机就是一项非常实用的技术。它利用高频振动和清洗液的共同作用&#xff0c;能够有效地清除各种物品上的污渍和细菌。而在我们的日常生活中&…

27系列DGUS智能屏发布:可实时播放高清模拟信号摄像头视频

针对高清晰度的模拟信号摄像头视频画面的显示需求&#xff0c;迪文特推出27系列DGUS智能屏。该系列智能屏可适配常见的AHD摄像头、CVBS摄像头&#xff0c;支持单路1080P高清显示、两路720P同屏显示&#xff08;同一类型摄像头&#xff09;。用户通过DGUS简单开发即可实现摄像头…

Android 升级签名算法从SHA1withRSA 升级到SHA256withRSA

一 背景 想到要修改这个问题 是因为收到下面整改通知 要求我们更新签名文件的签名算法&#xff0c;看到这个问题都懵了呀 虽然打包天天用签名文件 但是从来没关注过他呀 开发者自查&#xff1a; 不要使用已经过时或不安全的弱算法&#xff0c;确保签名证书使用了更强的签名算法…