文章目录

  • 作业1框架下实现PCF
  • 作业1框架下实现PCSS
  • 在QT下使用PCF
    • 代码
    • 结果
  • 在QT下使用PCSS
    • 代码
    • 结果
  • 采用低通采样
    • PCF使用低通采样
      • 代码
      • 结果
    • PCSS
    • 代码
    • 结果
      • 采样数20
      • 采样数100
  • frag完整代码
    • 问题

作业1框架下实现PCF

200采样数(特别卡)

20采样数(有噪点)

50采样数

作业1框架下实现PCSS

可以看出在脚附近的阴影为硬阴影,而头部附近的阴影为软阴影。
20x20 采样数100(卡)

20x20 采样数20(还好)

在QT下使用PCF

代码

计算光照颜色

  // 执行透视除法vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;// 变换到[0,1]的范围projCoords = projCoords * 0.5 + 0.5;// 计算阴影float shadow = PCSS(projCoords);//float shadow = PCF(projCoords,5);vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular));return lighting;

采用PCF算法,r为抗锯齿矩形半径。

float PCF(vec3 projCoords,int r)
{// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)float closestDepth = texture(shadowMap, projCoords.xy).r;// 取得当前片段在光源视角下的深度float currentDepth = projCoords.z;// 检查当前片段是否在阴影中float bias = max(0.05 * (1.0 - dot(Normal, -dirLight.direction)), 0.005);//PCFfloat shadow = 0.0;vec2 texelSize = 1.0 / textureSize(shadowMap, 0);//每像素偏移距离for(int x = -r; x <= r; ++x){for(int y = -r; y <= r; ++y){float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r;shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;}}shadow /= (2*r+1)*(2*r+1);//远平面矫正if(projCoords.z > 1.0)shadow = 0.0;return shadow;
}

结果

在QT下使用PCSS

代码

float PCSS(vec3 projCoords){const float weightOfLight = 10.0;// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)float closestDepth = texture(shadowMap, projCoords.xy).r;// 取得当前片段在光源视角下的深度float currentDepth = projCoords.z;// 检查当前片段是否在阴影中float bias = max(0.05 * (1.0 - dot(Normal, -dirLight.direction)), 0.005);//每像素偏移距离vec2 texelSize = 1.0 / textureSize(shadowMap, 0);//PCSS核心算法float visibility = 0.0;//第一步计算平均遮挡物深度float averBlocker = averageBlockDep(projCoords,texelSize);//第二步,计算半影半径float penumbra = (projCoords.z - averBlocker) * weightOfLight / averBlocker;//第三步 PCFvisibility = PCF(projCoords,int(penumbra));return visibility;
}

结果

由于我们的采样方式使用了矩形区域采样,因此当阴影重叠较大时,会出现如下情况。

采用低通采样

PCF使用低通采样

代码

//全局参数
vec2 poissonDisk[NUM_SAMPLES];//传入一个二维数,传出一个假随机数。
highp float rand_2to1(vec2 uv ) {// 0 - 1const highp float a = 12.9898, b = 78.233, c = 43758.5453;highp float dt = dot( uv.xy, vec2( a,b ) );highp float sn = mod( dt, PI );return fract(sin(sn) * c);//只取小数部分(取值范围0~1,若为负+1)
}//获取泊松采样数组,生成(a,b)|(a^2+b^2<1) 的二维数组
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;//弧度增加}
}

该泊松采样方式如下,初始弧度为随机数,⚪半径为1.
NUM_RINGS 为采样圈数

float PCF(vec3 projCoords,int r)
{// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)float closestDepth = texture(shadowMap, projCoords.xy).r;// 取得当前片段在光源视角下的深度float currentDepth = projCoords.z;// 检查当前片段是否在阴影中float bias = max(0.05 * (1.0 - dot(Normal, -dirLight.direction)), 0.005);//PCFpoissonDiskSamples(projCoords.xy);//获取泊松采样数组float shadow = 0.0;vec2 texelSize = 1.0 / textureSize(shadowMap, 0);//每像素偏移距离for(int i=0;i<NUM_SAMPLES;i++){float pcfDepth = texture(shadowMap, projCoords.xy + r * poissonDisk[i] * texelSize).r;shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;}//远平面矫正if(projCoords.z > 1.0)shadow = 0.0;return shadow;
}

