ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OpenGL学习随笔(二)——2022.1.22

2022-01-24 17:58:00  阅读:266  来源: 互联网

标签:GLSL 22 OpenGL void 顶点 MyWidget include 着色器 2022.1


        上回通过用顶点着色器和片段着色器绘制了一个30像素大小的点,这次主要简单介绍一下各个着色器的功能,介绍检测OpenGL和GLSL错误的模板,以及从文件当中读取GLSL源代码的模板,最后绘制一个简单的二维动画。

一、各着色器功能

        顶点着色器:所有的顶点数据都会被传入顶点着色器,顶点们会被一个一个的处理,即顶点着色器会对每个顶点执行一次。对拥有许多顶点的大型复杂规模而言,顶点着色器会执行成百上千甚至上万次,这些执行是并行的。顶点着色器一次只处理一个顶点。

        曲面细分着色器:用以生成大量三角形,通常是网格形式,同时也提供了一些可以以各种方法操作这些三角形的工具。当简单形状上需要很多顶点时,曲面细分着色器能很好的发挥作用。

        几何着色器:顶点着色器赋予程序员一次操作一个顶点的能力,而几何着色器赋予程序员一次处理一个图元的能力(最常用的图元是三角形)。当到达几何着色阶段时,管线肯定已经完成了将顶点组合为三角形的过程(图元组装),接下来几何着色器会让程序员可以同时访问每个三角形的所有顶点。按图元处理有很多用途,如可以让图元变形,还可以删除图元,同时几何着色器也提供了生成额外图元的方法,几何着色器也能在物体上增加表面纹理。

        光栅化:3D模型中的点、三角形、颜色等全部都要展现在一个2D显示器上。这个2D屏幕由光栅(矩阵像素阵列)组成。当3D物体光栅化后,OpenGL将物体中的图元(通常是三角形)转化为片段。片段拥有关于像素的信息。光栅化过程确定了用以显示3个顶点所确定的三角形的所有像素需要绘制的位置。

        片段着色器:片段着色器用于为光栅化的像素指定颜色。片段着色器还提供了其他计算颜色的方式,比如可以基于像素位置决定输出颜色等。片段着色器一次操作一个像素。

二、检测OpenGL和GLSL错误

        编译和运行GLSL代码与普通代码的过程不同,GLSL编译总发生在C++运行时,且GLSL运行在GPU上,因此操作系统不总是能捕获OpenGL运行时的错误(GLSL错误不会导致C++程序崩溃)。这时,我们通常需要将有关GLSL的日志打印出来,其中glGetShaderiv()和glGetProgramiv()用于提供有关编译过的GLSL着色器和程序信息。以下提供了三个用于捕获和显示GLSL错误的模块。

        checkOpenGLError:检查OpenGL错误标志,即是否发生OpenGL错误,既可以用于检测GLSL编译错误,又可以检测OpenGL运行时的错误。

        paintShaderLog:当GLSL编译失败时,显示OpenGL日志内容。

        paintProgramLog:当GLSL链接失败时,显示OpenGL日志内容。

void printShaderLog(GLuint shader)
{
    int len = 0;
    int chWrittn = 0;
    char *log;
    glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&len);
    if(len > 0){
        log = (char*)malloc(len);
        glGetShaderInfoLog(shader,len,&chWrittn,log);
        cout<<"shader Info Log:"<<log<<endl;
        free(log);
    }
}

void printProgramLog(int prog)
{
    int len = 0;
    int chWrittn = 0;
    char *log;
    glGetProgramiv(prog,GL_INFO_LOG_LENGTH,&len);
    if(len > 0){
        log = (char*)malloc(len);
        glGetProgramInfoLog(prog,len,&chWrittn,log);
        cout<<"Program Info Log:"<<log<<endl;
        free(log);
    }

}

bool checkOpenGLError()
{
    bool foundError = false;
    int glErr = glGetError();
    while(glErr != GL_NO_ERROR){
        cout<<"glError:"<<glErr<<endl;
        foundError = true;
        glErr = glGetError();
    }
    return foundError;

}

        应用示例如下:

//捕获编译着色器时的错误
glCompileShader(vShader);//编译着色器
checkOpenGLError();
glGetShaderiv(vShader,GL_COMPILE_STATUS,&vertCompiled);//GLint vertCompiled
if(vertComplied != 1){
    cout<<"vertex compilation failed:"<<endl;
    printShaderLog(vShader);
}

