首页 > 其他分享 >Lecture 03 Real-time Shadows 1

Lecture 03 Real-time Shadows 1

时间:2024-09-01 13:36:05浏览次数:3  
标签:Real int Omega 阴影 dx time Lecture Shadow omega

Real-time Shadows 1

Recap: shadow mapping

Shadow Mapping

  • 2-Pass Algorithm
    • The light pass generates the shadow map
    • the camera pass uses the shadow map
  • An image-space algorithm
    • 好处:无需场景中的几何信息
    • 坏处:导致自遮挡和走样问题

Pass

  • Pass 1: Render from light

    • 从光源输出深度图
  • Pass 2: Render from Eye

    • 从相机出发渲染

    • 将实际相机看到的像素投影回光源看到的深度图(逆变换)记录的像素上,如果该点深度与光源记录的深度图中对应像素深度一致,则说明是同一个点,可以被看到,否则说明这个点是光源看不到的点,说明在阴影中

      使用Z值或者离相机的实际距离都可以,但两次Pass要比较同一种值

Shadow Mapping中的问题

Self occlusion 自遮挡

  • 解决方法:

    • 增加一个bias减轻自遮挡

      bias过大,出现Peter Pan现象(detached shadow)

      工业界几乎没有方法解决,只能找一个比较合适的bias

    • Second-depth shadow mapping
      • Shadow Map中存最小深度和第二小深度

      • 使用最小深度和最二小深度的中值来做后续阴影比较

      • 这样无需设定bias(相当于自适应bias)

      • 工业界用的少

        原因:

        • 要求所有物体是watertight,即必须有正面反面,就算是一张纸,也得是很薄的一个物体

          比如说“地板”不是一个watertight(解决方法,记录次浅深度为无限大)

        • 输入的fragment是无序的,要始终保持最小和次小,要涉及到Swap,虽然时间复杂度仍然是\(O(n)\),能够实现,但是实时渲染不相信复杂度,只相信绝对的速度,因为实时渲染要求速度很苛刻,\(n\)和\(2n\)差异很大

Aliasing 走样

The math behind shadow mapping

微积分中的不等式

  • Schwaz施瓦茨不等式

    \[[\int_a^bf(x)g(x)dx]^2\le\int_a^bf^2(x)dx\cdot\int_a^bg^2(x)dx \]

  • Minkowski明可夫斯基不等式

    \[\{\int_a^b[f(x)+g(x)]^2dx\}^{\frac{1}{2}} \le \{\int_a^b f^2(x)dx\}^{\frac{1}{2}} + \{\int_a^b g^2(x)dx\}^{\frac{1}{2}} \]

实时渲染中的重要约等式

\[\int_{\Omega}f(x)g(x)dx \approx \frac{\int_{\Omega}f(x)dx}{\int_{\Omega}dx} \cdot \int_{\Omega}g(x)dx \]

这里将两个乘积的积分转化成了两个积分的乘积,右侧分母\(\int_{\Omega}dx(=\int_{\Omega}1dx)\)是归一化常数

以下条件满足任一可认为约等式成立

  • Small support(当积分域很小时)

    • point/directional lighting

      点光源或方向光时,只有一个点有光照

  • Smooth integrand,g(x)在积分域内比较光滑

    • (diffuse bsdf / constant radiance area lighting)

      如有一个面光源,面光源内部给出radiance不变,这样L就完完全全smooth了

      在BRDF中,当BRDF是diffuse时,认为其变化非常小,当是glosssing,则不行

  • 即使两项都不满足,也能强行用,如Ambient Occultation环境光遮蔽中

在Shadow Mapping中

\[L_o(p,\omega_o)=\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_iV(p,\omega_i)d\omega_i\\ 通过上面的约等式,可以转化成\\ L_0(p,\omega_o) \approx \frac{\int_{\Omega^+}V(p,\omega_i)d\omega_i}{\int_{\Omega^+d\omega_i}}\cdot\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_id\omega_i\\ 左式是Visibility(上式的V),右式是shading,这样就把shading和visibility分开了 \]

于是在BRDF中,对于面光源或环境光照(环境光照可以理解为一个超大的面光源),Shadow Mapping是不准的

Percentage closer soft shadows (PCSS)

Soft shadows 软阴影:本影到没有阴影处的过渡

Percentage Closer Filtering (PCF)

PCF最早用于反走样(抗锯齿)

PCF的两点注意

  • 不是对最后已经有阴影的地方进行Filter
  • 如果是对Shadow Map做模糊,那阴影处和阴影外之间模糊的一圈毫无物理意义,且第二趟Pass和模糊了的Shadow Map做深度测试,得到的结果还是非0即1

PCF的工作原理:

普通Shadow Map是比较shading point变换到Shadow Map上对应点的深度,而PCF比较对应点周围一圈的深度(比如说\(7\times 7\)),将做完比较的结果平均(每次比较结果非0即1),得到一个visibility

PCF Filter的是任意一个shading point,做很多次阴影比较的结果

Filter size:

Filter size越大阴影越软。越小阴影越硬,那么是否各个不同的位置都要给一个相同大小的Filter size呢?

如图这里笔尖阴影非常锐利,几乎没有软阴影,而远处的阴影有软阴影

远近取决于shading point离投射阴影的point的距离(与遮挡物距离有关,与光源距离无关)

关键结论

Filter size取决于遮挡物距离,更精确的说,与投射阴影的遮挡物的平均深度有关

