解讀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.pswp.cn/news/214495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/214495.shtml
英文地址,請注明出處:http://en.pswp.cn/news/214495.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

個人博客搭建保姆級教程-Nginx篇

官方文檔 nginx documentation 說明 nginx是我們本次教程使用的http服務器。它能承受很高的并發&#xff0c;并且安裝簡單&#xff0c;占用內存少。 在服務器篇我們提到了nginx的安裝&#xff0c;在發布篇我們簡述了該怎么放置我們創建的博客html文檔。 在本篇&#xff0c…

css的復合選擇器(有案例)

目錄 復合選擇器的描述 后代選擇器&#xff08;常用重點&#xff09; 子選擇器 并集選擇器&#xff08;重點常用&#xff09; 偽類選擇器 鏈接偽類選擇器 focus 偽類選擇器 知識總結&#xff1a; 案例實現&#xff1a; 復合選擇器的描述 在 CSS 中&#xff0c;可以根…

日志門面slf4j和各日志框架

簡介 簡單日志門面(Simple Logging Facade For Java) SLF4J主要是為了給Java日志訪問提供一套標準、規范的API框架&#xff0c; 其主要意義在于提供接口&#xff0c;具體的實現可以交由其他日志框架&#xff0c;如log4j、logback、log4j2。 對于一般的Java項目而言&#xff…

一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具 `kobs-ng`

一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具 kobs-ng kobs-ng 是一個用于處理嵌入式系統中的 NAND Flash 存儲器的工具。它是 U-Boot&#xff08;開源引導加載程序&#xff09;中的一個子項目&#xff0c;用于擦除、寫入和讀取 NAND Flash 設備上的數據。 以下是 kob…

SpringData JPA 搭建 xml的 配置方式

1.導入版本管理依賴 到父項目里 <dependencyManagement><dependencies><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-bom</artifactId><version>2021.1.10</version><scope>…

【力扣100】238.除自身以外數組的乘積

添加鏈接描述 class Solution:def productExceptSelf(self, nums: List[int]) -> List[int]:# 構造第i個數的左右數組n len(nums)left,right,res [1]*n,[1]*n,[1]*nfor i in range(1,n):left[i] nums[i-1]*left[i-1]for i in range(n-2,-1,-1):right[i] nums[i1]*right…

STM32Cube高效開發教程<基礎篇>(十二)----ADC

聲明:本人水平有限,博客可能存在部分錯誤的地方,請廣大讀者諒解并向本人反饋錯誤。 ?? 本專欄博客參考《STM32Cube高效開發教程(基礎篇)》,有意向的讀者可以購買正版書籍輔助學習,本書籍由王維波老師、鄢志丹老師、王釗老師傾力打造,書籍內容干貨滿滿。 一、功能概述 …

【C++11】lambda表達式及包裝器

一.lambda表達式 1.可調用對象 可調用對象即可以像函數一樣被調用的對象&#xff0c;有以下三種&#xff1a; 函數(指針)仿函數對象lambda表達式 tips&#xff1a;調用函數時&#xff0c;既可以用函數名&#xff0c;也可以用函數地址&#xff0c;因為函數名和函數地址是一回事…

Python從入門到精通五:Python數據容器

數據容器入門 為什么學習數據容器 思考一個問題&#xff1a;如果我想要在程序中&#xff0c;記錄5名學生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 學習數據容器&#xff0c;就是為了批量存儲或批量使用多份數據 Python中的數據容器&#xff1a; 一種可以容納多份…

Kalman濾波、擴展Kalman濾波、無跡Kalman濾波和異步濾波的原理及其Matlab代碼

目錄 引言Kalman濾波代碼及其結果展示 擴展Kalman濾波代碼及其結果展示 無跡Kalman濾波無跡變換無跡Kalman濾波代碼及其結果展示 異步無跡Kalman濾波原理代碼及其結果展示 引言 本文給出了Kalman Filter&#xff08;卡爾曼濾波&#xff09;、Extended Kalman Filter&#xff0…