//捕获链接时的着色器错误
glAttachShader(vfProgram,vShader);
glAttachShader(vfProgram,fShader);
glLinkProgram(vfProgram);
checkOpenGLError();
glGetProgramiv(vfProgram,GL_LINK_STATUS,&linked);//GLint linked
if(linked != 1){
    cout<<"linking failed"<<endl;
    printProgramLog(vfProgram);
}
 

三、从文件读取GLSL源代码

        当程序变得复杂时,将GLSL着色器代码内联存储到字符串中就显得不太实际,应该将GLSL代码放到文件(.glsl) 当中,并用读取文件的方式读取GLSL代码。下面提供了一个读取着色器代码的模块。readShaderSource()读取着色器文本并返回一个字符串数组,其中的每个字符串是文件中的一行文本。根据读入的行数确定数组的大小。

QString MyWidget::readShaderSource(const char*filePath)
{
    QString content;
    ifstream fileStream(filePath, ios::in);
    QString line = "";
    while(!fileStream.eof()){
        getLine(fileStream,line);
        content.append(line+"\n");
    }
    fileStream.close();
    return content;
}

   四、简单的三角形动画

        与上一回中绘制一个点的程序类似,只需要稍微修改顶点着色器代码便可以绘制三角形,再添加变量控制动画即可。需要注意的是,使用glUniform1f函数需要引入glew库,但是Qt Creator本身不具备这个库,需要添加库之后在.pro文件中将库连接起来,否则编译不成功。

mywidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H
#define numVAOs 1
#include<GL/glew.h>  //该头文件一定要在最前面
#include<QOpenGLWidget>
#include<QOpenGLFunctions>
#include<QOpenGLBuffer>
#include<QOpenGLShader>
#include<QOpenGLShaderProgram>
#include<GL/glfw3.h>
#include<GL/gl.h>
#include<GL/glu.h>

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

protected:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int width, int height) override;
private:
    GLuint renderingProgram;
    GLuint vao[numVAOs];
    float x = 0.0f;
    float inc = 0.01f;
};

#endif // MYWIDGET_H

  mywidget.cpp 

#include "mywidget.h"
//#include"checkerror.h"
#include<QOpenGLVertexArrayObject>
#include<QString>
#include<iostream>
#include<fstream>
MyWidget::MyWidget(QWidget *parent)
{
    Q_UNUSED(parent);
    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setVersion(4, 3);
    QSurfaceFormat::setDefaultFormat(format);
}

void MyWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(1.0,0.0,0.0,1.0);//设置背景色为红色
    glClear(GL_COLOR_BUFFER_BIT);
    renderingProgram = createShaderProgram();
    //glGenVertexArrays(numVAOs,vao);
    //glBindVertexArray(vao[0]);
}
void MyWidget::paintGL()
{
    glClear(GL_DEPTH_BOUNDS_EXT);
    glClearColor(1.0,0.0,0.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(renderingProgram);
    
    //每次更新偏移量
    x += inc;
    if(x > 1.0f) inc = -0.02f;
    if(x < -1.0f) inc = 0.02f;
    //获取顶点着色器中uniform类全局变量offset的位置
    GLuint offsetLoc = glGetUniformLocation(renderingProgram,"offset");
    //将x的值赋给uniform类变量offset
    glUniform1f(offsetLoc,x);
    
    //将3个顶点绘制为三角形
    glDrawArrays(GL_TRIANGLES,0,3);
}
void MyWidget::resizeGL(int width, int height)
{

}

GLuint MyWidget::createShaderProgram()
{
    //修改顶点着色器使之变为三角形
    //使用unifom类的全局变量offset控制动画
    const char *vshaderSource =
        "#version 430 \n"
         "uniform float offset; \n"
        "void main(void) \n"
        "{ if(gl_VertexID ==0) gl_Position = vec4(0.25+offset , -0.25, 0.0, 1.0);\n"
        "else if(gl_VertexID == 1) gl_Position = vec4(-0.25+offset ,-0.25,0.0,1.0);\n"
        "else gl_Position = vec4(0.0 +offset,0.25,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;


}

void MyWidget::animate()
{
    this->update();//调用update时会自动调用PaintGL函数重绘
}

 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"
#include<QTimer>
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 );
    
    //设置计时器并连接槽函数
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, my_widget, &MyWidget::animate);
    timer->start(10);//设置重绘的时间间隔
    
}

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

运行结果:

标签:GLSL,22,OpenGL,void,顶点,MyWidget,include,着色器,2022.1
来源: https://blog.csdn.net/weixin_59876363/article/details/122647754

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有