PCF

Percentage Closer Filtering:PCF是一种用于阴影反锯齿的方法,本身不是软阴影方法。

算法流程说明:

  1. Perform multiple (e.g. 7x7) depth

    comparisons for each fragment
  2. Then, averages results of comparisons
  3. e.g. for point P on the floor,
  1. compare its depth with all pixels

    in the red box, e.g. 3x3
  2. get the compared results, e.g.

    1, 0, 1,

    1, 0, 1,

    1, 1, 0,
  3. take avg. to get visibility, e.g. 0.667

pcf使用的过滤size越大,得到的阴影结果越模糊,范围也被扩大。

code:

vec2 poissonDisk[NUM_SAMPLES];

void poissonDiskSamples( const in vec2 randomSeed ) {
float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES ); float angle = rand_2to1( randomSeed ) * PI2;
float radius = INV_NUM_SAMPLES;
float radiusStep = radius; for( int i = 0; i < NUM_SAMPLES; i ++ ) {
poissonDisk[i] = vec2( cos( angle ), sin( angle ) ) * pow( radius, 0.75 );
radius += radiusStep;
angle += ANGLE_STEP;
}
} float PCF(sampler2D shadowMap, vec4 coords,float filterSize) {
if(coords.x <-1.0 || coords.y>1.0 || coords.y<-1.0 || coords.y>1.0){
//不在灯光相机视野中,默认完全照亮
return 1.0;
}
float pixelSize = 1.0/2048.0;
float count = 0.0;
poissonDiskSamples(vec2(coords.x,coords.y));
for(int i=0;i<NUM_SAMPLES;i++){
vec2 uv = poissonDisk[i]*pixelSize*filterSize + coords.xy;
if(uv.x <-1.0 || uv.y>1.0 || uv.y<-1.0 || uv.y>1.0){
//不在灯光相机视野中,默认完全照亮
return 1.0;
}
float dNearest = sampleDepth(shadowMap, uv) * Z_RANGE;
float d = (coords.z+1.0)/2.0*Z_RANGE;//[-1,1]->[0,1.0]
if(!(d > dNearest+Z_EPS)){//pcf 返回可见性指标
count += 1.0;
}
}
float res = count/float(NUM_SAMPLES);
return res;
}

result:

PCF_8X8

PCF_64X64

PCSS

Percentage Closer Soft Shadows

通过观察可知阴影的软硬与阴影距离阴影投掷物的距离有关,距离越近,阴影越实(硬);距离越远,阴影越虚(软)。因此可以在PCF的基础上,动态调整filter size从而实现软阴影效果。

关键在于如何选择filter size,filter size和阴影距离阴影投掷物的距离(d_receiver-d_blocker)和遮挡物距离光源的距离(d_blocker)有关, 如下图所示:

其中W_penumbra就是面光源形成的软阴影范围,也就是pcf使用的filter size。

通过三角相似关系,可以得到下面的数量关系:

\[w_{Pemumbra} = (d_{Reciiver}- d_{Blocker}) * w_{Light}/d_{Blocker}
\]

其中W_light为光源宽度,在代码中可以取常量;d_receiver是物体深度,可以在shader中uLightMVP*aPosition计算得到;

d_blocker是遮挡物深度可以从pass1中记录的最近物体深度读取,读取一定范围内的遮挡物的平均深度,这个范围需要取多大?

1.Can be set constant (e.g. 5x5), but can be better with heuristics

2.理论上的blocker search region:

依赖于光源面积大小和观察点距离光源的距离,即图中红虚线构成四棱锥范围。有一个问题是从面光源的不同点看场景会有不同的shadw map,如果只用一个shadw map进行采样,本身这里是精确分析,最后的结果又变成了近似。

最后得到PCSS的算法流程:

The complete algorithm of PCSS

  • Step 1: Blocker search

    (getting the average blocker depth in a certain region)
  • Step 2: Penumbra estimation

    (use the average blocker depth to determine filter size)
  • Step 3: Percentage Closer Filtering

code:

