结合OPenGL绘图渲染实现

前言

本篇是学习OpenGL时候做的实验,意在理解OpenGL渲染的相关操作,网上入门100多节课,看看了解下原理也可以,实际上开发起来就那些东西,我这篇看懂了可以去看看更深的课程了。本次项目是绘制一个彩色三角形并且让它动起来。

准备

首先是环境方面,我是用 c++QT框架开发,他里面已经集成了OpenGL模块很方便,qmake环境下在pro文件中加上Qt+=opengl就可以了,就glad要上网下载一下,这里就不提供了,下载好后放到项目中就行了。

原理

OpenGL其实就是一个状态机,通过他的api调用gpu函数在gpu上操作,比如在显存上开辟空间存放绘制数据等

vbo

简单理解就是存放在显存上的绘制数据,开辟空间放入数据等后面看我程序就行了。

vao

vao是vbo的描述属性,vbo里面的vec在gpu中是flaot数组,gpu哪里知道哪些是顶点数据,一个顶点有几个数据,哪些又是颜色数据,就是描述这些,和vbo一起绑定在OpenGL状态机的接口上

shader

这里就要提一下gpu的渲染了,他是先把顶点绘制出来,然后光栅化,比如画一个三角形,先通过vertxt部分glsl程序传入的顶点数据画出来顶点,然后进入到光栅化,将三个点围成的区域处理成许多小方格,每个小方格就是一个像素,也叫片元,vertxt程序中在开始的时候接收进来的颜色数据会传到fragment程序里面,fragment程序就会先处理已绘制的顶点颜色,然后中间那些片元的颜色咋搞,就用到了插值算法着色,插值算法就不提了,不懂去搜很简单。

程序实现

接下来我带着打架看看一个简单的三角形绘制,环境配置讲过了就不多说了,直接进入程序编写

UI

UI我搭建的很简单,用的mainwindow,里面放入openglwediget模块,选择这个模块是为了方便后期事件处理等比较方便,用纯c++写比较繁琐,qt有些api把这些过程封装起来了好用。然后这个窗口要提升一下,写了一个mywediget类,继承的是OpenGLwediget和OpenGL的函数

class myWediget : public QOpenGLWidget,protected QOpenGLExtraFunctions

提升的时候加入object,挂到对象树上管理。

mywediget

刚创建的时候做好初始化

构造函数:

myWediget(QWidget *parent = nullptr) ;
myWediget::myWediget(QWidget *parent) : QOpenGLWidget(parent) {}

重写了三个函数:

virtual void initializeGL() override;
virtual void resizeGL(int w, int h) override;
virtual void paintGL() override;

initializeGL()是初始化函数,resizeGL()是设置窗口函数,paintGL()是绘制函数。

初始化函数就是把vbo和vao放进去准备绘制的数据

void myWediget::initializeGL() {
    initializeOpenGLFunctions();
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

    // 1. 创建 VAO 和 VBO
    glGenVertexArrays(1, &VAO);//posVBO,ColorVBO,VAO是创建好的GLuint变量=uint
    glGenBuffers(1, &posVBO);//分别创建一个vao,posVBO,ColorVBO
    glGenBuffers(1, &ColorVBO);

    // 2. 绑定 VAO 和 VBO,vao是vbo的描述,把他们两个绑定起来
    //QOpenGLExtraFunctions::glBindVertexArray(VAO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, posVBO);//GL_ARRAY_BUFFER是状态机上的vbo接口,把这个vbo绑上去
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//vertices是矩阵数据数组类型的,下面的color一样

    glBindBuffer(GL_ARRAY_BUFFER, ColorVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);

    // 3. 设置顶点属性指针
    glBindBuffer(GL_ARRAY_BUFFER, posVBO);//当前状态机绑定的是colorvbo,我们要先绘制顶点,传入顶点数据,所以要绑定会posvbo
    glEnableVertexAttribArray(0);//激活vao的0号位置,0号位置存放的就是posvbo的描述
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    glBindBuffer(GL_ARRAY_BUFFER, ColorVBO);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);


    // 4. 解绑
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    prepareshader();
}

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);第一个参数是vao的0号位置,第二个参数是3,意思是数组中有三个顶点的数据,字符类型float,归一化这里不管填false,3 * sizeof(float)意思是步长,就是我们一个顶点有三个float数值x,y,z,三个为一个顶点,后面哪个是偏移量,这里用不上,填0,历史原因前面要加(void*)。

prepareshader

经过上面的操作以后数据准备好了,我们再要准备着色器程序,就是vertexshader和fragmentshader

c++写法是:

shaderprogram.addShaderFromSourceCode(QOpenGLShader::Vertex,
                                          "#version 460 core\n"
                                          "layout(location=0) in vec3 aPos;\n"
                                          "layout(location=1) in vec3 aColor;\n"
                                          "out vec3 Color;\n"
                                          "void main() { gl_Position = vec4(aPos, 1.0); "
                                          "Color=aColor;"
                                          "}");
    shaderprogram.addShaderFromSourceCode(QOpenGLShader::Fragment,
                                          "#version 460 core\n"
                                          "in vec3 Color;\n"
                                          "out vec4 FragColor;\n"
                                          "void main() { FragColor = vec4(Color, 1.0); }");
    shaderprogram.link();

这样写glsl他代码没有高亮,你也不知道是对是错,也不方便管理,就在vscode中单独写了这两个glsl程序 

vertexshader

#version 460 core
layout(location=0)in vec3 aPos;
layout(location=1)in vec3 aColor;
uniform float time;

out vec3 Color;
void main(){
    float offsetx=sin(time);
    gl_Position=vec4(aPos.x+offsetx,aPos.y,aPos.z,1.0);
    Color=aColor;
}

fragmentshader 

#version 460 core
in vec3 Color;
out vec4 FragColor;
void main(){FragColor=vec4(Color,1.0);}

把这两个程序加入到项目中,用qfile把他以文本形式读取出来

void shaderprogram::loadfile(const QString &vertexpath, const QString &fragmentpath)
{
    auto readfile=[](const QString &path)->QString{
        QFile file(path);
        if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){
            qDebug()<<"文件打开失败";
            return "";
        }
        else {
            auto text= QString(file.readAll());
            qDebug() << "读取文件内容:" << path << "\n" << text; // 输出实际读取的内容
            return text;
        }

    };
    QString vertextcode=readfile(vertexpath);
    QString fragmentcode=readfile(fragmentpath);

    if(vertextcode.isEmpty()||fragmentcode.isEmpty()){
        qDebug()<<"代码读取为空!";
        return;
    }

    //编译顶点着色器
    if(!addShaderFromSourceCode(QOpenGLShader::Vertex,vertextcode)){
        qDebug()<<"顶点着色器编译失败!"<<log();
        return;
    }

    //编译片段着色器
    if(!addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentcode)){
        qDebug()<<"片段着色器编译失败!"<<log();
        return;
    }
    //链接shader
    if(!link()){
        qDebug()<<"链接shader失败"<<log();
        return;
    }

}
paintGL

这些弄好了以后就开始绘制了

void myWediget::paintGL() {
    static QElapsedTimer timer;
    if (!timer.isValid()) timer.start();
    m_time = timer.elapsed() / 1000.0f;

    glClear(GL_COLOR_BUFFER_BIT);//清理画布颜色
    sdprogram.bind();绑定program
    sdprogram.setUniformValue("time",m_time);

    glBindVertexArray(VAO);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    sdprogram.release();
    update();//更新界面
}

把系统时间m_time传给vertexshader中的uniform time变量,让三角形随着时间变动移动。

到此就做完了,更多细节可以看我源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值