QT+OpenGL模板测试和混合

简介: 当片段着色器处理完一个片段之后,模板测试会开始执行。和深度测试一样,它可能会丢弃片段,接下来被保留的片段会进入深度测试。

QT+OpenGL模板测试和混合

本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主


模板测试

当片段着色器处理完一个片段之后,模板测试会开始执行。和深度测试一样,它可能会丢弃片段,接下来被保留的片段会进入深度测试。


通常每个模板的值是8位的,所以每个像素/片段一共能有256种不同的模板值。


 ●  启用模板缓冲的写入。

 ●  渲染物体,更新模板缓冲的内容。

 ●  禁用模板缓冲的写入。

 ●  渲染(其它)物体,这次根据模板缓冲的内容丢弃特定的片段

// 启用模板缓冲
glEnable(GL_STENCIL_TEST);
// 清除模板缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glStencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
glStencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)

模板函数

一共有两个函数能够用来配置模板测试: glStenciFunc和glStencilOp

glStencilFunc(GLenum func, GLint ref, GLuint mask)


●  func:设置模板测试函数(Stencil Test Function)。这个测试函数将会应用到已储存的模板值上和glStencilFunc函数的ref值上。可用的选项有:GL_NEVER、GL_LESS、GL_LEQUAL、GL_GREATER、GL_GEQUAL、GL_EQUAL、GL_NOTEQUAL和GL_ALWAYS。它们的语义和深度缓冲的函数类似。

●  ref:设置了模板测试的参考值(Reference Value)。模板缓冲的内容将会与这个值进行比较。

●  mask:设置一个掩码,它将会与参考值和储存的模板值在测试比较它们之前进行与(AND)运算。初始情况下所有位都为1。


这个函数告诉opengl,只要一个片段的模板值等于1,它将会通过测试并被绘制,否则会被丢弃。但glStencilFunc仅仅描述了OpenGL应该对模板缓冲内筒做什么,而不是我们应该如何更新缓冲。

glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)


●  sfail:模板测试失败时采取的行为。

●  dpfail:模板测试通过,但深度测试失败时采取的行为。

●  dppass:模板测试和深度测试都通过时采取的行为。

行为 描述
GL_KEEP 保持当前储存的模板值
GL_ZERO 将模板值设置为0
GL_REPLACE 将模板值设置为glStencilFunc函数设置的ref
GL_INCR 如果模板值小于最大值则将模板值加1
GL_INCR_WRAP 与GL_INCR一样,但如果模板值超过了最大值则归零
GL_DECR 如果模板值大于最小值则将模板值减1
GL_DECR_WRAP 与GL_DECR一样,但如果模板值小于0则将其设置为最大值
GL_INVERT 按位翻转当前的模板缓冲值

默认情况下glStencilOp是设置为(GL_KEEP, GL_KEEP, GL_KEEP)的,所以不论任何测试的结果是如何,模板缓冲都会保留它的值。默认的行为不会更新模板缓冲,所以如果你想写入模板缓冲的话,你需要至少对其中一个选项设置不同的值。


物体轮廓

1.在绘制(需要添加轮廓的)物体之前,将模板函数设置为GL_ALWAYS,每当物体的片段被渲染时,将模板缓冲更新为1。

2.渲染物体。

3.禁用模板写入以及深度测试。

4.将每个物体缩放一点点。

5.使用一个不同的片段着色器,输出一个单独的(边框)颜色。

6.再次绘制物体,但只在它们片段的模板值不等于1时才绘制。

7.再次启用模板写入和深度测试。


代码展示:

void TurboOpenGLWidget::paintGL()
{
    model.setToIdentity();
    view.setToIdentity();
    projection.setToIdentity();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_STENCIL_TEST);
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glStencilMask(0x00); // 禁止模板缓冲的写入
    // float time = m_time.elapsed() / 50.0;
    // model.rotate(time, 1.0f, 5.0f, 0.0f);
    shader_program_.bind();
    projection.perspective(camera_.getZoom(), float(width() / height()), near_, far_);
    view = camera_.getViewMatrix();
    shader_program_.setUniformValue("projection", projection);
    shader_program_.setUniformValue("view", view);
    shader_program_.setUniformValue("viewPos", camera_.getPosition());
    shader_program_.setUniformValue("spotLight.ambient",  QVector3D(0.7, 0.7, 0.7));
    shader_program_.setUniformValue("spotLight.diffuse",  QVector3D(0.9, 0.9, 0.9)); // 将光照调暗了一些以搭配场景
    shader_program_.setUniformValue("spotLight.specular", QVector3D(1.0, 1.0, 1.0));
    shader_program_.setUniformValue("spotLight.position",  camera_.getPosition());
    shader_program_.setUniformValue("spotLight.direction", camera_.getFront());
    shader_program_.setUniformValue("spotLight.cutOff",   (float)cos(12.5f*3.1415926/180));
    shader_program_.setUniformValue("spotLight.outerCutOff",   (float)cos(17.5f*PI/180));
    shader_program_.setUniformValue("spotLight.constant",  1.0f);
    shader_program_.setUniformValue("spotLight.linear",  0.09f); // 将光照调暗了一些以搭配场景
    shader_program_.setUniformValue("spotLight.quadratic", 0.032f);
    shader_program_.setUniformValue("dirLight.direction", -0.2, -1.0, -0.3);
    shader_program_.setUniformValue("dirLight.ambient",  0.05, 0.05, 0.05);
    shader_program_.setUniformValue("dirLight.diffuse",  0.4, 0.4, 0.4); // 将光照调暗了一些以搭配场景
    shader_program_.setUniformValue("dirLight.specular", 0.5, 0.5, 0.5);
    shader_program_.setUniformValue("material.shininess", 32.0f);
    shader_program_.setUniformValue("model", model);
    m_planeMesh->draw(shader_program_);
    shader_program_.release();
    signal_color_program_.bind();
    signal_color_program_.setUniformValue("projection", projection);
    signal_color_program_.setUniformValue("view", view);
    signal_color_program_.release();
    auto it = models_.begin();
    while(it != models_.end())
    {
        model.setToIdentity();
        model.translate(it.value().world_pos);
        model.rotate(it.value().pitch, QVector3D(1.0, 0.0, 0.0));
        model.rotate(it.value().yaw, QVector3D(0.0, 1.0, 0.0));
        model.rotate(it.value().roll, QVector3D(0.0, 0.0, 1.0));
        glStencilFunc(GL_ALWAYS, 1, 0xFF);
        glStencilMask(0xFF);
        shader_program_.bind();
        shader_program_.setUniformValue("model", model);
        it.value().model->draw(shader_program_);
        shader_program_.release();
        if(!it.value().is_selected)
        {
            it++;
            continue;
        }
        glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
        glStencilMask(0x00);
        float height=it.value().model->max_y_-it.value().model->min_y_;
        float width=it.value().model->max_x_-it.value().model->min_x_;
        if(it.value().model->min_y_>=0)
            model.translate(0.0f,height/2,0.0f);
        model.scale(1.1f,1.0+0.1*(width/height));
        if(it.value().model->min_y_>=0)
            model.translate(0.0f,-height/2,0.0f);
        signal_color_program_.bind();
        signal_color_program_.setUniformValue("model", model);
        it.value().model->draw(signal_color_program_);
        signal_color_program_.release();
        glStencilMask(0xFF);
        glStencilFunc(GL_ALWAYS, 1, 0xFF);
        it++;
    }
    update();
}

image.png


混合

在OpenGL中混合主要是实现物体透明度的一中技术


丢弃片段

有些图片并不需要半透明,只需要根据纹理颜色值,显示一部分,或者不显示一部分,没有中间情况。


discard.frag

