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. 【Phoenix】4.14.1-Hbase-1.3版本 安装

    Phoenix 版本 会附带Hbase版本,要根据安装的Hbase版本下载Phoenix 下载Phoenix wget http://archive.apache.org/dist/phoenix/a ...

  2. 【Windows】关闭 Ctrl+Alt+Delete 锁屏

    参考百度经验: https://jingyan.baidu.com/article/9158e0005787c3a2541228b3.html Win + R 运行 gpedit.msc

  3. 【Vue】07 Webpack Part3 Loader

    Loader是Webpack的核心概念: 除了JS文件以外我们还有CSS,图片,包括一些ES6规范的代码 或者是TypeScript各种前端类型的文件 但是最终必须统一转换成JS文件,Webpack本 ...

  4. GPG公钥的删除与注销

    参考: 如何在 Gitee 上使用 GPG 我们通过在本地主机保存GPG私钥,然后在Gitee或Github上保存GPG公钥的方式来实现对git的commit和tag操作的签名. 当GPG公私秘钥对作 ...

  5. ubuntu 服务器显示 计算显卡的剩余 显存空间 并执行计划任务

    在Ubuntu服务器上需要运行gpu计算任务,由于需要无人值守的在显存空间满足条件的时候运行程序,于是有了自动显示显存的程序及自动运行任务的程序: 自动显示显存: import os import r ...

  6. WarpDrive 教程 第一部分修改版

    本文参考: https://www.cnblogs.com/devilmaycry812839668/p/15327509.html warpDrive是一个python库,目的是使用GPU并行运行多 ...

  7. Jupyter 实验室中的 GPU 仪表板

    这两天收到了NVIDIA公司推送的新闻: https://developer.nvidia.com/zh-cn/blog/gpu-dashboards-in-jupyter-lab/?ncid=em- ...

  8. Navicat 导入sql文件执行失败问题的处理

    之前有很多次用Navicat执行导出的sql文件,跑着跑着就批量报错,有的报错能看懂,有的报错压根就找不到,很长的记录中翻也不好翻. 现在用命令行执行的话,还是挺好使. 1进入mysql安装路径下的b ...

  9. VSCode 插件离线安装方法

    一.引言 最近想要使用 VSCode 来进行项目的开发工作,无奈工作机上无法上网.这就涉及到了相关插件的离线安装的问题. 在参考了 https://blog.csdn.net/wangwei703/a ...

  10. Ynoi2016镜中的昆虫

    [Ynoi 2016] 镜中的昆虫 简化题意 给定长为 \(n\) 序列 \(a\) , 两种操作 \(m\) 次: 1 l r x : 将 \([l , r]\) 修改为 \(x\) 2 l r : ...