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. 7、SpringMVC之RESTful概述

    创建名为spring_mvc_rest的新module,过程参考5.2节和6.6节 7.1.简介 RESTful 也称为REST(英文:Representational State Transfer) ...

  2. 【ActiveJdbc】03

    一.查询API 简单条件筛选: List<Person> list = Person.where("name = 'John'") 动态参数条件: List<Pe ...

  3. 【Docker】11 私有仓库

    Docker的私有仓库也是一个镜像形式: docker pull registry 运行容器: run -d --name my-docker-repo -p 5000:5000 registry 访 ...

  4. 国内的开源AI模型共享网站(AI模型的GitHub)—— modeldscope —— 对标外网的“huggingface”,modelscope好用吗?

    搞AI的应该都是知道huggingface是啥的,这里不过多介绍,简单的来说就是AI模型的Github,之所以这么说是因为计算机的项目往往都是代码文件,所有计算机项目的Github只需要上传项目的代码 ...

  5. 机器学习中的权重衰退 —— 深度学习中的权重衰退 —— 权重衰退 —— weight decay

    在看代码时看到了这个概念,以前虽然也看到过但是没有太在意,再次看到于是研究了一下. 引自: https://sota.jiqizhixin.com/models/methods/0bdb8f87-9c ...

  6. 从baselines库的common/vec_env/vec_normalize.py模块看方差的近似计算方法

    在baselines库的common/vec_env/vec_normalize.py中计算方差的调用方法为: RunningMeanStd 同时该计算函数的解释也一并给出了: https://en. ...

  7. 【转载】 python进程绑定CPU

    版权声明:本文为CSDN博主「人间再无张居正」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/u01388765 ...

  8. 国产最好用的操作系统——deepin,为什么不适合生产环境

    前几天看了罗永浩的视频,他推荐了一本Intel创始人的书,看了这本书后想到书中大谈Intel和Microsoft的合作才创建下了今天的PC世界.看到这我就有些不爽了,现在美国各种控制和制裁我们的芯片, ...

  9. 再升级!MoneyPrinterPlus集成GPT_SoVITS

    最近有很多优秀的语音合成TTS工具,目前MoneyPrinterPlus已经集成了ChatTTS和fasterWhisper.应朋友们的要求,最近MoneyPrinterPlus也集成了GPT_SoV ...

  10. SMU Autumn 2023 Round 5

    SMU Autumn 2023 Round 5 A. Everyone Loves to Sleep 把时间都转成分钟,然后存起来,二分找到离他睡觉点最近的一个时间段,减去他的睡觉点,如果最近的在第二 ...