结果

采样数 20
采样圈数 10

  • 当滤波步长(r)为1时

  • 当滤波步长为5时

  • 当滤波为20时,当光线处于一定角度,地面出现黑色线条

调整光线角度后,黑色线条消失。(应该是在计算是误将地板上阴影深度大于计算点阴影深度的点算入,造成自阴影,解决办法是加大bias,或减小滤波大小)。

PCSS

在PCSS中计算中,第一步计算平均遮挡物深度也需要用到低通采样。

代码

float averageBlockDep(vec3 projCoords,vec2 texelSize){float blockerZ = 0.0;//遮挡物总深度int count = 0;int r=5;//确定半径为5//在一定范围内判断是否有遮挡物poissonDiskSamples(projCoords.xy+vec2(0.1314,0.351));for(int i=0;i<NUM_SAMPLES;++i){float depth = texture(shadowMap, projCoords.xy + r * poissonDisk[i] * texelSize).r;if(depth < projCoords.z){//如果为遮挡物count++;blockerZ +=depth;}}if(count == 0||count==(r*2+1)*(r*2+1))return 1.0f;return blockerZ / count;
}

结果

采样数20

#define NUM_SAMPLES 20


采样数100

#define NUM_SAMPLES 100

噪点减少,但帧数卡顿


与矩形均匀采样相比

  • 低通采样优点:低通采样可变性更强,采样阴影结果更圆滑(更真实)。
  • 低通采样缺点:采样数较低时容易遭受噪点,较高时对于小滤波(脚底阴影)造成性能浪费,且影响性能程度较大。

但噪点在当前并不是一个很大的问题,因为在之后会接触时序降噪,空间降噪等,早申不会是很大的问题。(即使当前看起来使用低通滤波效果较差)。

frag完整代码

该shader只计算了平行光的阴影,对于点光源并未处理阴影。

