1. 说明
在OPenGL中,三维物体模型并非只能渲染单一颜色,还可以通过纹理贴图的方式进行渲染,增强物体模型的渲染效果,本篇文章简单讲解给一个矩形添加纹理图片效果。
效果展示:
纹理数据加载
2. 实现步骤:
第一步:
在myopenglwidget.h文件中添加纹理管控变量,命名为textureStar,并加入对应头文件:
#include <QOpenGLTexture>
private:QOpenGLTexture *textureStar;
第二步:
在myopenglwidget.cpp文件中添加数据,命名为verticesColorTexture,同时引入资源文件,也就是需要弄成纹理的图片资源,并在***initializeGL()***函数中对纹理进行初始化,如下所示:
float verticesColorTexture[] = { //每一行数据的前三个是位置坐标,后三个是颜色值0.5f,0.5f,0.5f,1.0f,0.0f,0.0f,1.0f,1.0f,0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,1.0f,0.0f,-0.5f,-0.5f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,-0.5f,0.5f,0.0f,0.5f,0.5f,0.5f,0.0f,1.0f
};
//添加纹理数据
textureStar = new QOpenGLTexture(QImage(":/img/images/star.jfif").mirrored());
第三步:
顶点数据时需要传输到顶点着色器中,所以需要在顶点着色器中定义一个变量来接收CPU传输过来的各种顶点数据,包括位置坐标数据、颜色数据、纹理坐标数据,在顶点着色器中使用layout关键字进行变量的修饰,方便变量之间的绑定。使用顶点着色器进行数据接收后,还需要将这些数据传输到片段着色器中。在顶点着色器中除了处理位置坐标外,颜色和纹理数据都是用过顶点着色器作为中间传输桥梁,过渡到片段着色器中进行处理和使用,顶点着色器代码如下:
#version 330 core//使用layout修饰的变量,会使用VAO通过特定方式进行数据读取,并绑定到对应变量上
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCord;out vec3 ourColor;
out vec2 texCord;void main()
{gl_Position = vec4(aPos.x,aPos.y,aPos.z,1.0f);ourColor = aColor; //数据传输到片段着色器中进行使用texCord = aTexCord; //数据传输到片段着色器中进行使用
}
第四步:
在片段着色器中定义对应名称的变量来接收顶点着色器中传输过来的数据,此次接收的数据主要时纹理顶点数据,使用texture采样函数,利用纹理坐标在图片上进行采样。代码如下:
#version 330 coreout vec4 FragColor;
in vec3 ourColor;//这个变量暂时没有用到
in vec2 texCord;//接收来自顶点着色器中传输过来的同名数据uniform sampler2D textureStar;//接收外界传输过来的uniform变量,此处指纹理图片数据void main()
{gl_FragColor = texture(textureStar,texCord);//利用纹理坐标texCord在纹理图片textureStar上进行采样
}
第五步:
在***initializeGL()***函数中对VAO进行设置,要设置三个VAO,分别用来提取位置坐标,颜色坐标和纹理坐标,并设置纹理图像数据的值,代码如下:
//告诉VAO怎么在VBO中拿数据(顶点位置数据)
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)0);
//开启第一个VAO
glEnableVertexAttribArray(0);
//告诉VAO怎么在VBO中拿数据(颜色数据)
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(3*sizeof(float)));
//开启第二个VAO
glEnableVertexAttribArray(1);
//告诉VAO怎么在VBO中拿数据(纹理数据)
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(6*sizeof(float)));
//开启第三个VAO
glEnableVertexAttribArray(2)//设置变量textureStar的值
textureStar = new QOpenGLTexture(QImage(":/img/images/water.jpg").mirrored());
shaderProgram.setUniformValue("textureStar",0);
最后,在绘图之前要绑定着色器和纹理,代码如下:
shaderProgram.bind();
textureStar->bind(0);
//在渲染前只需开启对应的VAO即可
glBindVertexArray(VAO);
3. 完整代码:
myopenglwidget.h:
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H#include <QObject>
#include <QWidget>#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{Q_OBJECT
public:enum Shape{None,Rect,Circle,Triangle};explicit MyOpenGLWidget(QWidget *parent = nullptr);void drawShape(Shape shape);void clearGraphic();void setWireFrame(bool wireFrame);protected:virtual void initializeGL() override;virtual void resizeGL(int w, int h) override;virtual void paintGL() override;signals:private:Shape m_shape;QOpenGLShaderProgram shaderProgram;QOpenGLTexture *textureStar;
};#endif // MYOPENGLWIDGET_H
myopenglwidget.cpp:
#include "myopenglwidget.h"unsigned int VBO,VAO;//添加一个索引控制器
unsigned int EBO;float verticesColorTexture[] = { //每一行数据的前三个是位置坐标,后三个是颜色值0.5f,0.5f,0.5f,1.0f,0.0f,0.0f,1.0f,1.0f,0.5f,-0.5f,0.0f,0.0f,1.0f,0.0f,1.0f,0.0f,-0.5f,-0.5f,0.0f,0.0f,0.0f,1.0f,0.0f,0.0f,-0.5f,0.5f,0.0f,0.5f,0.5f,0.5f,0.0f,1.0f
};//添加索引数据
unsigned int indices[]={0,1,3,1,2,3
};MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{}void MyOpenGLWidget::drawShape(MyOpenGLWidget::Shape shape)
{makeCurrent();m_shape = shape;update();doneCurrent();
}void MyOpenGLWidget::clearGraphic()
{makeCurrent();drawShape(MyOpenGLWidget::None);makeCurrent();glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);update();doneCurrent();
}void MyOpenGLWidget::setWireFrame(bool wireFrame)
{makeCurrent();if(wireFrame){glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);}else{glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);}update();doneCurrent();
}void MyOpenGLWidget::initializeGL()
{initializeOpenGLFunctions();//void glGenVertexArrays(GLsizei n, GLuint *arrays)生成顶点数组对象名称// n: 要产生的VAO对象的数量// arrays: 存放产生的VAO对象的名称glGenVertexArrays(1,&VAO);// void glGenBuffers(GLsizei n,GLuint *buffers)生成顶点缓冲对象// n: 要产生的VBO对象的数量// arrays: 存放产生的VBO对象的名称glGenBuffers(1,&VBO);//初始化索引器glGenBuffers(1,&EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof (indices),indices,GL_STATIC_DRAW);//绑定VAO和VBOglBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER,VBO);//在VBO中存入顶点数据glBufferData(GL_ARRAY_BUFFER,sizeof (verticesColorTexture),verticesColorTexture,GL_STATIC_DRAW);//告诉VAO怎么在VBO中拿数据(顶点位置数据)glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)0);//开启第一个VAOglEnableVertexAttribArray(0);//告诉VAO怎么在VBO中拿数据(颜色数据)glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(3*sizeof(float)));//开启第二个VAOglEnableVertexAttribArray(1);//告诉VAO怎么在VBO中拿数据(纹理数据)glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,8*sizeof (float),(void*)(6*sizeof(float)));//开启第三个VAOglEnableVertexAttribArray(2);//用完之后解除绑定(信息已经被记录下来了)glBindBuffer(GL_ARRAY_BUFFER,0);glBindVertexArray(0);//添加着色器shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/Shaders/shape.vert");shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/Shaders/shape.frag");shaderProgram.link();//添加纹理数据textureStar = new QOpenGLTexture(QImage(":/img/images/water.jpg").mirrored());shaderProgram.setUniformValue("textureStar",0);
}void MyOpenGLWidget::resizeGL(int w, int h)
{Q_UNUSED(w);Q_UNUSED(h);}void MyOpenGLWidget::paintGL()
{glClearColor(0.5f,0.9f,0.4f,1.0f);glClear(GL_COLOR_BUFFER_BIT);shaderProgram.bind();textureStar->bind(0);//在渲染前只需开启对应的VAO即可glBindVertexArray(VAO);switch (m_shape) {case Rect:glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,&indices);break;default:break;}}