vec2 findBlocker( sampler2D shadowMap,  vec2 uv, float zReceiver ) {
if(uv.x <-1.0 || uv.y>1.0 || uv.y<-1.0 || uv.y>1.0){
//不在灯光相机视野中,默认没有遮挡物
return vec2(0.0,0.0);
}
//uniformDiskSamples(uv); float pixelSize = 1.0/2048.0;
float db = 0.0;
float ds = 0.0;
//启发式的得到搜索范围,单位:像素
float searchSize = 0.0;
searchSize = LIGHT_WIDTH*(zReceiver-NEAR_PLANE)/zReceiver;
searchSize = searchSize/200.0/pixelSize;
searchSize *= 1.5;
//固定
//searchSize = 64.0;// 64.0; float count = 0.0;
poissonDiskSamples(vec2(uv.x*9999.0+8888.0,uv.y*7777.0+12345.0));
for(int i = 0; i < NUM_SAMPLES; i++){
vec2 uv2 = poissonDisk[i]*pixelSize*searchSize + uv;
if(uv2.x <-1.0 || uv2.y>1.0 || uv2.y<-1.0 || uv2.y>1.0){
//不在灯光相机视野中,默认没有遮挡物
return vec2(0.0,0.0);
}
float d = sampleDepth(shadowMap, uv2);
d = d*Z_RANGE;
if(d < zReceiver-5.0 ){
db += d;
count += 1.0;
}
}
db = db / count;
ds = zReceiver - db;
//db = db / float(NUM_SAMPLES);
//ds = ds / float(NUM_SAMPLES);
return vec2(db,ds);
} float PCF(sampler2D shadowMap, vec4 coords,float filterSize) {
if(coords.x <-1.0 || coords.y>1.0 || coords.y<-1.0 || coords.y>1.0){
//不在灯光相机视野中,默认完全照亮
return 1.0;
}
float pixelSize = 1.0/2048.0;
float count = 0.0;
poissonDiskSamples(vec2(coords.x,coords.y));
for(int i=0;i<NUM_SAMPLES;i++){
vec2 uv = poissonDisk[i]*pixelSize*filterSize + coords.xy;
if(uv.x <-1.0 || uv.y>1.0 || uv.y<-1.0 || uv.y>1.0){
//不在灯光相机视野中,默认完全照亮
return 1.0;
}
float dNearest = sampleDepth(shadowMap, uv) * Z_RANGE;
float d = (coords.z+1.0)/2.0*Z_RANGE;//[-1,1]->[0,1.0]
if(!(d > dNearest+10.0)){//pcf 返回可见性指标
count += 1.0;
}
}
float res = count/float(NUM_SAMPLES);
return res;
} float PCSS(sampler2D shadowMap, vec4 coords){
float d_r = (coords.z+1.0)/2.0*Z_RANGE;// d_receiver
float pixelSize = 1.0/2048.0;
float d_b = 0.0;//d_blocker
float d_s = 0.0;//average d_reciver - d_blocker
float filterSize = 16.0;
// STEP 1: avgblocker depth
vec2 vB = findBlocker(shadowMap,coords.xy,d_r);
d_b = vB.x;
d_s = vB.y;
//return d_b/Z_RANGE;
//return d_s/Z_RANGE;
if(d_s<=EPS){
//不受遮挡,可见性为1
return 1.0;
}
// STEP 2: penumbra size
//float wp = pow(d_s / d_b,2.0) * LIGHT_WIDTH; // d_s / d_b* LIGHT_WIDTH;//
float wp = d_s / d_b* LIGHT_WIDTH;
//float wp = (dr/db-1.0) * L_WIDTH;
float penumbraPixelSize = wp/200.0/pixelSize;//200是正交相机里设定的窗口范围
//return penumbraPixelSize/100.0;
// STEP 3: filtering
//filterSize = penumbraPixelSize+8.0;
filterSize = penumbraPixelSize;
float res = PCF(shadowMap, coords, filterSize);
return res;
}

result:

PCF 64X64

PCSS

