QT With OpenGL(延时着色法)(Deferred Shading)

文章目录

  • 1.创建G-Buffer帧缓存
  • 2.修改各类型物体的着色器
  • 3. 测试G_Buffer
  • 4. 使用G_Buffer生成场景
  • 5. 添加高光项
    • (1)如果为光源物体,则不进行光照结果计算
    • (2)对高光项进行高斯模糊
    • (3)泛光合成
  • 更多
    • 光体积(Light Volumes)

1.创建G-Buffer帧缓存

//G-Buffer
    //  位置
    G_Buffer = new QOpenGLFramebufferObject(size(),QOpenGLFramebufferObject::CombinedDepthStencil,GL_TEXTURE_2D,GL_RGBA16F);
    //  法向量
    G_Buffer->addColorAttachment(size(),GL_RGBA);
    //  颜色(HDR)+镜面颜色
    G_Buffer->addColorAttachment(size(),GL_RGBA16F);
    //  高光图(只计算光源物体)
    G_Buffer->addColorAttachment(size(),GL_RGBA16F);
    //  设置着色器渲染纹理路径
    G_Buffer->bind();
    GLenum buffers2[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
    core->glDrawBuffers(3, buffers2);
    G_Buffer->release();

2.修改各类型物体的着色器

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去除光照部分

#version 450 core

//输出
layout (location = 0) out vec3 gPosition;
layout (location = 1) out vec3 gNormal;
layout (location = 2) out vec4 gAlbedoSpec;
layout (location = 3) out vec4 BrightColor;

struct Material {
    vec3 color;
    //float shiness;
};


//顶点信息
in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;

uniform Material material;
uniform bool islight;


void main()
{
    // 存储第一个G缓冲纹理中的片段位置向量
    gPosition = FragPos;
    // 同样存储对每个逐片段法线到G缓冲中
    gNormal = normalize(Normal);
    // 和漫反射对每个逐片段颜色
    gAlbedoSpec.rgb = material.color;
    // 存储镜面强度到gAlbedoSpec的alpha分量
    gAlbedoSpec.a = 0.3;

    // 高光项存储在BrightColor
    if(islight){
        BrightColor = vec4(material.color, 1.0);
    }
    else BrightColor = vec4(0,0,0, 1.0);

}

3. 测试G_Buffer

    glViewport(0,height()/2,width()/2,height()/2);
    showShadow(G_Buffer->textures().at(0));
    glViewport(width()/2,height()/2,width()/2,height()/2);
    showShadow(G_Buffer->textures().at(1));
    glViewport(0,0,width()/2,height()/2);
    showShadow(G_Buffer->textures().at(2));
    glViewport(width()/2,0,width()/2,height()/2);
    showShadow(G_Buffer->textures().at(3));
    return;

QT With OpenGL(延时着色法)(Deferred Shading)_第1张图片
gPosition = FragPos;改为 gPosition = FragPos/(FragPos+vec3(100,100,100)); 获得更好的视觉效果
QT With OpenGL(延时着色法)(Deferred Shading)_第2张图片

4. 使用G_Buffer生成场景

着色器frag:

#version 450 core

//输出
layout (location = 0) out vec4 FragColor;
in vec2 TexCoords;

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;
};

//视点
uniform vec3 viewPos;
//平行光
uniform DirLight dirLight;

//点光源
uniform PointLight pointLights[16];
uniform int numPointLights;

//材质
uniform float shiness;

//传入参数
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gAlbedoSpec;
//光照
uniform bool blinn;
//色调映射
uniform float toneMapping;
//gamma
uniform bool gamma;


//输入信息
vec3 Normal;
vec3 FragPos;
vec3 diffusecolor;
vec3 specularcolor;


