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_OBJECTpublic: 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)#endifMainWindow::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可以避免这个情况。