#version 450 corestruct Material {vec3 color;float shiness;
};struct DirLight {bool Activated;vec3 direction;vec3 ambient;vec3 diffuse;vec3 specular;
};struct PointLight {vec3 position;vec3 lightnormal;vec3 ambient;vec3 diffuse;vec3 specular;float constant;float linear;float quadratic;
};//顶点信息
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;in vec4 FragPosLightSpace;
uniform sampler2D shadowMap;
//输出
out vec4 FragColor;//视点
uniform vec3 viewPos;
//平行光
uniform DirLight dirLight;//点光源
uniform PointLight pointLights[16];
uniform int numPointLights;uniform Material material;#define PI 3.141592653589793
#define PI2 6.283185307179586//采样数
#define NUM_SAMPLES 100
//采样圈数
#define NUM_RINGS 10//函数申明
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light,vec3 normal, vec3 fragPos,vec3 viewDir);
float PCF(vec3 projCoords,int r);
float PCSS(vec3 projCoords);
float averageBlockDep(vec3 projCoords,vec2 texelSize);
void poissonDiskSamples(const in vec2 randomSeed);//全局参数
vec2 poissonDisk[NUM_SAMPLES];highp float rand_2to1(vec2 uv ) {//传入一个二维数,传出一个假随机数。// 0 - 1const highp float a = 12.9898, b = 78.233, c = 43758.5453;highp float dt = dot( uv.xy, vec2( a,b ) );highp float sn = mod( dt, PI );return fract(sin(sn) * c);//只取小数部分(取值范围0~1,若为负+1)
}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;//弧度增加}
}void main()
{// propertiesvec3 norm = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);//片元点指向视点vec3 result = vec3(0,0,0);// phase 1: parallel lightsif(dirLight.Activated){result += CalcDirLight(dirLight, norm, viewDir);}// phase 2: point lightsfor(int i = 0; i < numPointLights; i++){result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);}FragColor = vec4(result,1.0);
}float averageBlockDep(vec3 projCoords,vec2 texelSize){float blockerZ = 0.0;//遮挡物总深度int count = 0;int r=5;//在一定范围内判断是否有遮挡物poissonDiskSamples(projCoords.xy+vec2(0.1314,0.351));for(int i=0;i<NUM_SAMPLES;++i){float depth = texture(shadowMap, projCoords.xy + r * poissonDisk[i] * texelSize).r;if(depth < projCoords.z){//如果为遮挡物count++;blockerZ +=depth;}}if(count == 0||count==(r*2+1)*(r*2+1))return 1.0f;return blockerZ / count;
}float PCSS(vec3 projCoords){const float weightOfLight = 10.0;// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)float closestDepth = texture(shadowMap, projCoords.xy).r;// 取得当前片段在光源视角下的深度float currentDepth = projCoords.z;// 检查当前片段是否在阴影中float bias = max(0.05 * (1.0 - dot(Normal, -dirLight.direction)), 0.005);//每像素偏移距离vec2 texelSize = 1.0 / textureSize(shadowMap, 0);//PCSS核心算法float visibility = 0.0;//第一步计算平均遮挡物深度float averBlocker = averageBlockDep(projCoords,texelSize);//第二步,计算半影半径float penumbra = (projCoords.z - averBlocker) * weightOfLight / averBlocker;//第三步 PCFvisibility = PCF(projCoords,int(penumbra));return visibility;
}float PCF(vec3 projCoords,int r)
{// 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)float closestDepth = texture(shadowMap, projCoords.xy).r;// 取得当前片段在光源视角下的深度float currentDepth = projCoords.z;// 检查当前片段是否在阴影中float bias = max(0.05 * (1.0 - dot(Normal, -dirLight.direction)), 0.005);//PCFfloat shadow = 0.0;vec2 texelSize = 1.0 / textureSize(shadowMap, 0);//每像素偏移距离poissonDiskSamples(projCoords.xy);for(int i=0;i<NUM_SAMPLES;i++){float pcfDepth = texture(shadowMap, projCoords.xy + r * poissonDisk[i] * texelSize).r;shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;}shadow /= float(NUM_SAMPLES);//远平面矫正if(projCoords.z > 1.0)shadow = 0.0;return shadow;
}//计算平行光源
vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir){//平行光反方向vec3 lightDir = normalize(-light.direction);//计算cos衰减float diff = max(dot(lightDir,normal),0.0);//反射方向vec3 reflectDir = reflect(-lightDir,normal);//计算镜面反射系数float spec = pow(max(dot(viewDir,reflectDir),0.0),material.shiness);vec3 ambient = light.ambient * material.color;vec3 diffuse = light.diffuse * diff * material.color;vec3 specular = light.specular * spec * material.color;// 执行透视除法vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;// 变换到[0,1]的范围projCoords = projCoords * 0.5 + 0.5;// 计算阴影float shadow = PCSS(projCoords);//float shadow = PCF(projCoords,20);vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular));return lighting;
}//计算点光源
vec3 CalcPointLight(PointLight light,vec3 normal, vec3 fragPos,vec3 viewDir){//光源反方向vec3 lightDir = normalize(light.position - fragPos);float angleDecay = 1.0f;if(any(notEqual(light.lightnormal,vec3(0,0,0)))){angleDecay = max(dot(-lightDir,normalize(light.lightnormal)),0.0f);}float diff = max(dot(lightDir,normal),0.0);vec3 reflectDir = reflect(-lightDir,normal);float spec = pow(max(dot(reflectDir,viewDir),0.0),material.shiness);float distance = length(light.position - fragPos);float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));vec3 ambient = light.ambient * material.color;vec3 diffuse = light.diffuse * diff * material.color;vec3 specular = light.specular * spec * material.color;ambient *= attenuation;diffuse *= attenuation;specular *= attenuation;ambient *= angleDecay;diffuse *= angleDecay;specular *= angleDecay;return (ambient + diffuse + specular);
}

问题