根据相似三角形,可以得到半影范围与遮挡物距离的关系\(w_{Penumbra}=(d_{Receiver}-d_{Blocker})\cdot w_{Light}/d_{Blocker}\),这里\(w_{Penumbra}\)一定程度就可以表示阴影软的程度

*对于面光源,原本是无法生成Shadow Map的,一般做法是将其当成点光源处理,将相机放在面光源中心生成Shadow Map

PCSS

  • step 1: Blocker search

    在特定范围内获得average blocker depth

    取周围区域,判断在不在阴影里,将所有Blocker的深度取平均记录(不是Blocker不管)

  • step 2: Penumbra estimation

    使用average blocker depth确定filter size

    知道各处filter size多大,后续就跟PCF做法一样了

  • step 3: Pencertage Closer Filtering

要获得average blocker depth时,需确定在多大范围内比较

  • 设为常量(如\(5\times5\))
    • 取决于光源大小
    • 以及shading point离光源的距离

Filter导致的开销问题?

多光源场景下如果使用Shadow Map,只能逐光源处理(延迟渲染不能解决阴影,延迟渲染解决光照)

点光源和平行光源本身就应该产生硬阴影

PCSS核心就是一个适应性的Filter size

标签:Real,int,Omega,阴影,dx,time,Lecture,Shadow,omega
From: https://www.cnblogs.com/Tellulu/p/18391221

相关文章

  • MySQL中日期和时间戳的转换:字符到DATE和TIMESTAMP的相互转换
    在MySQL中,经常需要在DATE、TIMESTAMP和字符串之间进行相互转换。以下是一些常见的转换方法:1.字符串到日期/时间类型字符串转DATE:使用STR_TO_DATE()函数将字符串转换为DATE类型。你需要提供字符串的格式。SELECTSTR_TO_DATE('2024-08-24','%Y-%m-%d')ASmy_......
  • C# 定时器 Timer 如何精确到 1-2 毫秒以内
    最近在排查项目OTA的一个问题,触发了一毫秒或者2毫秒执行一次进程间通信的,导致通信阻塞的问题。这样就需要用到模拟触发1ms或者2ms触发事件。这让我第一时间想到了C#的定时器。由于我们项目用到的框架是基于.NETFramwork4.8的,所以我就建立了一个.NETFramwork4.8的WPFDemo去验证......
  • Android 11 About SleepToken / (Settings)Screen timeout
    AndroidR激活Settings里面的息屏休眠,最前台运行的Activity生命周期->onPause->onStopadbshelldumpsyswindowpolicy//获取PhoneWindowManager的dump信息...bootCompleted=truescreenState=SCREEN_STATE_ON/SCREEN_STATE_OFFinteractiveState=INTERACTIVE_STATE_AW......
  • java.time包时间类浅谈
    Java早期日期时间API:java.util.Date与java.util.Calendar一、背景在Java的早期版本中,处理日期和时间的需求主要由java.util.Date类和随后加入的java.util.Calendar类来满足。这两个类在Java1.0和Java1.1中分别被引入,为Java程序提供了基本的日期和时间操作能力。然而,随着......
  • MySQL 使用pt-osc添加索引Lock wait timeout exceeded管窥
    1.pt-osc工具1.1.pt-osc简介pt-osc是pt-online-schema-change的简写,pt-online-schema-change是percona-toolkit工具包中用于在线变更DDL的工具1.2.pt-osc原理1.3.pt-toolkit安装#yuminstallperl-ExtUtils-CBuilderperl-ExtUtils-MakeMakercpan#yumload-transaction......
  • gyp GET https://nodejs.org/download/release/v20.15.0/node-v20.15.0-headers.tar.g
    如图我执行yarn关于node会报错:gyphttpGEThttps://nodejs.org/download/release/v20.15.0/node-v20.15.0-headers.tar.gzgyphttpfetchGEThttps://nodejs.org/download/release/v20.15.0/node-v20.15.0-headers.tar.gzattempt1failedwithETIMEDOUTgypWARNins......
  • TimeWheel算法介绍及在应用上的探索
    作者:来自vivo互联网服务器团队-LiFan本文从追溯时间轮算法的出现,介绍了时间轮算法未出现前,基于队列的定时任务实现,以及基于队列的定时任务实现所存在的缺陷。接着我们介绍了时间轮算法的算法思想及其数据结构,详细阐述了三种时间轮模型的数据结构和优劣性。再次,我们介绍时......
  • E - Permute K times
    原题链接启发式思考替换的过程,可以看成数组\(A\)内部的流动,既然是流动,我们可以用图来表示这种流动经过样例测试发现,这样的图,每个节点最多有一个入度,但可以有多个出度,很像树,但是又存在环感知一下,每一次替换,都是父节点的值赋给子节点,因此,k次替换后,该节点的值就是第\(k\)个祖......
  • STM32F4 timer定时器触发ADC采集,DMA转运数据 (标准库)
    硬件平台:STM32F401RCT6项目需求:需要实现100hzADC采集用于FFT频谱分析,同时要支持切换采集通道,每次采集之前改变数据存储地址与buff长度直接说配置过程的重点在DMA和ADC初始化之后,要处于disable状态,每次采集之前enable。如果一开始处于enable状态,没有采集,执行了disable再enabl......
  • C# yield keyword relieve congest and consume at the same time with produce
    usingSystem.Threading;namespaceConsoleApp57{internalclassProgram{staticvoidMain(string[]args){PrintNumers();Console.WriteLine("Hello,World!");}staticvoidPrintN......