当前位置:网站首页>QT with OpenGL(Shadow Mapping)(面光源篇)
QT with OpenGL(Shadow Mapping)(面光源篇)
2022-08-03 10:13:00 【Elsa的迷弟】
面光源与平行光源的生成大多相同,因此这里只说明面光源与平行光源的区别。
关于平行光源的细节看这篇blog
1. 生成深度帧缓存
对于每一个面光源都要生成一个帧缓存
depthMapFBO = new QOpenGLFramebufferObject(SHADOW_WIDTH,SHADOW_HEIGHT,QOpenGLFramebufferObject::Depth);
2. 生成Shadow Mapping
注意生成面光源时,不能使用正交矩阵,这便是面光源与点光源所不同的。
(1)计算平行光源视口下的正交投影矩阵
QMatrix4x4 PointLight::getLightMatrix()
{
QMatrix4x4 lightProjection, lightView;
float near_plane = 0.50f, far_plane = 100.5f;
lightProjection.perspective(150.0f,1,near_plane,far_plane);
//lightProjection.ortho(-eyeing, eyeing, -eyeing, eyeing, near_plane, far_plane);
lightView.lookAt(position, position+lightNormal, QVector3D(0.0, 1.0, 0.0));
lightSpaceMatrix = lightProjection * lightView;
return lightSpaceMatrix;
}
(2)生成shadow mapping图
void GLWidget::generatePointShadow(int k)
{
scene.pointlights[k]->depthMapFBO->bind();
glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT);
simpleDepthShader->bind();//阴影图着色器
simpleDepthShader->setUniformValue("lightSpaceMatrix",scene.pointlights[k]->getLightMatrix());
simpleDepthShader->setUniformValue("isPerspective",true);
for(int i=0;i<scene.objects.size();++i){
simpleDepthShader->setUniformValue("model",scene.objects.at(i)->model.getmodel());
scene.objects.at(i)->Draw(*simpleDepthShader);
}
scene.pointlights[k]->depthMapFBO->release();
}
其中simpleDepthShader
着色器的frag代码需要改变为
#version 450 core
out vec4 FragColor;
float near_plane = 0.5f;
float far_plane = 100.5f;
uniform bool isPerspective = false;
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // Back to NDC
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane))/ far_plane;
}
void main()
{
gl_FragDepth = gl_FragCoord.z;
float depth = gl_FragCoord.z;
if(isPerspective){
depth = LinearizeDepth(depth);
}
FragColor = vec4(vec3(depth), 1.0f);
}
当渲染的为面光源时,需要传入参数isPerspective=true
,将透视投影下的深度变成了线性的深度值。
否则,将深度缓冲视觉化经常会得到一个几乎全白的结果。
3. 显示阴影图,验证正确性
if(shadowShow){
if(objectNumber&&scene.objects.at(objectNumber-1)->islight){
showShadow(scene.pointlights.at(objectNumber-1)->depthMapFBO->texture());
}
else showShadow(scene.dirlight->depthMapFBO->texture());
return;
}
void GLWidget::showShadow(GLuint ID)
{
glViewport(0,0,width(),height());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
debug_dep->bind();//shader
debug_dep->setUniformValue("depthMap",0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,ID);
renderQuad();
debug_dep->release();
}
4.使用阴影贴图生成阴影
(1)设置Uniform参数
void ShaderSelector::setPointDir(int j, QVector<PointLight *> pointlights)
{
// struct PointLight {
// vec3 position;
// vec3 ambient;
// vec3 diffuse;
// vec3 specular;
// vec3 lightnormal;
// float constant;
// float linear;
// float quadratic;
// //阴影
// sampler2D shadowMap;
// mat4 lightSpaceMatrix;
// float width;
// }pointLights[16];
QOpenGLShaderProgram *shader = getShader(j);
int numPointLight = pointlights.size();
shader->setUniformValue("numPointLights",numPointLight);
QString structNameFront = "pointLights[";
for(int i = 0; i < numPointLight; i++){
QString StringNum;
StringNum.setNum(i);
QString StringI = structNameFront+StringNum;
shader->setUniformValue(QString(StringI+"].position").toStdString().c_str(),pointlights[i]->position);
shader->setUniformValue(QString(StringI+"].ambient").toStdString().c_str(),pointlights[i]->color * PointLight::ambient);
shader->setUniformValue(QString(StringI+"].diffuse").toStdString().c_str(),pointlights[i]->color * PointLight::diffuse);
shader->setUniformValue(QString(StringI+"].specular").toStdString().c_str(),pointlights[i]->color * PointLight::specular);
shader->setUniformValue(QString(StringI+"].lightnormal").toStdString().c_str(),pointlights[i]->lightNormal);
shader->setUniformValue(QString(StringI+"].constant").toStdString().c_str(),PointLight::constant);
shader->setUniformValue(QString(StringI+"].linear").toStdString().c_str(),PointLight::linear);
shader->setUniformValue(QString(StringI+"].quadratic").toStdString().c_str(),PointLight::quadratic);
shader->setUniformValue(QString(StringI+"].shadowMap").toStdString().c_str(), 4+i);
shader->setUniformValue(QString(StringI+"].lightSpaceMatrix").toStdString().c_str(),pointlights[i]->getLightMatrix());
shader->setUniformValue(QString(StringI+"].width").toStdString().c_str(),pointlights[i]->width);
//qDebug()<<"width="<<pointlights[i]->width;
glActiveTexture(GL_TEXTURE4+i);
glBindTexture(GL_TEXTURE_2D,pointlights[i]->depthMapFBO->texture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[] = {
1.0, 1.0, 1.0, 1.0 };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
}
}
(2)shader渲染
vert
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
out vec3 Normal;
out vec3 FragPos;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
Normal = mat3(transpose(inverse(model))) * aNormal;
TexCoords = aTexCoords;
FragPos = vec3(model * vec4(aPos,1.0));
gl_Position = projection * view * model * vec4(aPos,1.0);
}
frag,点光源代码
PCSS添加参数weightOfLight
,面光源面积越大,计算的半影就越大。
float PCSS(vec3 projCoords,sampler2D shadowMap,float weightOfLight){
// 取得最近点的深度(使用[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,shadowMap);
//第二步,计算半影半径
float penumbra = (projCoords.z - averBlocker) * weightOfLight / averBlocker;
//第三步 PCF
visibility = PCF(projCoords,int(penumbra),shadowMap);
return visibility;
}
void main()
{
for(int i = 0; i < numPointLights; i++){
result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
}
FragColor = vec4(result,1.0);
}
vec3 CalcPointLight(PointLight light,vec3 normal, vec3 fragPos,vec3 viewDir){
vec3 lightDir = normalize(light.position - fragPos);//片元指向光源
float angleDecay = 0.0f;
if(any(notEqual(light.lightnormal,vec3(0,0,0)))){
//光强cos衰减
angleDecay = max(dot(-lightDir,normalize(light.lightnormal)),0.0f);
}
float diff = max(dot(lightDir,normal),0.0);
vec3 reflectDir = reflect(-lightDir,normal);
float spec = 0.0;//反射系数
if(blinn){
vec3 halfwayDir = normalize(viewDir+lightDir);//半程向量
spec = pow(max(dot(normal,halfwayDir),0.0),material.shiness*4);
}
else{
vec3 reflectDir = reflect(-lightDir,normal); //反射方向
spec = pow(max(dot(viewDir,reflectDir),0.0),material.shiness);//计算镜面反射系数
}
float distance = length(light.position - fragPos);
float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));
vec3 diffusecolor = vec3(texture2D(material.texture_diffuse1,TexCoords));
vec3 specularcolor = vec3(texture2D(material.texture_specular1,TexCoords));
//反gamma矫正
diffusecolor = pow(diffusecolor, vec3(2.2));
specularcolor = pow(specularcolor , vec3(2.2));
vec3 ambient = light.ambient * diffusecolor;
vec3 diffuse = light.diffuse * diff * diffusecolor;
vec3 specular = light.specular * spec * specularcolor;
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
diffuse *= angleDecay;
specular *= angleDecay;
//阴影计算
float shadow = 0.0;
{
//光视角的点位置
vec4 FragPosLightSpace = light.lightSpaceMatrix * vec4(FragPos, 1.0);
// 执行透视除法
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
// 变换到[0,1]的范围
projCoords = projCoords * 0.5 + 0.5;
//转化为线性深度
projCoords.z = LinearizeDepth(projCoords.z);
// 计算阴影
shadow = PCSS(projCoords,light.shadowMap,light.width);
//shadow = PCF(projCoords,1,light.shadowMap);
}
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular));
return lighting;
}
frag全部代码
#version 450 core
struct Material {
sampler2D texture_diffuse1;
sampler2D texture_specular1;
float shiness;
};
struct DirLight {
bool Activated;
vec3 direction;
vec3 ambient;
vec3 diffuse;
vec3 specular;
//阴影
sampler2D shadowMap;
mat4 lightSpaceMatrix;
};
struct PointLight {
vec3 position;
vec3 lightnormal;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
//阴影
sampler2D shadowMap;
mat4 lightSpaceMatrix;
float width;
};
//顶点信息
in vec3 Normal;
in vec3 FragPos;//该像素在世界坐标系下的坐标
in vec2 TexCoords;
//输出
out vec4 FragColor;
//视点
uniform vec3 viewPos;
//平行光
uniform DirLight dirLight;
//点光源
uniform PointLight pointLights[16];
uniform int numPointLights;
uniform Material material;
//光照
uniform bool blinn;
//色调映射
uniform float toneMapping;
//gamma
uniform bool gamma;
const float PI = 3.141592653589793;
const float PI2 = 6.283185307179586;
float near_plane = 0.5f;
float far_plane = 100.5f;
//采样数
const int NUM_SAMPLES = 30;
//采样圈数
const int 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,sampler2D shadowMap);
float PCSS(vec3 projCoords,sampler2D shadowMap,float weightOfLight);
float averageBlockDep(vec3 projCoords,vec2 texelSize,sampler2D shadowMap);
void poissonDiskSamples(const in vec2 randomSeed);
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0; // Back to NDC
return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane))/far_plane;
}
//全局参数
vec2 poissonDisk[NUM_SAMPLES];
highp float rand_2to1(vec2 uv ) {
//传入一个二维数,传出一个假随机数。
// 0 - 1
const 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()
{
// 属性
vec3 norm = normalize(Normal);
vec3 viewDir = normalize(viewPos - FragPos);
vec3 result = vec3(0,0,0);
// 平行光
if(dirLight.Activated){
result += CalcDirLight(dirLight, norm, viewDir);
}
// 点光源
for(int i = 0; i < numPointLights; i++){
result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
}
//色调映射
if(toneMapping>0.0f){
//result.rgb = result.rgb /(result.rgb+ vec3(1.0));
result.rgb = vec3(1.0) - exp(-result.rgb * toneMapping);
}
//gamma矫正
float gamma_ = 2.2;
if(gamma){
result.rgb = pow(result.rgb, vec3(1.0/gamma_));
}
FragColor = vec4(result,1.0);
}
float averageBlockDep(vec3 projCoords,vec2 texelSize,sampler2D shadowMap){
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,sampler2D shadowMap,float weightOfLight){
// 取得最近点的深度(使用[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,shadowMap);
//第二步,计算半影半径
float penumbra = (projCoords.z - averBlocker) * weightOfLight / averBlocker;
//第三步 PCF
visibility = PCF(projCoords,int(penumbra),shadowMap);
return visibility;
}
float PCF(vec3 projCoords,int r,sampler2D shadowMap)
{
// 取得最近点的深度(使用[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);
//PCF
float 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);//平行光反方向
float diff = max(dot(lightDir,normal),0.0);//cos衰减系数
float spec = 0.0;//反射系数
if(blinn){
vec3 halfwayDir = normalize(viewDir+lightDir);//半程向量
spec = pow(max(dot(normal,halfwayDir),0.0),material.shiness*4);
}
else{
vec3 reflectDir = reflect(-lightDir,normal); //反射方向
spec = pow(max(dot(viewDir,reflectDir),0.0),material.shiness);//计算镜面反射系数
}
vec3 diffusecolor = vec3(texture2D(material.texture_diffuse1,TexCoords));
vec3 specularcolor = vec3(texture2D(material.texture_specular1,TexCoords));
//反gamma矫正
diffusecolor = pow(diffusecolor, vec3(2.2));
specularcolor = pow(specularcolor , vec3(2.2));
vec3 ambient = light.ambient * diffusecolor;
vec3 diffuse = light.diffuse * diff * diffusecolor;
vec3 specular = light.specular * spec * specularcolor;
//阴影计算
float shadow = 0.0;
{
//光视角的点位置
vec4 FragPosLightSpace = light.lightSpaceMatrix * vec4(FragPos, 1.0);
// 执行透视除法
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
// 变换到[0,1]的范围
projCoords = projCoords * 0.5 + 0.5;
// 计算阴影
shadow = PCSS(projCoords,light.shadowMap,5);
//shadow = PCF(projCoords,1,light.shadowMap);
}
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 = 0.0f;
if(any(notEqual(light.lightnormal,vec3(0,0,0)))){
//不是(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 = 0.0;//反射系数
if(blinn){
vec3 halfwayDir = normalize(viewDir+lightDir);//半程向量
spec = pow(max(dot(normal,halfwayDir),0.0),material.shiness*4);
}
else{
vec3 reflectDir = reflect(-lightDir,normal); //反射方向
spec = pow(max(dot(viewDir,reflectDir),0.0),material.shiness);//计算镜面反射系数
}
float distance = length(light.position - fragPos);
float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));
vec3 diffusecolor = vec3(texture2D(material.texture_diffuse1,TexCoords));
vec3 specularcolor = vec3(texture2D(material.texture_specular1,TexCoords));
//反gamma矫正
diffusecolor = pow(diffusecolor, vec3(2.2));
specularcolor = pow(specularcolor , vec3(2.2));
vec3 ambient = light.ambient * diffusecolor;
vec3 diffuse = light.diffuse * diff * diffusecolor;
vec3 specular = light.specular * spec * specularcolor;
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
diffuse *= angleDecay;
specular *= angleDecay;
//阴影计算
float shadow = 0.0;
{
//光视角的点位置
vec4 FragPosLightSpace = light.lightSpaceMatrix * vec4(FragPos, 1.0);
// 执行透视除法
vec3 projCoords = FragPosLightSpace.xyz / FragPosLightSpace.w;
// 变换到[0,1]的范围
projCoords = projCoords * 0.5 + 0.5;
//转化为线性深度
projCoords.z = LinearizeDepth(projCoords.z);
// 计算阴影
shadow = PCSS(projCoords,light.shadowMap,light.width);
//shadow = PCF(projCoords,1,light.shadowMap);
}
vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular));
return lighting;
}
问题
在平面上灯光在地面上形成光晕(一圈一圈的波纹),猜测是自阴影导致吧???
边栏推荐
猜你喜欢
VL53L0X V2 laser ranging sensor collects distance data serial output
go版本升级
Scrapy + Selenium implements simulated login and obtains dynamic page loading data
三大产品力赋能欧萌达OMODA5
被审稿人吐槽没有novelty!深度学习方向怎么找创新点?
2022T电梯修理考试题及答案
后台图库上传功能
Scrapy + Selenium 实现模拟登录,获取页面动态加载数据
Oracle 迁移至Mysql
ImportError: DLL load failed with error code -1073741795
随机推荐
如何优雅的消除系统重复代码
Mysql OCP 26题
Mysql OCP 29题
消费者认可度较高 地理标志农产品为啥“香”
2022年山东省安全员C证复习题模拟考试平台操作
Go操作Redis数据库
Scapy的介绍(一)「建议收藏」
go——并发编程
This article understands the process from RS485 sensor to IoT gateway to cloud platform
With strong network, China mobile to calculate excitation surging energy network construction
分辨率_分辨率越高越好?手机屏幕分辨率多少才合适?现在终于搞清楚了[通俗易懂]
HCIP第十七天笔记
力扣递归训练
Mysql OCP 30题
Mysql 主从复制 作用和原理
金先生谈长效生长激素出海与产品力
【学习笔记之菜Dog学C】通讯录
Mysql OCP 27题
2022T电梯修理考试题及答案
问下flink -sql 通过cdc抽取数据怎么能更快的抽取数据写到目标端?如何配置?