pcss 软阴影的更多相关文章

  1. DirectX11 With Windows SDK--38 级联阴影映射(CSM)

    前言 在31章我们曾经实现过阴影映射,但是受到阴影贴图精度的限制,只能在场景中相当有限的范围内投射阴影.本章我们将以微软提供的例子和博客作为切入点,学习如何解决阴影中出现的Atrifacts: 边缘闪 ...

  2. DirectX11 With Windows SDK--39 阴影技术(VSM、ESM)

    前言 上一章我们介绍了级联阴影贴图.刚开始的时候我尝试了给CSM直接加上PCSS,但不管怎么调难以达到说得过去的效果.然后文章越翻越觉得阴影就是一个巨大的坑,考虑到时间关系,本章只实现了方差阴影贴图( ...

  3. WebGL光照阴影映射

      原文地址:WebGL光照阴影映射   经过前面的学习,webgl的基本功能都已经掌握了,我们不仅掌握了着色器的编写,图形的绘制,矩阵的变换,添加光照,还通过对webgl的基础api封装,编写出了便 ...

  4. 转:体积阴影(Shadow Volumes)生成算法

    下面以最快的速度简单谈谈阴影生成技术,目前普遍采用的一般有三种:Planar Shadow.Shadow Mapping和Shadow Volume,前者类似投影,计算最简单,缺点只能绘制抛射在平面上 ...

  5. Unity QualitySettings.shadows 阴影

    QualitySettings.shadows 阴影 public static ShadowQuality shadows; Description 描述: 要使用的实时阴影类型. 这就决定了应该使 ...

  6. Unity3D中使用Projector生成阴影

    在Unity3D中使用Projector实现动态阴影 无意中看见一篇博客叙述使用Projector实现动态阴影可以在移动平台拥有非常好的性能,遂按照其想法实现了一遍,发现其中竟有许多细节,写下这篇博客 ...

  7. Shadow Map阴影贴图技术之探 【转】

    这两天勉勉强强把一个shadowmap的demo做出来了.参考资料多,苦头可不少.Shadow Map技术是目前与Shadow Volume技术并行的传统阴影渲染技术,而且在游戏领域可谓占很大优势.本 ...

  8. Unite 2018 | 《崩坏3》:在Unity中实现高品质的卡通渲染(上)

    http://forum.china.unity3d.com/thread-32271-1-1.html 我们已经发布了Unite 2018 江毅冰的<发条乐师>.Hit-Point的&l ...

  9. 基于Unity 5的次世代卡通渲染技术 -- Unite 2017 米哈游总监贺甲分享实录

    在5月12日Unite2017开发者大会上,米哈游技术总监兼美术指导贺甲进行了主题为次世代卡通渲染的演讲.一下为详细分享内容: 大家好,首先自我介绍一下,我叫贺甲,在米哈游担任技术总监和美术指导工作, ...

  10. 3D游戏中的画质与效率适配

      哪里来的需求? 众所周知,由于不同的设备配置不同.导致其CPU和GPU处理能力有高有低.同样的游戏想要在所有设备上运行流畅且画面精美,是不可能的.这就需要我们针对不同的设备能力进行画质调节,以保证 ...

随机推荐

  1. JAVA并发编程理论基础

    注:本文章是对极客时间<java并发编程实战>学习归纳总结,更多知识点可到原文 java并发编程实战 进行学习.如果侵权,联系删除: 一.并发编程的BUG的源头 1.1 缓存导致的可见性问 ...

  2. Known框架实战演练——进销存财务管理

    本文介绍如何实现进销存管理系统的财务对账模块,财务对账模块包括供应商对账和客户对账2个菜单页面.供应商和客户对账字段相同,因此可共用一个页面组件类. 项目代码:JxcLite 开源地址: https: ...

  3. Jmeter函数助手27-urlencode

    urlencode函数用于将字符串进行application/x-www-form-urlencoded编码格式化. String to encode in URL encoded chars:填入字 ...

  4. SemanticKernel/C#:检索增强生成(RAG)简易实践

    检索增强生成(RAG)是什么? RAG是"Reference-based Generative model with Attention"的缩写,也可以被称为"Retri ...

  5. 【Mycat】01 概述

    什么是Mycat? 数据库中间件 中间件:是一类连接软件组件和应用的计算机软件,以便于软件各部件之间的沟通. 例子:Tomcat,web中间件. 数据库中间件:连接java应用程序和数据库 为什么要用 ...

  6. xshell终端——多个窗格同步输入——xshell同时控制多个窗口的快捷方式

    参考: https://blog.csdn.net/m0_58347801/article/details/129551382 ======================== 突发发现了终端的另类用 ...

  7. 在计算框架MindSpore中手动保存参数变量(Parameter 变量)—— from mindspore.train.serialization import save_checkpoint

    本文参考内容: https://www.mindspore.cn/doc/programming_guide/zh-CN/r1.2/advanced_usage_of_checkpoint.html? ...

  8. mojo编程语言:mojo调用python库及内置函数builtins

    编程语言mojo调用python十分方便,mojo不仅可以调用python的库函数更可以调用python的内置函数(builtins),给出示例代码: from python import Pytho ...

  9. Apache SeaTunnel社区首位学生Committer诞生!

    采访对象 | 陈炳烨 采访人&编辑 | Debra Chen Apache SeaTunnel社区第一位学生Committer就此诞生!这位来自西安交通大学软件工程专业的同学从较为简单的文档修 ...

  10. Apache SeaTunnel 4月回顾:明星贡献者与技术突破

    各位热爱 SeaTunnel 的小伙伴们,SeaTunnel 社区 4 月份月报来啦!这里将记录 SeaTunnel 社区每月的重要更新,欢迎关注! 月度 Merge 之星 感谢以下小伙伴 4 月为 ...