Games202,作业1(QT下实现PCSS)相关推荐

  1. linux+Qt 下利用D-Bus进行进程间高效通信的三种方式

    linux+Qt 下利用D-Bus进行进程间高效通信的三种方式 原文链接: https://www.cnblogs.com/wwang/archive/2010/10/27/1862552.html ...

  2. Qt下一行代码就可以使用的稳定易用的日志log类

    Qt下一行代码就可以使用的稳定易用的日志类 此日志类是基于Qt 自带的 扩展的一个易用的日志类, 使用的是Qt自带的日志输出形式, 已长期运行在许多实际项目中,稳定可靠,而且跨平台, 在windows ...

  3. Qt下使用Shader绘制三角形

    在Qt下使用可编程管线编写OpenGL的流程是怎样的呢? 下面演示了Qt下使用可编程管线的基本代码:(绘制三个不同的三角形,并做些旋转变换) 在Qt中,我们从QGLWidget继承,来实现OpenGL ...

  4. Qt下的OpenGL 编程(3)绘制平面几何体

    一. 提要 之前的一篇教程已经搭建好了Qt下的OpenGL的编程环境,几天要来学习的就是OpenGL的2D绘图. 2D作为绘图的基础,还是很值得去好好学习,比如迪卡尔坐标,透视设置等等,而所谓的3D, ...

  5. Qt下使用OpenCV3打开摄像头并把图像显示到QLabel上

    前言 1.Qt5有自己摄像头的类QCamera,但是图像处理相关还是要使用OpenCV来做,这里我演示在Qt下使用OpenCV打开摄像头. 2.Qt的版本是5.9,Qt Creator 4.4.1,O ...

  6. Qt下Tcp传输文件

    Qt下Tcp传输文件 文章目录 Qt下Tcp传输文件 1.服务端 2.客户端 1.服务端 //ServerWidgets.h #ifndef SERVERWIDGET_H #define SERVER ...

  7. Qt下Undefined reference to 'vtable for xxx'

    QT下遇到这种错误提示时候需要注意以下情况: 一.cpp文件里使用了Q_OBJECT 分析:qmake不会处理.cpp文件里的Q_OBJECT,所以如果在.cpp文件中有它的话将会产生undefine ...

  8. Qt下继承于QObject创建的线程

    Qt线程 线程创建方法 示例 线程创建方法 Qt下创建线程的方法有两种: 一种是通过继承QThread,并重写run()函数,在run()函数中,编写线程所做的事情,在需要线程的文件中,创建线程对象, ...

  9. Qt下实现多线程串口通信

    Qt下实现多线程串口通信 Qt下无论是RS232.RS422.RS485的串口通信都可以使用统一的编码实现.本文把每路串口的通信各放在一个线程中,使用movetoThread的方式实现. 用Seria ...

最新文章

  1. Go 学习笔记(21)— 标准库 os 操作文件(新建、打开、写入、读取、删除、关闭文件)
  2. pandas使用pd.MultiIndex.from_product函数和pd.MultiIndex.from_tuples函数创建复合索引dataframe数据实战
  3. 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
  4. Matlab之if-else-end分枝语句
  5. spring计算方圆300km内其它城市(附完整代码)
  6. IOS 开发中 Whose view is not in the window hierarchy 错误的解决办法
  7. Oracle 11gR2 sessions=1.5*processes+22
  8. stm32采集脉冲信号_外部脉冲实现ADC采样的问题 STM32f103VE
  9. 搭建企业级Docker Registry -- Harbor
  10. 前端工程化:vue代码检查工具vetur
  11. 在VMware下安装中标麒麟操作系统7.0以及Neokylin基础常用知识
  12. Prometheus(6)Pormetheus+ Alertmanager配置邮件警告,并使用模板进行发送
  13. Ubuntu 下安装VirtualBox主要步骤及出现的问题的解决方案
  14. [爬虫]爬取猫眼电影票房信息(信息字体加密)
  15. 微信小程序收款手续费_为什么有人做的小程序交易手续费是0.38%而不是0.6%?
  16. 工作队列模式(任务队列)| RabbitMQ系列(二)
  17. uniapp开发-公众号h5、小程序、移动app
  18. K8S部署机器学习平台
  19. Linux服务器之内存过高解决思路
  20. vue——双层循环嵌套

热门文章

  1. 京东数据分析——电商平台比价api接口
  2. 电子电路学习笔记(3)——电感
  3. cocos2dx下载安装
  4. 佳能IR2520I远程扫描怎么用
  5. 基于stm32的FIR滤波
  6. leetcode703. 数据流中的第K大元素(PriorityQueue 最小堆)
  7. 判断Object类型的数据是否为空
  8. 做完心脏支架手术后一年内反反复复 胸闷失眠、心绞痛老是得不到改善
  9. 计算机组成原理课程设计总结,计算机组成原理课程设计的实验报告
  10. 常用计算机二级题库,计算机等级考试题库