Games202,作业1(QT下实现PCSS)
文章目录
- 作业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)相关推荐
- linux+Qt 下利用D-Bus进行进程间高效通信的三种方式
linux+Qt 下利用D-Bus进行进程间高效通信的三种方式 原文链接: https://www.cnblogs.com/wwang/archive/2010/10/27/1862552.html ...
- Qt下一行代码就可以使用的稳定易用的日志log类
Qt下一行代码就可以使用的稳定易用的日志类 此日志类是基于Qt 自带的 扩展的一个易用的日志类, 使用的是Qt自带的日志输出形式, 已长期运行在许多实际项目中,稳定可靠,而且跨平台, 在windows ...
- Qt下使用Shader绘制三角形
在Qt下使用可编程管线编写OpenGL的流程是怎样的呢? 下面演示了Qt下使用可编程管线的基本代码:(绘制三个不同的三角形,并做些旋转变换) 在Qt中,我们从QGLWidget继承,来实现OpenGL ...
- Qt下的OpenGL 编程(3)绘制平面几何体
一. 提要 之前的一篇教程已经搭建好了Qt下的OpenGL的编程环境,几天要来学习的就是OpenGL的2D绘图. 2D作为绘图的基础,还是很值得去好好学习,比如迪卡尔坐标,透视设置等等,而所谓的3D, ...
- Qt下使用OpenCV3打开摄像头并把图像显示到QLabel上
前言 1.Qt5有自己摄像头的类QCamera,但是图像处理相关还是要使用OpenCV来做,这里我演示在Qt下使用OpenCV打开摄像头. 2.Qt的版本是5.9,Qt Creator 4.4.1,O ...
- Qt下Tcp传输文件
Qt下Tcp传输文件 文章目录 Qt下Tcp传输文件 1.服务端 2.客户端 1.服务端 //ServerWidgets.h #ifndef SERVERWIDGET_H #define SERVER ...
- Qt下Undefined reference to 'vtable for xxx'
QT下遇到这种错误提示时候需要注意以下情况: 一.cpp文件里使用了Q_OBJECT 分析:qmake不会处理.cpp文件里的Q_OBJECT,所以如果在.cpp文件中有它的话将会产生undefine ...
- Qt下继承于QObject创建的线程
Qt线程 线程创建方法 示例 线程创建方法 Qt下创建线程的方法有两种: 一种是通过继承QThread,并重写run()函数,在run()函数中,编写线程所做的事情,在需要线程的文件中,创建线程对象, ...
- Qt下实现多线程串口通信
Qt下实现多线程串口通信 Qt下无论是RS232.RS422.RS485的串口通信都可以使用统一的编码实现.本文把每路串口的通信各放在一个线程中,使用movetoThread的方式实现. 用Seria ...
最新文章
- Go 学习笔记(21)— 标准库 os 操作文件(新建、打开、写入、读取、删除、关闭文件)
- pandas使用pd.MultiIndex.from_product函数和pd.MultiIndex.from_tuples函数创建复合索引dataframe数据实战
- 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
- Matlab之if-else-end分枝语句
- spring计算方圆300km内其它城市(附完整代码)
- IOS 开发中 Whose view is not in the window hierarchy 错误的解决办法
- Oracle 11gR2 sessions=1.5*processes+22
- stm32采集脉冲信号_外部脉冲实现ADC采样的问题 STM32f103VE
- 搭建企业级Docker Registry -- Harbor
- 前端工程化:vue代码检查工具vetur
- 在VMware下安装中标麒麟操作系统7.0以及Neokylin基础常用知识
- Prometheus(6)Pormetheus+ Alertmanager配置邮件警告,并使用模板进行发送
- Ubuntu 下安装VirtualBox主要步骤及出现的问题的解决方案
- [爬虫]爬取猫眼电影票房信息(信息字体加密)
- 微信小程序收款手续费_为什么有人做的小程序交易手续费是0.38%而不是0.6%?
- 工作队列模式(任务队列)| RabbitMQ系列(二)
- uniapp开发-公众号h5、小程序、移动app
- K8S部署机器学习平台
- Linux服务器之内存过高解决思路
- vue——双层循环嵌套