#version 330 core
struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};
uniform Material material;
struct SpotLight {
    vec3 position;
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant;
    float linear;
    float quadratic;
    float cutOff;
    float outerCutOff;
};
struct DirLight {
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirLight dirLight;
uniform SpotLight spotLight;
struct PointLight {
    vec3 position;
    float constant;
    float linear;
    float quadratic;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
out vec4 FragColor;
uniform vec3 lightPos;
uniform vec3 viewPos;
in vec3 Normal;
in vec3 fragPos;
in vec2 TexCoords;
void main()
{
    vec4 texColor=texture(material.diffuse,TexCoords);
    if(texColor.a < 0.1) discard;
    FragColor = texColor;
}
discard_program_.bind();
discard_program_.setUniformValue("projection", projection);
discard_program_.setUniformValue("view", view);
discard_program_.setUniformValue("model", model);
foreach (auto item, vegetation) {
    model.setToIdentity();
    model.translate(item);
    discard_program_.setUniformValue("model", model);
    m_discardMesh->draw(discard_program_);
}

具体的实现可以在项目中查询到。

42b35768eeb5453e941de1a1b42af06f.png


混合

虽然直接丢弃片段很好,但它不能让我们渲染半透明的图像。我们要么渲染一个片段,要么完全丢弃它。要想渲染有多个透明度级别的图像,我们需要启用混合(Blending)。和OpenGL大多数的功能一样,我们可以启用GL_BLEND来启用混合:

glEnable(GL_BLEND);


●  源颜色向量。这是源自纹理的颜色向量。

●  目标颜色向量。这是当前储存在颜色缓冲中的颜色向量。

●  源因子值。指定了alpha值对源颜色的影响。

●  目标因子值。指定了alpha值对目标颜色的影响。


选项
GL_ZERO 因子等于
GL_ONE 因子等于
GL_SRC_COLOR 因子等于源颜色向量
GL_ONE_MINUS_SRC_COLOR 因子等于
GL_DST_COLOR 因子等于目标颜色向量
GL_ONE_MINUS_DST_COLOR 因子等于
GL_SRC_ALPHA 因子等于的分量
GL_ONE_MINUS_SRC_ALPHA 因子等于 的分量
GL_DST_ALPHA 因子等于的分量
GL_ONE_MINUS_DST_ALPHA 因子等于 的分量
GL_CONSTANT_COLOR 因子等于常数颜色向量
GL_ONE_MINUS_CONSTANT_COLOR 因子等于
GL_CONSTANT_ALPHA 因子等于的分量
GL_ONE_MINUS_CONSTANT_ALPHA 因子等于 的分量

为了获得之前两个方形的混合结果,我们需要使用源颜色向量的作为源因子,使用作为目标因子。这将会产生以下的glBlendFunc:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

也可以使用glBlendFuncSeparate为RGB和alpha通道分别设置不同的选项:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);

OpenGL甚至给了我们更多的灵活性,允许我们改变方程中源和目标部分的运算符。当前源和目标是相加的,但如果愿意的话,我们也可以让它们相减。glBlendEquation(GLenum mode)允许我们设置运算符,它提供了三个选项:


●  GL_FUNC_ADD:默认选项,将两个分量相加:。

●  GL_FUNC_SUBTRACT:将两个分量相减: 。

●  GL_FUNC_REVERSE_SUBTRACT:将两个分量相减,但顺序相反:。


融合会存在遮挡问题,所以应该按照如下顺序绘制物体。


1.先绘制所有不透明的物体。

2.对所有透明的物体排序。

3.按顺序绘制所有透明的物体。


代码在项目中查看


效果展示:

b38fe0836f094a2c9ed68472ab95df62.png

目录
相关文章
|
7月前
|
测试技术 持续交付 人机交互
软件测试计划说明书模板
软件测试计划说明书模板
341 0
|
7月前
QT4.7版本的OPENGL的3D旋转模型例子
QT4.7版本的OPENGL的3D旋转模型例子
135 0
|
7月前
|
存储 C语言 Windows
音视频使用qt测试ffmpeg接口时无法运行
音视频使用qt测试ffmpeg接口时无法运行
125 0
|
4月前
|
Linux
关于linux的qt发布(linuxdeployqt)中opengl版本过高的解决
关于linux的qt发布(linuxdeployqt)中opengl版本过高的解决
|
5月前
|
监控 Python
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
`pytest-qt` 是一个用于在 Qt 应用程序中进行 GUI 测试的 pytest 插件。
|
6月前
|
运维 算法 计算机视觉
【Qt&OpenCV 图像的模板匹配 matchTemplate/minMaxLoc】
【Qt&OpenCV 图像的模板匹配 matchTemplate/minMaxLoc】
88 1
|
测试技术 UED
如何实施测试用例评审维护与更新?附模板
如何实施测试用例评审维护与更新?附模板
180 0
|
7月前
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
1049 4
|
7月前
|
Linux API iOS开发
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
【Qt 渲染引擎】一文带你了解qt的三种 渲染引擎,包括栅格引擎(Raster)、OpenGL 和本地绘图系统
232 0
|
7月前
Qt+OpenGL 打砖块游戏
Qt+OpenGL 打砖块游戏
90 0