面光源与平行光源的生成大多相同,因此这里只说明面光源与平行光源的区别。
关于平行光源的细节看这篇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;
}

问题

在平面上灯光在地面上形成光晕(一圈一圈的波纹),猜测是自阴影导致吧???
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