文章目录

  • 作业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. WCF基础知识问与答
  2. composer安装laravel框架时未生成Vendor解决办法
  3. Redis进阶-JedisCluster初始化 自动管理连接池中的连接 _ 源码分析
  4. 深度理解Android InstantRun原理以及源码分析
  5. 北京地铁线路图纯算法附带求极权值(原创) 性能提升版
  6. 【渝粤教育】国家开放大学2019年春季 770房地产估价 参考试题
  7. 深入理解卷积层,全连接层的作用意义
  8. Implicit declaration of function 'NSFileTypeForHFSTypeCode' is invalid in C99
  9. spring-boot-2.0.3应用篇 - shiro集成
  10. c语言中被调用函数只需在主调函数中声明,其他函数中不用声明,求助,函数在其他函数中使用时要先声明后调用,这个没声明就用了...
  11. c# webclient 保存会话信息_winform项目——仿QQ即时通讯程序16:会话列表的存储
  12. annotation:@Override出现The method of type must override asuperclass解决方案
  13. ISA Server、虚拟机、托管服务器的使用
  14. python中valueerror怎么改_Python:ValueError:使用序列设置数组元素
  15. java实现中文语音朗读
  16. JVM——Java类加载机制总结
  17. FPGA篮球计分设计
  18. win10计算机管理 用户,Win10专业版系统管理员帐户的开启设置方法
  19. 论文笔记 EMNLP 2021|Modeling Document-Level Context for Event Detection via Important Context Selection
  20. JAVA后端面经总结——应用类

热门文章

  1. nginx-stream端口转发
  2. 米老师讲课 ITOO4.1 ——Java(2)
  3. Universal adversarial perturbations(翻译,侵删)
  4. mysql面试题115道
  5. 欢迎光临我的公众号和我的博客
  6. 拉线式位移编码器的电路及原理大家需要了解一下
  7. Xshell-7工具栏隐藏了
  8. Vue路由动态渲染和动态传参
  9. 深度学习的基本专业术语
  10. 检查错别字在线检测系统网站,免费错别字识别软件丨推荐