OpenGL学习随笔(一)——2022.1.18

        本次学习采用的编译器为Qt Creator,利用Qt自带的OpenGLWidget作为OpenGL图形显示的窗口,学习教材为《计算机图形学编程(使用OpenGL和C++)》作者:V.斯科特.戈登    约翰.克莱维吉  (人民邮电出版社)

一、OpenGL简介

        OpenGL是整合软硬件的多平台2D和3D图形的API。使用OpenGL需要显卡(GPU)支持足够新版 的OpenGL。在硬件方面,OpenGL提供了一个多级图形管线,可以使用一种名为GLSL的语言进行部分编程(语法与C++类似);在软件方面,OpenGL的API是用C语言编写的,因此API调用直接兼容C和C++,对于十几种其他的流行语言(Java, Python, Visual Basic, Delphi,Perl等),OpenGL也有稳定的库,具有与C语言库几乎相同的性能。本次学习采用C++。

二、管线的概念

        使用C++时,程序员编写在CPU上运行的代码包含OpenGL调用,将包含OpenGL调用的C++程序称为C++/OpenGL应用程序。C++/OpenGL程序的一个重要任务就是将程序员的GLSL代码安装到GPU上(GLSL是一种着色器语言,主要运行在GPU上)

        现代3D图形编程会使用管线的概念,下图展示了管线的一般流程。

图源自《计算机图形学编程(使用OpenGL和C++)》作者:V.斯科特.戈登    约翰.克莱维吉  (人民邮电出版社)96页)

         简单来说就是:C++/OpenGL应用发送图形数据到顶点着色器,随着管线处理,最终生成在显示器上显示的像素点。图中阴影部分表示的阶段(顶点着色器、曲面细分着色器、几何着色器、片段着色器)可以用GLSL进行编程。将这些GLSL程序载入这些着色器的过程如下:

        (1)首先使用C++获取GLSL着色器代码,可以从文件中读取,也可以硬编码在字符串中(简单粗暴)

        (2)接下来创建OpenGL空着色器对象并将GLSL着色器代码加载进着色器对象(一般至少要提供顶点着色器和片段着色器阶段的GLSL代码,而曲面细分着色器和几何着色器阶段是可选的)

        (3)最后,用OpenGL命令编译并连接着色器对象,并将它们安装进GPU。

三、第一个C++/OpenGL程序——用顶点着色器和片段着色器绘制一个点(像素大小为30)

  MyWidget.h 

        类MyWidget继承QOpenGLWidget,只需要重写initializeGL(), paintGL(), resizeGL() 便可以绘制图形。定义createShaderProgram()函数完成加载GLSL着色器代码的功能。

#ifndef MYWIDGET_H
#define MYWIDGET_H
#define numVAOs 1
#include<GL/gl.h>
#include<GL/glu.h>
#include<QOpenGLWidget>
#include<QOpenGLFunctions>

//继承OpenGLWidget,重写initializeGL(),paintGL(),resizeGL()三个函数即可绘制OpenGL图元
class MyWidget : public QOpenGLWidget,protected QOpenGLFunctions
{
public:
    MyWidget(QWidget *parent);
    GLuint createShaderProgram();//GLuint相当于C++里的unsinged int类型

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int width, int height) override;
private:
    GLuint renderingProgram;
    GLuint vao[numVAOs];//此程序中用不上
};

 MyWidget.cpp, 每个函数的细节写在注释里了。将GLSL代码采用硬编码的方式写在字符串中。

#include "mywidget.h"

MyWidget::MyWidget(QWidget *parent)
{
    Q_UNUSED(parent);
    
    //设置QopenGL的版本
    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setVersion(4, 3);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyWidget::initializeGL()
{
    initializeOpenGLFunctions();

    //glClearColor()能够指定颜色缓冲区清除后填充的值,此处为红色
    glClearColor(1.0,0.0,0.0,1.0);

    //glClear()用来重置缓冲区时填充的颜色
    glClear(GL_COLOR_BUFFER_BIT);
    
    renderingProgram = createShaderProgram();

}
void MyWidget::paintGL()
{
    //glUseProgram()将含有两个已编译着色器的程序载入OpenGL管线阶段(GPU上)
    //glUseProgram并没有运行着色器,只是将着色器加载进硬件
    glUseProgram(renderingProgram);

    //默认一个点的像素为1,更改点的像素
    glPointSize(30.0f);

    //调用glDrawArrays()来启动管线处理过程
    glDrawArrays(GL_POINTS,0,1);
}
void MyWidget::resizeGL(int width, int height)
{

}

GLuint MyWidget::createShaderProgram()
{
    //顶点着色器,采用GLSL语句写(与C++类似)
    //第一行指明了OpenGL的版本,内置变量gl_Position用来设置顶点在3D空间的坐标位置,并发送至下一管线
    //采用vec4来存储四元组,前三个分别表示X,Y,Z
    const char *vshaderSource =
        "#version 430 \n"
        "void main(void) \n"
        "{ gl_Position = vec4(0.0,0.0,0.0,1.0);}";

    //顶点沿着管线移动到光栅着色器,它们会在这里被转化为像素(片段)位置,最终这些像素(片段)到达片段着色器
    //片段着色器的目的就是将要展示的像素赋予RGB颜色
    //"out"标签表明color变量是输出变量
    //此处vec4前三个元表示RGB颜色,第四个元表示不透明度
    const char *fshaderSource =
        "#version 430 \n"
        "out vec4 color; \n"
        "void main(void) \n"
        "{ color = vec4(0.0,0.0,1.0,1.0); }";

    //调用glCreateShader(parameter)创建类型为parameter的空着色器
    //创建每个着色器对象后会返回一个整数ID作为后面引用它们的序号
    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);//GLuint相当于“unsigned int"
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    //glShaderSource将GLSL代码从字符串载入空着色器对象中
    //四个参数:1、存放着色器的着色器对象,2、着色器源代码中的字符串数量,3、包含源代码的字符串指针,4、
    glShaderSource(vShader,1,&vshaderSource,NULL);
    glShaderSource(fShader,1,&fshaderSource,NULL);

    //glCompileShader编译着色器
    glCompileShader(vShader);
    glCompileShader(fShader);

    //创建程序对象,并存储指向它的整数ID
    //OpenGL的程序对象包含一系列编译过的着色器,使用glAttachShader将着色器加入程序对象
    //之后使用glLinkProgram来请求GLSL编译器来确保它们的兼容性。
    GLuint vfProgram  = glCreateProgram();
    glAttachShader(vfProgram,vShader);
    glAttachShader(vfProgram,fShader);
    glLinkProgram(vfProgram);

    return vfProgram;


}

  mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include"mywidget.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    MyWidget *my_widget;
};
#endif // MAINWINDOW_H

 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建OpenGLWidget对象
    my_widget = new MyWidget(this);
    //将mainWindow界面设置为OpenGLWidget
    setCentralWidget( my_widget );
    //设置界面的大小
    resize( 1000, 800 );
}

MainWindow::~MainWindow()
{
    delete ui;
}

 main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

 运行效果如下:

 四、编译时出现的问题

        在刚开始调用glPointSize()函数时,会出现error: undefined reference to `_imp__glPointSize@4',其原因是未将相应的库文件没有链接,需要在pro文件中添加相关的链接命令。此程序用的构建套件是32位的,故添加如下链接:

LIBS += -lOpengl32 \
                -lglu32 \
                -lglut

本人水平有限,有错误之处请大家斧正!!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#include<BJTU>

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值