之前学习过OpenGL的固定管线,后来了解到现在大部分公司都用可编程管线(programmable pipeline)。所以决定开始研究学习可编程管线。Qt5.*之后对OPenGL的支持更好了。经过一番学习,写了一个基于Qt 的OpenGL例子—— Hello Triangle Demo,在此记录一下加深一下印象,同时让像我一样的新手快速入门。本文假定您对OPenGL 的固定管线有所了解。
一、GLSL(OPenGL Shading Language)初探
现在只需要理解该语言用于编写运行在GPU上程序——“着色器”。下文会介绍如何编写着色器程序。现在先看看一些基本的语法特点。
$1.1类型修饰符
1> uniform:被uniform修饰的变量是在CPU程序中设置好的变量,对于着色器(vertex shader/fragment shader)只可以读。我们的CPU程序通过调用glUniform*()函数来对该变量赋值。例如下文中我们会使用代码来设置与平接头体(frustum)相关的矩阵供着色器使用。
// CPU Application 代码片段
_program->setUniformValue(_matrixUniform, matrix);
// shader代码片段
uniform highp mat4 matrix;
2> attribute:被attribute修饰的变量是只能在vertex shader中使用的变量。不能在fragment shader中声明attribute变量,也不能被fragment shader使用。attribute表示一些顶点的数据比如:顶点坐标,法线,纹理坐标,顶点颜色等。
// CPU 顶点与颜色属性设置;
glVertexAttribPointer(_posAttr, 2, GL_FLOAT, GL_FALSE,0, vertexs);
glVertexAttribPointer(_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);
// vertex shader代码片段,位置(posAttr)、颜色(colAttr)属性;
layout (location = 0) in vec4 posAttr;
attribute lowp vec4 colAttr;
3> varying :被varying修饰的变量是vertex和fragment shader之间做数据传递用的。一般vertex shader修改该变量的值,然后fragment shader使用该变量的值。因此变量在vertex和fragment shader二者之间的声明必须是一致的。CPU 程序不能使用此变量。
// vertex sharder
varying lowp vec4 col;
col = colAttr; // 赋值;
// fragment sharder
varying lowp vec4 col;
gl_FragColor = col; // 读取到gl_FragColor[shader build-in]
二、CPU程序编码流程
这里简单列出Qt5OPenGL的使用方法,具体的OPenGL函数使用方式,将在下一篇与VAO一起陈述,基本流程如下图所示:
1.子类化QOpenGLWidget,重写下边函数。
2.初始化:初始化opengl函数(Qt 要求)——》编写着色器——》编译着色器——》链接着色器——》获取属性等的位置
3. 视口映射。
4.绘制
class QOpenGLShaderProgram;
class OpenGLWidget : public QOpenGLWidget,
protected QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit OpenGLWidget(QWidget *parent = nullptr);
protected:
void initializeGL() override;
void resizeGL(int widget, int height) override;
void paintGL() override;
private:
QOpenGLShaderProgram *_program;
GLuint _posAttr;
GLuint _colAttr;
GLuint _matrixUniform;
};
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.f, 0.f, 0.f,1.f);
const static char *vertexShaderStr =
"#version 330 core\n"
"layout (location = 0) in vec4 posAttr;\n"
"attribute lowp vec4 colAttr;\n"
"varying lowp vec4 col;\n"
"uniform highp mat4 matrix;\n"
"void main()\n"
"{\n"
" col = colAttr;\n"
" gl_Position = matrix * posAttr;\n"
"}\n";
const static char *fragmentShaderStr =
"#version 330 core\n"
"varying lowp vec4 col;\n"
"void main()\n"
"{\n"
"gl_FragColor = col;\n"
"}\n";
QOpenGLShader *vertexShader =
new QOpenGLShader(QOpenGLShader::Vertex, this);
vertexShader->compileSourceCode(vertexShaderStr);
QOpenGLShader *fragmentShader =
new QOpenGLShader(QOpenGLShader::Fragment, this);
fragmentShader->compileSourceCode(fragmentShaderStr);
_program = new QOpenGLShaderProgram(this);
_program->addShader(vertexShader);
_program->addShader(fragmentShader);
_program->link();
_posAttr = _program->attributeLocation("posAttr");
_colAttr = _program->attributeLocation("colAttr");
_matrixUniform = _program->uniformLocation("matrix");
}
void OpenGLWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void OpenGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
static GLfloat vertexs[] = {
0.0f, 0.707f,
-0.5f, -0.5f,
0.5f, -0.5f
};
static GLfloat colors[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
_program->bind();
QMatrix4x4 matrix;
matrix.perspective(60.0f, 4.0f/3.0f, 0.1f, 100.0f);
matrix.translate(0, 0, -2);
_program->setUniformValue(_matrixUniform, matrix);
glVertexAttribPointer(_posAttr, 2, GL_FLOAT, GL_FALSE,0, vertexs);
glVertexAttribPointer(_colAttr, 3, GL_FLOAT, GL_FALSE, 0, colors);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
_program->release();
}
三、运行结果
四、参考文献
Qt document
《OPenGL编程指南 9th edition》
下一篇将记录如何使用VAO以及介绍OPenGL绘制三角形的细节。