学习视频链接
OpenGL,Qt实现:1入门篇(已更完)_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1UL411W71w?p=12&vd_source=0471cde1c644648fafd07b54e303c905
目录
一、封装代码读取shader
1.1 编码使用 UTF8
1.2 头文件和对象
1.3 使用对象操作着色器
1.4 新建着色器
二、着色器
2.1 一个 shader 程序的典型结构
2.2 顶点数量
2.3 类型
2.4 输入输出
2.5 代码
2.6 layout(location = ...)
注释掉相关的内容
#include "openglwidget.h"
#include
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
// 创建VBO和VAO对象,并赋予ID
unsigned int VAO, VBO;
// unsigned int shaderProgram;
const char* vertexShaderScource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\n\0";
const char* fragmentShaderScource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
OpenGLWidget::~OpenGLWidget()
{
makeCurrent();
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &VAO);
//glDeleteProgram(shaderProgram);
doneCurrent();
}
void OpenGLWidget::drawShape(OpenGLWidget::Shape shape)
{
m_shape = shape;
update();
}
void OpenGLWidget::setWireframe(bool wireframe)
{
makeCurrent();
if(wireframe)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
update();
doneCurrent();
}
// 初始化的槽函数
// 在第一次调用paintGL()或resizeGL()之前调用一次,然后在小部件被分配新的QGLContext时调用一次
void OpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VBO和VAO对象
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 当前绑定到target的缓冲区对象创建一个新的数据存储
// 如果data不是NULL,则使用来自此指针的数据初始化数据存储
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 告知显卡如何解析缓冲里的属性值
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 开启VAO管理的第一个属性值
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
/*unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderScource, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderScource, NULL);
glCompileShader(fragmentShader);
shaderProgram = glCreateProgram();*/
/*glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);*/
bool success;
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderScource);
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderScource);
success = shaderProgram.link();
if(success)
qDebug() << "ERR:" << shaderProgram.log();
/*glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);*/
}
void OpenGLWidget::resizeGL(int w, int h)
{
}
void OpenGLWidget::paintGL()
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
switch (m_shape) {
case Triangle:
shaderProgram.bind();
//glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
default:
break;
}
}
着色器可以混用
运行结果没问题
对于顶点着色器,输入变量为 顶点属性 (vertex attribute)
#version version_number
in type in_variable_name ;
in type in_variable_name ;
out type out_variable name ;
uniform type uniform_name ;
void main ( )
{
// process input(s) and do some weird graphics stuff ...
// output processed stuff to output variable
out_variable_name = weird_stuff_we_processed;
}
我们能声明的顶点属性数量是有上限的,可以通过下面的代码获取:
nt nrAttributes;
glGetIntegerv(GL_MAX_VERTEX ATTRIBS, &nrAttributes) ;
OpenGL 确保至少有 16 个包含 4 分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性
1、GLSL 中包含 C 等其它语言大部分的默认基础数据类型:
int、float、double、uint 和 bool
2、GLSL 也有两种容器类型:
(1) 向量(Vector)
vecn: the default vector of n floats
bvecn: a vector of n booleans
ivecn: a vector of n integers
uvecn: a vector of n unsigned integers
dvecn: a vector of n double components
(2) 矩阵 (Matrix)
3、重组
向量允许一些有趣而灵活的分量选择方式,叫做重组 (Swizzling):
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
在发送方着色器中声明一个输出
在接收方着色器中声明一个类似的输入
当类型和名字都一致,OpenGL 将把变量链接到一起 (在链接程序对象时完成)
1、输入、输出
2、其他语法
还可以 .xxz 之类的
顶点着色器接收的是一种特殊形式的输入,否则就会效率低下
从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用 location 这一元数据(mietadata) 指定输入变量,这样我们才可以在 CPU 上配置顶点属性。例如: layout (location = 0)。layout 这个的标识,使得我们能把它链接到顶点数据。
可以忽略 layout(location = 0) 标识符,通过在 OpenGL 代码中使用 glGetAttribLocation 查询属性位置值 (Location),或是 glBindAttribLocation 属性位置值 (Location),但是推荐在着色器中设置它们,这样会更容易理解而且节省你 (和OpenGL) 的工作量。
下图中从 0 开始存数据,从 2 开始读数据,程序会报错
现在我们获取到 shader 里面读取数据是从 2 开始,所以我们存放数据也是从 2 开始,这样就能顺利的运行程序了
Uniform:另一种从 CPU 的应用,向 GPU 中的着色器发送数据的方式
uniform 是全局的 (Global),可以被任意着色器程序在任意阶段访问
注意:如果声明了一个 uniform 却没用过,编译器会默移除这个变量,导致最后编译出的版本中并不会包含它,这可能导致几个非常麻烦的错误
OpenGL 在其核心是一个 C 库,所以它不支持类型重载,在函数参数类型不同的时候就要为其定义新的函数:glUniform 是一个典型例子。这个函数有一个特定的后缀,标识设定的 uniform 的类型。可能的后缀有:
代码:
使用定时器,定期改变颜色
把颜色数据加进顶点数据中