leetcode 98. 驗證二叉搜索樹

leetcode 98. 驗證二叉搜索樹 題目 給你一個二叉樹的根節點 root &#xff0c;判斷其是否是一個有效的二叉搜索樹。 有效 二叉搜索樹定義如下&#xff1a; 節點的左子樹只包含 小于 當前節點的數。 節點的右子樹只包含 大于 當前節點的數。 所有左子樹和右子樹自身必須也是…

vue3 引入 markdown編輯器

參考文檔 安裝依賴 pnpm install mavon-editor // "mavon-editor": "3.0.1",markdown 編輯器 <mavon-editor></mavon-editor>新增文本 <mavon-editor ref"editorRef" v-model"articleModel.text" codeStyle"…

Adams與Abaqus沖突問題

隨著工程仿真軟件的廣泛應用&#xff0c;Adams和Abaqus已成為眾多工程師的首選工具。然而&#xff0c;在使用過程中&#xff0c;一些用戶可能會遇到這兩個軟件之間的沖突問題&#xff0c;導致無法正常進行仿真分析。為了幫助大家解決這一難題&#xff0c;我們推出了一篇關于Ada…

Softmax回歸

一、Softmax回歸關鍵思想 1、回歸問題和分類問題的區別 Softmax回歸雖然叫“回歸”&#xff0c;但是它本質是一個分類問題。回歸是估計一個連續值&#xff0c;而分類是預測一個離散類別。 2、Softmax回歸模型 Softmax回歸跟線性回歸一樣將輸入特征與權重做線性疊加。與線性回歸…

Linux安裝Nginx并部署Vue項目

今天部署了一個Vue項目到阿里云的云服務器上&#xff0c;現記錄該過程。 1. 修改Vue項目配置 我們去項目中發送axios請求的文件里更改一下后端的接口路由&#xff1a; 2. 執行命令打包 npm run build ### 或者 yarn build 打包成功之后&#xff0c;我們會看到一個dist包&a…

[MySQL]SQL優化之索引的使用規則

&#x1f308;鍵盤敲爛&#xff0c;年薪30萬&#x1f308; 目錄 一、索引失效 &#x1f4d5;最左前綴法則 &#x1f4d5;范圍查詢> &#x1f4d5;索引列運算&#xff0c;索引失效 &#x1f4d5;前模糊匹配 &#x1f4d5;or連接的條件 &#x1f4d5;字符串類型不加 …

110. 平衡二叉樹(Java)

給定一個二叉樹&#xff0c;判斷它是否是高度平衡的二叉樹。 本題中&#xff0c;一棵高度平衡二叉樹定義為&#xff1a; 一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1 。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;t…

如何通過SPI控制Peregrine的數控衰減器

概要 Peregrine的數控衰減器PE4312是6位射頻數字步進衰減器(DSA,Digital Step Attenuator)工作頻率覆蓋1MHz~4GHz,插入損耗2dB左右,衰減步進0.5dB,最大衰減量為31.5dB,高達59dBm的IIP3提供了良好的動態性能,切換時間0.5微秒,供電電源2.3V~5.5V,邏輯控制兼容1.8V,20…

?如何使用https://www.krea.ai/來實現文生圖,圖生圖,

網址&#xff1a;https://www.krea.ai/apps/image/realtime Krea.ai 是一個強大的人工智能藝術生成器&#xff0c;可用于創建各種創意內容。它可以用來生成文本描述的圖像、將圖像轉換為其他圖像&#xff0c;甚至寫博客文章。 文本描述生成圖像 要使用 Krea.ai 生成文本描述…

設計模式——建造者模式(Java示例)

引言 生成器是一種創建型設計模式&#xff0c; 使你能夠分步驟創建復雜對象。 與其他創建型模式不同&#xff0c; 生成器不要求產品擁有通用接口。 這使得用相同的創建過程生成不同的產品成為可能。 復雜度&#xff1a; 中等 流行度&#xff1a; 流行 使用示例&#xff1a…