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. 使用 Alba 对 AspnetCore项目进行测试

    前言 在AspnetCore生态系统中,我们测试项目一般使用Microsoft.AspNetCore.TestHost的TestServer 到.NET6后提供的Microsoft.AspNetCor ...

  2. 数字人 —— 虚拟人 —— Inworld AI用生成式AI——生成式游戏NPC

    相关: https://www.ithome.com/0/756/603.htm https://baijiahao.baidu.com/s?id=1774732295233220838 https: ...

  3. AI4Science 再填新成员:谷歌推出天气模型MetNet-3 已落地相关产品、谷歌天气预报模型GraphCast登刊Science —— AI天气预报大模型

    相关: https://zhidx.com/news/40169.html https://zhidx.com/news/40290.html PS. 要知道,华为公司的最高学术成果就是AI天气预报, ...

  4. ( Ubuntu环境 ) Vim下一键运行python代码

    使用一个统一的顺手的编辑&编译环境对于写代码是一件事半功倍的事情, 说白了就是有个顺手的写代码的工具很重要,这里要说的工具就是Vim. 假设   Ubuntu 系统中已经安装   Vim  : ...

  5. cloudpickle —— Python分布式序列化的专用模块

    给出cloudpickle的GitHub地址: https://github.com/cloudpipe/cloudpickle =================================== ...

  6. RabbitMq高级特性之延迟队列 通俗易懂 超详细 【内含案例】

    RabbitMq高级特性之延迟队列 介绍 消息进入队列后不能立即被消费,到达指定时间后才可被消费 实现 结合以下两种即可达到延迟队列 RabbitMq高级特性之TTL过期时间 RabbitMq高级特性 ...

  7. SMU Summer 2024 Contest Round 7

    SMU Summer 2024 Contest Round 7 Make Equal With Mod 题意 给定一个长度为 \(n\) 的数列 \(a\).你可以执行若干次操作,每次操作选择一个大于 ...

  8. apisix-dashboard上添加自定义插件

    参考:https://overstarry.vip/posts/apisix如何添加自定义插件/ 首先,我们需要向自定义的插件user-remote-auth添加到apisix中,对这块不清楚的同学, ...

  9. 国产崛起,Solon:我们的性能是 Spring 的 300%

    Solon 应用开发框架(java framework).是从零开始构建,有自主的标准规范与开放生态.纯血国产. 追求: 更快.更小.更简单 提倡: 克制.简洁.高效.开放.生态 相对于 Spring ...

  10. 我是如何使用 vue2+element-ui 处理负责表单,避免单文件过大的问题

    引言 在工作中我经常需要处理一些复杂.动态表单,但是随着需求不断迭代,我们也许会发现曾经两三百行的.vue文件现在不知不觉到了两千行,三千行,甚至更多... 这对于一个需要长期维护的项目,无疑是增加了 ...