Qt提供QOpenGLShaderProgram这种类来完成GLSL操作,同时也支持原生的OpenglAPI。在接下来的两篇博客里,我将分别介绍如何用QOpenGLShaderProgram来贴出纹理,以及如何用原生的OpenglAPI贴出纹理。这篇博客介绍的是如何利用原生的OpenglAPI贴纹理:不论是顶点数据的传值,还是纹理的传值,都在GLSL里留下显式的接口变量。
pro文件:
#-------------------------------------------------
#
# Project created by QtCreator 2018-02-09T21:30:32
#
#-------------------------------------------------
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = shader1
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
LIBS += -lopengl32 -lGLU32
h文件:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
GLuint m_uiTexID;
GLuint m_uiProgram;
GLuint m_uiVertShader;
GLuint m_uiFragShader;
GLuint m_uiMatrixHandle;
GLuint m_uiVerticesHandle;
GLfloat * m_pVertices;
unsigned char * pLoadTex(char * Image, unsigned long & bWidth, unsigned long & bHeight);
protected:
void initializeGL();
void paintGL();
void resizeGL(int, int);
};
#endif // MAINWINDOW_H
cpp文件:
#include "mainwindow.h"
#include <QDir>
#include <gl/GLU.H>
#include <QMatrix4x4>
#include <QDebug>
#if !defined(DEG2RAD)
#define DEG2RAD (3.1415926 / 180)
#endif
MainWindow::MainWindow(QWidget *parent)
: QOpenGLWidget(parent)
{
}
MainWindow::~MainWindow()
{
glDeleteTextures(1, &m_uiTexID);
delete [] m_pVertices;
}
void MainWindow::initializeGL()
{
initializeOpenGLFunctions();
//载入纹理
unsigned long bWidth = 0;
unsigned long bHeight = 0;
QString qstrPath = QDir::currentPath();
qstrPath += "/earth.bmp";
unsigned char * pData = pLoadTex(qstrPath.toLatin1().data(), bWidth, bHeight);
//准备纹理
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &m_uiTexID);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_uiTexID);
//这下面的两句是必要的,否则在绘制区的像素数和纹理的像素数不等时,图案不能正常显示
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, bWidth, bHeight,
GL_BGR_EXT, GL_UNSIGNED_BYTE, pData);
glDisable(GL_TEXTURE_2D);
free(pData);
const char * pSrcVert= "#version 300 es\n"
"in vec3 pos;\n"
"out vec2 texCoord;\n"
"uniform mat4 mat4MVP;\n"
"void main()\n"
"{\n"
" gl_Position = mat4MVP * vec4(pos, 1.0);\n"
" texCoord = pos.xy;\n"
"}\n";
m_uiVertShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(m_uiVertShader, 1, (const char **)&pSrcVert, NULL);
glCompileShader(m_uiVertShader);
GLint compiled = 0;
glGetShaderiv(m_uiVertShader,GL_COMPILE_STATUS,&compiled);
if (!compiled)
{
// Get the info log for compilation failure
GLint infoLen = 0;
glGetShaderiv(m_uiVertShader,GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen)
{
char* buf = (char*) malloc(infoLen);
if (buf)
{
glGetShaderInfoLog(m_uiVertShader, infoLen, NULL, buf);
qDebug()<<"Could not compile shader %s:"<< buf;
free(buf);
}
}
// Delete the shader program
glDeleteShader(m_uiVertShader);
m_uiVertShader = 0;
}
const char * pSrcFrag= "#version 300 es\n"
"out vec4 color;\n"
"in vec2 texCoord;\n"
"uniform sampler2D Tex\n;"
"void main()\n"
"{\n"
" color = texture(Tex, texCoord);\n"
"}\n";
m_uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(m_uiFragShader, 1, (const char **)&pSrcFrag, NULL);
glCompileShader(m_uiFragShader);
compiled = 0;
glGetShaderiv(m_uiFragShader,GL_COMPILE_STATUS,&compiled);
if (!compiled)
{
// Get the info log for compilation failure
GLint infoLen = 0;
glGetShaderiv(m_uiFragShader,GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen)
{
char* buf = (char*) malloc(infoLen);
if (buf)
{
glGetShaderInfoLog(m_uiFragShader, infoLen, NULL, buf);
qDebug()<<"Could not compile frag shader %s:"<< buf;
free(buf);
}
}
// Delete the shader program
glDeleteShader(m_uiFragShader);
m_uiFragShader = 0;
}
m_uiProgram = glCreateProgram();
glAttachShader(m_uiProgram, m_uiVertShader);
glAttachShader(m_uiProgram, m_uiFragShader);
glLinkProgram(m_uiProgram);
GLint status;
//获取链接状态
glGetProgramiv(m_uiProgram, GL_LINK_STATUS, &status);
//如果链接失败,打印日志信息
if (status == GL_FALSE)
{
GLint infoLogLength;
glGetProgramiv(m_uiProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar *strInfoLog = new GLchar[infoLogLength + 1];
glGetProgramInfoLog(m_uiProgram, infoLogLength, NULL, strInfoLog);
qDebug()<<"Linker failure: %s\n"<<strInfoLog;
delete[] strInfoLog;
}
//给顶点赋值
GLfloat arrVertices[18] = {0.0, 1.0, 0.0,
0.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 0.0, 0.0,
1.0, 1.0, 0.0,
0.0, 1.0, 0.0};
m_pVertices = new GLfloat[18];
memcpy(m_pVertices, arrVertices, 18 * sizeof(GLfloat));
m_uiVerticesHandle = glGetAttribLocation(m_uiProgram, "pos");
glUseProgram(m_uiProgram);
//给纹理赋值的步骤可以放在initializeGL()里面(glUseProgram之后),也可以放在paintGL()里面
glUniform1i(glGetUniformLocation(m_uiProgram, "Tex"), /*m_uiTexID*/0);
glEnable(GL_DEPTH_TEST);
glClearColor(0,0,0,1);
}
void MainWindow::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
//给矩阵赋值的步骤可以放在initializeGL()里面(glUseProgram之后),也可以放在paintGL()里面
m_uiMatrixHandle = glGetUniformLocation(m_uiProgram, "mat4MVP");
QMatrix4x4 m;
//m.lookAt(eye, center, up);
m.translate(-0.5f, 0.0f, 0.0f);
m.ortho(-1.0f, +1.0f, -1.0f, 1.0f, -4.0f, 15.0f);
glUniformMatrix4fv(m_uiMatrixHandle, 1, GL_FALSE, m.data());
//绘制过程。绘制过程中,必须保持glBindTexture和glEnable(GL_TEXTURE_2D)围在最外围,否则纹理画不出来
glBindTexture(GL_TEXTURE_2D, m_uiTexID);
glEnable(GL_TEXTURE_2D);
//给顶点 赋值
glEnableVertexAttribArray(m_uiVerticesHandle);
// glVertexAttribPointer(m_uiVerticesHandle, 3/*坐标值有3个分量*/, GL_FLOAT,
// GL_FALSE/*不要归一化到0,1区间*/, 0/*stride*/, (GLvoid *)m_pVertices);
glVertexAttribPointer(m_uiVerticesHandle, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid *)m_pVertices);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(m_uiVerticesHandle);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
void MainWindow::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
/*glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0, 50.0, -50.0, 50.0, -50.0, 50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(60,0,0,0,0,0,0,0,1);*/
}
unsigned char * MainWindow::pLoadTex(char * Image, unsigned long & bWidth, unsigned long & bHeight)
{
FILE* img = NULL;
img = fopen(Image, "rb");
DWORD size = 0;
fseek(img,18,SEEK_SET);
fread(&bWidth,4,1,img);
fread(&bHeight,4,1,img);
fseek(img,0,SEEK_END);
size = ftell(img) - 54;
unsigned char *data = (unsigned char*)malloc(size);
fseek(img,54,SEEK_SET); // image data
fread(data,size,1,img);
fclose(img);
return data;
}
结果:
怪异的结果--一开始显示的是黑屏。但是将窗口最小化之后,再弹出就看到纹理了。现在还不知道原因,但是在下一篇博客里,利用QOpenGLShaderProgram可以避免这个情况。