面光源与平行光源的生成大多相同,因此这里只说明面光源与平行光源的区别。
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;
}
问题
在平面上灯光在地面上形成光晕(一圈一圈的波纹),猜测是自阴影导致吧???