//常量
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()
{
       //FragColor = vec4(1,1,1,0);
       //return ;
        //获取G—Buffer信息
       Normal = texture2D(gNormal,TexCoords).rgb;
       FragPos = texture2D(gPosition,TexCoords).rgb;
       diffusecolor = texture2D(gAlbedoSpec,TexCoords).rgb;
       specularcolor = diffusecolor * texture2D(gAlbedoSpec,TexCoords).a;

       //反gamma矫正
       diffusecolor = pow(diffusecolor, vec3(2.2));
       specularcolor = pow(specularcolor , vec3(2.2));

       // 属性
       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 brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
//       //if(brightness > 1.0) BrightColor = vec4(FragColor.rgb, 1.0);
//       //else
//       BrightColor = vec4(0,0,0, 1.0);
//       //BrightColor = vec4(1.0,1.0,1.0, 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),shiness*4);
        }
        else{
            vec3 reflectDir = reflect(-lightDir,normal); //反射方向
            spec = pow(max(dot(viewDir,reflectDir),0.0),shiness);//计算镜面反射系数
        }

        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);
        float spec = 0.0;//反射系数
        if(blinn){
            vec3 halfwayDir = normalize(viewDir+lightDir);//半程向量
            spec = pow(max(dot(normal,halfwayDir),0.0),shiness*4);
        }
        else{
            vec3 reflectDir = reflect(-lightDir,normal); //反射方向
            spec = pow(max(dot(viewDir,reflectDir),0.0),shiness);//计算镜面反射系数
        }

        float distance = length(light.position - fragPos);
        float attenuation = 1.0/(light.constant + light.linear * distance + light.quadratic * (distance * distance));

        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;
}

Uniform绑定

G_BufferShader->bind();
    // G_Buffer输入
    G_BufferShader->setUniformValue("gPosition",0);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,G_Buffer->textures().at(0));
    G_BufferShader->setUniformValue("gNormal",1);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D,G_Buffer->textures().at(1));
    G_BufferShader->setUniformValue("gAlbedoSpec",2);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D,G_Buffer->textures().at(2));
    // 光照信息输入
    glActiveTexture(GL_TEXTURE3);
    scene.dirlight->setShaderPara(G_BufferShader);
    int numPointLight = pointLight.size();
    G_BufferShader->setUniformValue("numPointLights",numPointLight);
    for(int i=0;i<pointLight.length();++i){
        glActiveTexture(GL_TEXTURE4+i);
        pointLight.at(i)->setShaderPara(G_BufferShader,i);
    }
    // 其他信息
    G_BufferShader->setUniformValue("viewPos",maincamera.getCameraPos());
    G_BufferShader->setUniformValue("shiness",64.0f);
    // 调试参数
    G_BufferShader->setUniformValue("gamma",gamma);
    G_BufferShader->setUniformValue("blinn",blinn);
    G_BufferShader->setUniformValue("toneMapping",toneMapping);
    renderQuad();

结果

5. 添加高光项

(1)如果为光源物体,则不进行光照结果计算

    if(islight) {
        gAlbedoSpec = vec4(0,0,0,0.1);
        BrightColor = vec4(color, 1.0);
    }
    else{
        gAlbedoSpec = vec4(color,0.1);
        BrightColor = vec4(0,0,0, 1.0);
    }

QT With OpenGL(延时着色法)(Deferred Shading)_第3张图片

(2)对高光项进行高斯模糊

unsigned int ID = gaussBlur->getGaussBlurPhoto(G_Buffer->textures().at(3),width(),height(),15);

QT With OpenGL(延时着色法)(Deferred Shading)_第4张图片

(3)泛光合成

Uniform绑定

    G_BufferShader->setUniformValue("gBrightColor",3);
    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D,ID);

Shader

       //泛光
       result += texture2D(gBrightColor,TexCoords).rgb;

QT With OpenGL(延时着色法)(Deferred Shading)_第5张图片

更多

光体积(Light Volumes)

延迟渲染一直被称赞的原因就是它能够渲染大量的光源而不消耗大量的性能。然而,延迟渲染它本身并不能支持非常大量的光源,因为我们仍然必须要对场景中每一个光源计算每一个片段的光照分量。真正让大量光源成为可能的是我们能够对延迟渲染管线引用的一个非常棒的优化:光体积(Light Volumes)

  • Tutorial 35: Deferred Shading - Part 1:OGLDev的一个分成三部分的延迟着色法教程。在Part 23中介绍了渲染光体积

你可能感兴趣的:(OpenGL+QT,OpenGL,qt,OpenGL,延时渲染)