qt openGL学习笔记:利用帧缓冲(颜色缓冲)实现鼠标拾取

实现思路

我们可以为每一个模型赋予其一个唯一的颜色ID,并将其传入shader中,然后在GL_COLOR_ATTACHMENT1等这些额外的颜色缓冲中绘制。当鼠标点击时,可以通过glReadPixels()获取像素值并遍历所有的模型ID,就能得知是否选中模型或选中了哪一个模型。

实现过程

1.创建帧缓冲

(部分代码借鉴阿西拜大佬的教程)

为了方便,笔者就全用贴图绑定帧缓冲了。创建两个GL_RGB贴图作为颜色缓冲0和1,,一个深度模板贴图并绑定

    //创建帧缓冲
    glGenFramebuffers(1,&FBO);
    glBindFramebuffer(GL_FRAMEBUFFER,FBO);

    //创建第零颜色缓冲贴图
    glGenTextures(1,&frame_color_texture);
    glBindTexture(GL_TEXTURE_2D,frame_color_texture);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width(),height(),0,GL_RGB,GL_UNSIGNED_BYTE,NULL);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,frame_color_texture,0);   //将纹理绑定为颜色缓冲
    //创建深度与模板缓冲贴图
    glGenTextures(1,&frame_depth_stencil_texture);
    glBindTexture(GL_TEXTURE_2D,frame_depth_stencil_texture);
    glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH24_STENCIL8,width(),height(),0,GL_DEPTH_STENCIL,GL_UNSIGNED_INT_24_8,NULL);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_TEXTURE_2D,frame_depth_stencil_texture,0);
    //创建第一颜色缓冲贴图
    glGenTextures(1,&frame_id_texture);
    glBindTexture(GL_TEXTURE_2D,frame_id_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width(), height(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,frame_id_texture,0);

注意:颜色附着1不能直接只设一个单通道灰度图进去,shader传出来的还是一个四元数

开启Buffer绘制

GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
    glDrawBuffers(2, drawBuffers);

2.设置模型与shader

这里笔者接下来的代码,都是基于ID的范围是0-255所写的,需要拓展的话,三个通道都可以用。

具体设置ID就不写了,确保不重复就行。直接上shader

片元着色器:

#version 330 core

layout(location =0) out vec4 FragColor;
layout(location =1) out vec4 Index;

uniform int ModelIndex;

void main(){
    ...
    float _index=float(ModelIndex);
    Index=vec4(_index/255.0,0.0,0.0,1.0);
}

location规定往那个颜色缓冲中传值。因为ID范围是0-255,而RED通道范围是0-1,所以除以255缩放一下。

3.像素获取

在qt鼠标点击事件mousePressEvent(QMouseEvent *event)中,切换刚刚创建好的帧缓冲,并使用glReadPixels()获取特定的像素值

        makeCurrent();
        //切换帧缓冲,为读取索引值做好准备
        glBindFramebuffer(GL_FRAMEBUFFER,FBO);
//从颜色附件1中读取像素值(索引值)
        GLubyte pixel[4];
        glReadBuffer(GL_COLOR_ATTACHMENT1);
        glReadPixels(
        (int)event->pos().x(),
        this->height()-(int)event->pos().y()
        ,1,1
        ,GL_RGB,GL_UNSIGNED_BYTE
        ,pixel);
        int flag=0;     //标识是否有模型被选中
        //遍历模型,检测哪个被选中
        foreach(auto modelinfo,m_Models){

            if(modelinfo.model_index==pixel[0]){
                flag=1;
                modelinfo.isSelected=true;
                qDebug()<<"The model be selected"<<"   index: "<<pixel[0];
            }else{
                modelinfo.isSelected=false;

            }

        }
        if(flag==0){
            qDebug()<<"No model be selected"<<"   index: "<<pixel[0];
        }
        //操作执行完毕,切换回默认帧缓冲
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject() );
        doneCurrent();

makeCurrent()和doneCurrent(): 因为是在initializeGL(),resizeGL()和paintGL()之外修改状态机,所以要添加上下文。

glReadBuffer(): 让glReadPiexls()去读颜色附件1而不是0的值

GLubyte: 因为附件1的格式是GL_UNSIGNED_BYTE,所以用一个GLuyte的数组去承接值。

this->height()-(int)event->pos().y(): 贴图的原点在左下角,但窗口的原点在左上角,所以要转换一下。

pixel[0]: shader会自动将0-1的浮点数转换回GL_UNSIGNED_BYTE(相当于又乘了个255),所以pixel[0]的值正好是模型ID。

4.实现效果

openGLU

可以看到,即使模型重叠到一起,程序依然可以区分不同的模型。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值