本文简述实现,具体步骤原理可查看闫大神高质量实时渲染。

一、PCSS简介

1.1 PCF

百分比渐近过滤(PCF)是一个简单,常见的进行阴影边缘反走的技术。它通过在片段周围进行采样,然后计算样本比片段的更接近光源的比例,使用这个比例对散射光和镜面光成分进行缩放,然后对片段进行着色。使用这一技术后,阴影边缘看上去进行了模糊一样。

但对于较大物体的内部的采样是一个较大的浪费,我们并无法只针对物体边界进行采样,所以在性能是具有很大的瓶颈。

1.2 PCSS

pcss是距离相关的, 它通过搜索阴影贴图上的附近区域来尝试找到所有可能的遮挡物。这些遮挡物与该位置的平均距离用于确定样本区域宽度。随着平均遮挡物越来越远离接收物并且更靠近光线,样本的表面区域的宽度增大。

如果找不到遮挡物,则该位置完全点亮,无需进一步处理。类似地,如果位置完全被遮挡,则处理可以结束。否则,继续对感兴趣的区域进行采样并计算光的近似贡献。为了节省处理成本,样本区域的宽度可用于改变采样的数量。 可以实现其他技术,例如,使用较低的采样率来获得不太重要的远距离软阴影。

具体实现步骤:

  1. 遮挡物平均深度计算;
  2. 半影区域计算;
  3. 适用性PCF采样;

其中需要注意的是:

  • 半影区域计算如下图三角形比例关系:

  • 遮挡物平均深度计算时的区域大小计算如下图(也可简化使用固定区域大小):

二、代码实现

本部分仅讲述Shader实现,C+代码具体参照之前阴影相关文章。

2.1 shadow map pass

简单取shadow map:

顶点着色器:

#version 330 corein vec3 position;uniform mat4 modelViewProjection;void main()
{gl_Position = modelViewProjection * vec4(position, 1);
}

片元着色器:

#version 330 coreout float outDepth;void main()
{outDepth = gl_FragCoord.z;
}

2.2 pcss pass

使用shadow map求PCSS

顶点着色器:

#version 330 corein vec3 position;
in vec3 normal;
in vec2 texcoords;out vec2 vTexcoords;
out vec3 vNormal;
out vec3 vViewDir;
out vec3 vWorldPosition;
out vec3 vCameraPosition;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 eyePosition;void main()
{vTexcoords = texcoords;vNormal = (model * vec4(normal, 0)).xyz;vec4 worldPosition = model * vec4(position, 1.0f);vWorldPosition = worldPosition.xyz;vec4 cameraPosition = view * model * vec4(position, 1.0f);vCameraPosition = cameraPosition.xyz;vViewDir = normalize(eyePosition - vWorldPosition);gl_Position = projection * cameraPosition;
}

片元着色器中实现步骤基本与上述PCSS实现步骤一致,详见注释。

片元着色器:

#version 330 core#define NEAR 0.1#define HARD_SHADOWS 0
#define SOFT_SHADOWS 1in vec2 vTexcoords;
in vec3 vNormal;
in vec3 vViewDir;
in vec3 vWorldPosition;
in vec3 vCameraPosition;struct LightSource
{vec3 diffuseColor;float diffusePower;vec3 specularColor;float specularPower;vec3 position;int type;float size;};layout (std140) uniform LightSources
{LightSource lightSources[2];
};uniform sampler2D shadowMap0;
uniform samplerCube shadowCubeMap0;
uniform mat4 shadowMapViewProjection0;uniform mat4 invView;
uniform mat4 lightProjection;
uniform vec3 eyePosition;
uniform vec3 ambientColor = vec3(0.8,0.8,0.8);
uniform vec3 specularColor = vec3(1,1,1);
uniform float specularity = 0;
uniform float frustumSize = 1;
uniform sampler2D tex0;
uniform sampler1D distribution0;
uniform sampler1D distribution1;
uniform int numBlockerSearchSamples = 16;
uniform int numPCFSamples = 16;
uniform int displayMode = 0;
uniform int selectedLightSource = -1;out vec3 outColor;//随机采样数据
vec2 RandomDirection(sampler1D distribution, float u)
{return texture(distribution, u).xy * 2 - vec2(1);
}//常规光照处理
vec3 LightContribution(vec3 diffuseColor)
{float NdotL=max(0, dot(vNormal, normalize(vViewDir - lightSources[0].position)));return diffuseColor * lightSources[0].diffuseColor * lightSources[0].diffusePower * NdotL  + pow(NdotL, specularity) * specularColor * lightSources[0].specularColor * lightSources[0].specularPower;
}//坐标点转相机空间
vec3 ShadowCoords(mat4 shadowMapViewProjection)
{vec4 projectedCoords = shadowMapViewProjection * vec4(vWorldPosition, 1);vec3 shadowCoords = projectedCoords.xyz / projectedCoords.w;shadowCoords = shadowCoords * 0.5 + 0.5;return shadowCoords;
}//遮挡范围计算
float SearchWidth(float uvLightSize, float receiverDistance)
{return uvLightSize * (receiverDistance - NEAR) / eyePosition.z;
}//遮挡物平均深度查询
float FindBlockerDistance_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvLightSize)
{int blockers = 0;float avgBlockerDistance = 0;float searchWidth = SearchWidth(uvLightSize, shadowCoords.z);for (int i = 0; i < numBlockerSearchSamples; i++){float z = texture(shadowMap, shadowCoords.xy + RandomDirection(distribution0, i / float(numBlockerSearchSamples)) * searchWidth).r;if (z < (shadowCoords.z - 0.002f)){blockers++;avgBlockerDistance += z;}}if (blockers > 0)return avgBlockerDistance / blockers;elsereturn -1;
}//PCF
float PCF_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvRadius)
{float sum = 0;for (int i = 0; i < numPCFSamples; i++){float z = texture(shadowMap, shadowCoords.xy + RandomDirection(distribution0, i / float(numPCFSamples)) * uvRadius).r;sum += (z < (shadowCoords.z - 0.002f)) ? 1 : 0;}return sum / numPCFSamples;
}//ShadowMap
float ShadowMapping_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvLightSize)
{float z = texture(shadowMap, shadowCoords.xy).x;return (z < (shadowCoords.z - 0.002f)) ? 0 : 1;
}//软阴影计算
float PCSS_DirectionalLight(vec3 shadowCoords, sampler2D shadowMap, float uvLightSize)
{// 遮挡物平均深度计算float blockerDistance = FindBlockerDistance_DirectionalLight(shadowCoords, shadowMap, uvLightSize);if (blockerDistance == -1)return 1;      // 半影区域计算float penumbraWidth = ((shadowCoords.z - blockerDistance) / blockerDistance) * uvLightSize;// PCFfloat uvRadius = penumbraWidth * NEAR / shadowCoords.z;return 1 - PCF_DirectionalLight(shadowCoords, shadowMap, uvRadius);
}void main()
{vec3 diffuseColor = texture(tex0, vTexcoords).rgb;switch (displayMode){case HARD_SHADOWS: //硬阴影outColor = LightContribution(diffuseColor) * ShadowMapping_DirectionalLight(ShadowCoords(shadowMapViewProjection0), shadowMap0, lightSources[0].size / frustumSize);break;case SOFT_SHADOWS:  //软阴影outColor = LightContribution(diffuseColor) * PCSS_DirectionalLight(ShadowCoords(shadowMapViewProjection0), shadowMap0, lightSources[0].size / frustumSize);break;default:outColor = vec3(0.3,0.3,0.3);}outColor += ambientColor*diffuseColor;
}

运行可见如下效果(32采样范围,面光源默认大小0.5):

对比原有shadow map效果:

再放一张低PCF效果(4采样范围,面光源默认大小0.5):

大家可以从帧率上明显看出PCFF的耗时,后续可有时间的话继续使用VSSM方法来优化PCSS第一步blocker search 和第三部pcf采样的耗时。

Vulkan_PCSS软阴影相关推荐

  1. unity3d软阴影和硬阴影的原理_使用随机采样创建软阴影

    本文是OpenGL 4.0 Shading Language Cookbook的学习笔记. Shadowmap的基础实现结合PCF可以产生软阴影.但是,如果想要更大宽度的软阴影,这种方法需要增加大量采 ...

  2. 一种软阴影的实现方法

    转载自:http://hi.baidu.com/laizhishen/blog/item/b4c219dee23df1e177c63851.html 软阴影 www.GameDev.net 作者:An ...

  3. 【GAMES-202实时渲染】1、软阴影01(Shadow Mapping、Peter Panning、PCSS原理超详细)

    Lecture3 Real-Time shadows1 1 Shadow Mapping回顾 2 Shadow Mapping缺点及解决方案 2.1 自遮挡现象 解决方案1 定义一个bias 解决方案 ...

  4. 软阴影(PCF、PCSS)

    软阴影与硬阴影的区别如下:上面为硬阴影,下面为软阴影. Percentage Closer Filtering (PCF) PCF最初是用来做抗锯齿的,后来发现可运用于软阴影上(PCSS) 从相机出发 ...

  5. ShaderJoy —— 最简单的软阴影(SoftShadow)实现【GLSL】

    效果图 关键思路解析 要实现阴影最核心的思想:就是要从场景中各个物体上的点 朝着光线的方向进行 Raymarching,如果经过 SDF 测试以后,发现有阻挡,则我们可知该像素是位于阴影区域的: 软阴 ...

  6. GAMES202 PCSS软阴影算法细节解析

    在LearnOpenGL框架的基础上实现了一遍GAMES202的PCF+PCSS软阴影,之前学习GAMES202时一些没弄清楚的问题顺便搞清楚了. 注:本文中代码和shader均在笔者自学LearnO ...

  7. unity3d软阴影和硬阴影的原理_在广告摄影中阴影和高光的重要作用和控制技巧...

    投影和高光在大多数广告摄影画面中是不可避免的(除非像无投影布光或被摄体为非光洁表面),但又往往是影像的重要的组成部分. 投影和高光的存在,既有助于造型和质感的表现,但处理不当时,又会破坏画面和使人感到 ...

  8. 333lu.vip.php,软阴影 - GameRes.com

    // Projecti= on matrix for the light<= /span>D3DXMatrixPerspectiveFovLH(&matPro= j, D3DXTo ...

  9. unity3d软阴影和硬阴影的原理_Unity3D中两种默认阴影的实现

    Unity3D中两种阴影的实现 传统的ShadowMap ShadowMap说起来十分简单,把摄像机和光源的位置重叠,那么场景中该光源的阴影区域就是那些摄像机看不到的地方,主要应用在前向渲染路径中. ...

最新文章

  1. fastdfs java client_fastdfs-client-java操作fastdfs5.0.4
  2. 如何成为一个卓越的程序员
  3. c++for循环求最大公约数_C/C++编程笔记:C语言 for 循环精讲!实例讲解带你吃透...
  4. 记那些年在asp.net mvc上挖过的坑
  5. 英伟达数据中心业务营收不断创下新高,已连续9个财季保持增长
  6. opengl渲染4k数据提高效率
  7. Intellij IDEA 2016 使用
  8. angular6之Http服务
  9. 用Java做一个及时翻译工具
  10. 关于rtx 2009 远程控制插件 下载的问题
  11. 苹果6怎样分屏_皮皮喵最新版下载安卓版更新 0.2.6
  12. 史上最全的Go语言模块(Module)管理详解(基于Go1.19)
  13. Protocol(一)[概述]
  14. 【arcgis中两种路网密度计算结果对比】
  15. 360修复服务器漏洞,360安全漏洞不需要修复怎么设置
  16. 1946年第一台计算机在哪个国家面试,面试问题 计算机
  17. Linux系统学习方法
  18. 腾讯金融级数据库TDSQL的架构与应用
  19. java jmf播放mp3_关于jmf不能播放mp3的问题解决
  20. GB 9706.1-2020和GB9706.1-2007对照表

热门文章

  1. echarts世界地图东西半球调换位置 ?
  2. kindle的重生:看视频
  3. 后台运行python程序
  4. 机床上电子数显光栅尺的安装注意事项
  5. nios工程,在eclipse工程中报错recipe for target ‘test.elf‘ failed
  6. 16 ,spark sql : 开窗函数 ,top3
  7. 制定游戏机计算机配置清单,求吃鸡游戏电脑配置清单
  8. 鸿蒙系统吃鸡用转区吗,吃鸡:即将迎来转区功能,4种转区方式选择,玩家可自己决定!...
  9. 记一次unity3d 游戏帧率踩坑的过程
  10. python语言关键字在异常处理结构中_python二级考试试题6