QT+OpenGL模板测试和混合

news2024/12/25 8:50:41

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

在这里插入图片描述

混合

在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_);
}

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

在这里插入图片描述

混合

虽然直接丢弃片段很好,但它不能让我们渲染半透明的图像。我们要么渲染一个片段,要么完全丢弃它。要想渲染有多个透明度级别的图像,我们需要启用混合(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. 按顺序绘制所有透明的物体。

代码在项目中查看

效果展示:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Win11的两个实用技巧系列之Win11怎么找回Win7照片查看器

Win11怎么找回Win7照片查看器? Win11旧版照片查看器的切换方法Win11怎么找回Win7照片查看器&#xff1f;用习惯了win7的照片查看器&#xff0c;想要在win11中使用&#xff0c;该怎么启用旧版照片查看器呢&#xff1f;下面我们就来看看Win11旧版照片查看器的切换方法Win11系统启…

c++之二叉树【进阶版】

前言 在c语言阶段的数据结构系列中已经学习过二叉树&#xff0c;但是这篇文章是二叉树的进阶版&#xff0c;因为首先就会讲到一种树形结构“二叉搜索树”&#xff0c;学习二叉搜索树的目标是为了更好的理解map和set的特性。二叉搜索树的特性就是左子树键值小于根&#xff0c;右…

【JVM】运行时数据区与对象的创建流程

4、运行时数据区 4.1、运行时数据区介绍 运行时数据区也就是JVM在运⾏时产生的数据存放的区域&#xff0c;这块区域就是JVM的内存区域&#xff0c;也称为JVM的内存模型——JMM 堆空间&#xff08;线程共享&#xff09;&#xff1a;存放new出来的对象 元空间&#xff08;线程共…

3,预初始化(一)(大象无形9.2)

正如书中所说&#xff0c;预初始化流程由FEngineLoop::PreInit()所实现 主要处理流程 1&#xff0c;设置路径&#xff1a;当前程序路径&#xff0c;当前工作目录路径&#xff0c;游戏的工程路径 2,设置标准输出&#xff1a;设置GLog系统输出的设备&#xff0c;是输出到命令行…

web自动化测试-执行 JavaScript 脚本

JavaScript 是一种脚本语言&#xff0c;有的场景需要使用 js 脚本注入辅助我们完成 Selenium 无法做到的事情。 当 webdriver 遇到无法完成的操作时&#xff0c;可以使用 JavaScript 来完成&#xff0c;webdriver 提供了 execute_script() 方法来调用 js 代码。 执行 js 有两种…

Leetcode.2385 感染二叉树需要的总时间

题目链接 Leetcode.2385 感染二叉树需要的总时间 Rating &#xff1a; 1711 题目描述 给你一棵二叉树的根节点 root&#xff0c;二叉树中节点的值 互不相同 。另给你一个整数 start。在第 0分钟&#xff0c;感染 将会从值为 start的节点开始爆发。 每分钟&#xff0c;如果节点…

一文3000字从0到1实现基于requests框架接口自动化测试项目实战(建议收藏)

requests库是一个常用的用于http请求的模块&#xff0c;它使用python语言编写&#xff0c;在当下python系列的接口自动化中应用广泛&#xff0c;本文将带领大家深入学习这个库 Python环境的安装就不在这里赘述了&#xff0c;我们直接开干。 01、requests的安装 windows下执行…

大数据常见应用场景及架构改进

大数据常见应用场景及架构改进大数据典型的离线处理场景1.大数据数据仓库及它的架构改进2.海量数据规模下的搜索与检索3.新兴的图计算领域4.海量数据挖掘潜在价值大数据实时处理场景大数据典型的离线处理场景 1.大数据数据仓库及它的架构改进 对于离线场景&#xff0c;最典型…

磷脂-聚乙二醇-丙烯酸酯;DSPE-PEG-AC试剂说明;DSPE-PEG-Acrylate科研用

中文名称&#xff1a;磷脂-聚乙二醇-丙烯酸酯 丙烯酸酯-聚乙二醇-磷脂 简称&#xff1a;DSPE-PEG-AC&#xff1b;DSPE-PEG-Acrylate 溶剂&#xff1a;溶于部分常规有机溶剂 PEG分子量:1000&#xff1b;2000&#xff1b;3400&#xff1b;5000等等 注意事项&#xff1a;避免…

JavaSE02-JVM、JRE、JDK

文章目录一、JVM、JRE、JDK区别二、JDK的安装和配置1.JDK安装2.测试验证3.环境变量配置3.1 配置JAVA_HOME系统变量3.2 配置Path环境变量再最前面加上&#xff1a; %JAVA_HOME%\bin一、JVM、JRE、JDK区别 JVM&#xff08;Java Virtual Machine&#xff09;&#xff0c;Java虚拟…

jar包和AAR包

以前在使用 Eclipse 开发 Android 时&#xff0c;如果想代码打包&#xff0c;只有 jar 包一个方法&#xff0c;但是 jar包 只能把 Java 文件代码打包进去&#xff0c;如果要使用一个有布局和资源的库的话&#xff0c;除了将 jar 放入 libs 外,还要引入相关的资源和配置文件&…

详解一个TCP连接的建立与销毁

目录 &#x1f332; 图解TCP三次握手建立连接 TCP数据报结构 TCP连接的建立&#xff08;三次握手&#xff09; 最后的说明 &#x1f332; 详细分析TCP数据的传输过程 &#x1f332; 图解TCP四次握手断开连接 &#x1f332; 图解TCP三次握手建立连接 TCP&#xff08;Tran…

【模拟集成电路】宽摆幅压控振荡器(VCO)设计

鉴频鉴相器设计&#xff08;Phase Frequency Detector&#xff0c;PFD&#xff09;前言一、VCO工作原理二、VCO电路设计VCO原理图三、压控振荡器&#xff08;VCO&#xff09;测试VCO测试电路图瞬态测试&#xff08;1&#xff09;瞬态输出&#xff08;2&#xff09;局部放大图&a…

【Java】Spring Boot项目的创建和使用

文章目录SpringBoot的创建和使用1. 什么是Spring Boot&#xff1f;为什么要学Spring Boot&#xff1f;2. Spring Boot项目的优点3. Spring Boot 项目的创建3.1 使用idea创建3.2 接下来创建Spring Boot项目4. 项目目录介绍和运行4.1 运行项目4.2 输出内容5. 总结SpringBoot的创建…

nyist最终淘汰赛第一场

我出的题喜欢吗 我要水题解所以每一篇题解都分一个博客 A 题解链接: Atcoder abc257 E_霾まる的博客-CSDN博客 构造贪心题 在本次淘汰赛中较难 B 题解链接: atcoder abc217 D_霾まる的博客-CSDN博客 STL二分题, 当然你可以数组二分, 相对麻烦一点 在本次淘汰赛中较简单…

学习 Python 之 Pygame 开发魂斗罗(二)

学习 Python 之 Pygame 开发魂斗罗&#xff08;二&#xff09;魂斗罗的需求开始编写魂斗罗1. 搭建主类框架2. 设置游戏运行遍历和创建窗口3. 获取窗口中的事件4. 创建角色5. 完成角色更新函数魂斗罗的需求 魂斗罗游戏中包含很多个物体&#xff0c;现在要对这些物体进行总结 类…

【RabbitMQ笔记02】消息队列RabbitMQ七种模式之最简单的模式

这篇文章&#xff0c;主要介绍RabbitMQ消息队列中七种模式里面最简单的使用模式。 目录 一、消息队列的使用 1.1、消息队列七种模式 1.2、最简单的模式使用 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;编写生产者 &#xff08;3&#xff09;编写消费者…

Enhanced ShockBurst (ESB)原文翻译

自我学习为主&#xff0c;同时也为所需要的提供一份资料 官方地址 增强型ShockBurst&#xff08;ESB&#xff09;是一种支持双向数据包通信的基本协议&#xff0c;包括数据包缓冲、数据包确认和丢失数据包的自动重传。ESB以低功耗提供无线通信&#xff0c;并且实现的代码量小且…

数据集市与数据仓库的区别

数据仓库是企业级的&#xff0c;能为整个企业各个部门的运作提供决策支持&#xff1b;而数据集市则是部门级的&#xff0c;一般只能为某个局部范围内的管理人员服务&#xff0c;因此也称之为部门级数据仓库。 1、两种数据集市结构 数据集市按数据的来源分为以下两种 &#x…

MATLAB算法实战应用案例精讲-【数模应用】多元线性回归(MLR)(附Java、python和matlab代码实现)

前言 在回归分析中,如果有两个或两个以上的自变量,就称为多元回归。事实上,一种现象常常是与多个因素相联系的,由多个自变量的最优组合共同来预测或估计因变量,比只用一个自变量进行预测或估计更有效,更符合实际。因此多元线性回归比一元线性回归的实用意义更大。 知识…