当前位置: 首页 > news >正文

【OpenGL】texture 纹理

参考文章:https://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-5-a-textured-cube/

一、UV坐标

1.1 关于UV坐标

给模型贴纹理时,我们需要通过UV坐标来告诉OpenGL用哪块图像填充三角形。

每个顶点除了位置坐标外还有两个浮点数坐标:U和V。这两个坐标用于访问纹理,如下图所示:

在这里插入图片描述

注意观察纹理是怎样在三角形上扭曲的。

1.2 加载bmp图片

我们可以使用QT库自带的QImage来读取bmp图片,而不需要自己手动解析

假设路径下存在.bmp格式的图像,可以这样加载:

QString bmp_path = "/Users/liuhang/CLionProjects/opengl-learning/resource/uvtemplate-cube.bmp";  
QImage texture_image(bmp_path);  
if (texture_image.isNull()) {  std::cerr << bmp_path .toStdString() << "路径错误或文件损坏!" << std::endl;  return 0;  
}

这里需要注意的是,QT的坐标系世纪上与opengl坐标系是镜像的关系,例如openGL坐标系的原点上左下角,但是QT实际上是左上角,因此可以使用mirrored函数进行反转,并且转换格式到8位的RGB格式

// 转换为RGB格式并垂直翻转(OpenGL坐标原点在左下角)  
QImage gl_image = texture_image.mirrored().convertToFormat(QImage::Format_RGB888);

二、在openGL中使用纹理

2.1 片段着色器

先来看看片段着色器。大部分代码一目了然:

#version 330 core// Interpolated values from the vertex shaders
in vec2 UV;// Ouput data
out vec3 color;// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;void main(){// Output color = color of the texture at the specified UVcolor = texture( myTextureSampler, UV ).rgb;
}

注意三点:

  • 片段着色器需要UV坐标。看似合情合理。
  • 同时也需要一个”Sampler2D”来获知要加载哪一个纹理(同一个着色器中可以访问多个纹理)
  • 最后一点,用texture()访问纹理,该方法返回一个(R,G,B,A)的vec4变量。马上就会了解到分量A。

2.2 顶点着色器

顶点着色器也很简单,只需把UV坐标传给片段着色器:

#version 330 core// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;// Output data ; will be interpolated for each fragment.
out vec2 UV;// Values that stay constant for the whole mesh.
uniform mat4 MVP;void main(){// Output position of the vertex, in clip space : MVP * positiongl_Position =  MVP * vec4(vertexPosition_modelspace,1);// UV of the vertex. No special space for this one.UV = vertexUV;
}

三、给正方体添加纹理

还记得第四课中的”layout(location = 1) in vec2 vertexUV”吗?我们得在这儿把相同的事情再做一遍,但这次的缓冲中放的不是(R,G,B)三元组,而是(U,V)数对。

// Two UV coordinatesfor each vertex. They were created with Blender. You'll learn shortly how to do this yourself.
static const GLfloat g_uv_buffer_data[] = {0.000059f, 1.0f-0.000004f,0.000103f, 1.0f-0.336048f,0.335973f, 1.0f-0.335903f,1.000023f, 1.0f-0.000013f,0.667979f, 1.0f-0.335851f,0.999958f, 1.0f-0.336064f,0.667979f, 1.0f-0.335851f,0.336024f, 1.0f-0.671877f,0.667969f, 1.0f-0.671889f,1.000023f, 1.0f-0.000013f,0.668104f, 1.0f-0.000013f,0.667979f, 1.0f-0.335851f,0.000059f, 1.0f-0.000004f,0.335973f, 1.0f-0.335903f,0.336098f, 1.0f-0.000071f,0.667979f, 1.0f-0.335851f,0.335973f, 1.0f-0.335903f,0.336024f, 1.0f-0.671877f,1.000004f, 1.0f-0.671847f,0.999958f, 1.0f-0.336064f,0.667979f, 1.0f-0.335851f,0.668104f, 1.0f-0.000013f,0.335973f, 1.0f-0.335903f,0.667979f, 1.0f-0.335851f,0.335973f, 1.0f-0.335903f,0.668104f, 1.0f-0.000013f,0.336098f, 1.0f-0.000071f,0.000103f, 1.0f-0.336048f,0.000004f, 1.0f-0.671870f,0.336024f, 1.0f-0.671877f,0.000103f, 1.0f-0.336048f,0.336024f, 1.0f-0.671877f,0.335973f, 1.0f-0.335903f,0.667969f, 1.0f-0.671889f,1.000004f, 1.0f-0.671847f,0.667979f, 1.0f-0.335851f
};

上述UV坐标对应于下面的模型:

在这里插入图片描述

其余的就很清楚了。创建一个缓冲、绑定、填充、配置,像往常一样绘制顶点缓冲对象。要注意把glVertexAttribPointer的第二个参数(大小)3改成2。

具体来说,在initilizeGL函数中添加下面的代码:

//===============顶点纹理数据vbo===============  
glGenBuffers(1,&texture_buffer_id);  
glBindBuffer(GL_ARRAY_BUFFER,texture_buffer_id);  
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_texture_data_triangle),vertex_texture_data_triangle,GL_STATIC_DRAW);  
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_texture_data_cube),vertex_texture_data_cube,GL_STATIC_DRAW);

paintGL中重新绑定VBO,然后激活、描述 layout = 1的uv坐标,传入uv数据

//加载layout = 1  
glEnableVertexAttribArray(1);  
glBindBuffer(GL_ARRAY_BUFFER,texture_buffer_id);  
glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,nullptr);

我们从bmp图片加载纹理,具体的加载逻辑代码如下,关于纹理过滤这一部分,下面讲解:

GLuint MyGLWidget::loadTexture() {  
//  QString bmp_path = "/Users/liuhang/CLionProjects/opengl-learning/resource/uvtemplate.bmp";  QString bmp_path = "/Users/liuhang/CLionProjects/opengl-learning/resource/uvtemplate-cube.bmp";  QImage texture_image(bmp_path);  if (texture_image.isNull()) {  std::cerr << bmp_path .toStdString() << "路径错误或文件损坏!" << std::endl;  return 0;  }  // 转换为RGB格式并垂直翻转(OpenGL坐标原点在左下角)  QImage gl_image = texture_image.mirrored().convertToFormat(QImage::Format_RGB888);  GLuint textureID;  glGenTextures(1, &textureID);  glBindTexture(GL_TEXTURE_2D, textureID);  // 上传纹理数据  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, gl_image.width(), gl_image.height(), 0,  GL_RGB, GL_UNSIGNED_BYTE, gl_image.bits());  // 设置纹理参数  //===============设置纹理过滤===============  #if 0  //GL_NEAREST:最近像素点采样  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);#endif  #if 0  //GL_LINEAR:线性过滤  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);#endif  #if 1  //GL_MIPMAP_NEAREST: mipmap  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);  //生成mipmap,多加这一行  glGenerateMipmap(GL_TEXTURE_2D);  
#endif  //解绑  glBindTexture(GL_TEXTURE_2D, 0); // 解绑  return textureID;  
}

结果如下:
在这里插入图片描述

放大后,可以发现边缘像素非常粗糙,这通常和纹理过滤有关,下面我们来介绍
在这里插入图片描述

四、纹理过滤和mipmap

正如在上面截图中看到的,纹理质量不是很好。这是因为在loadBMP_custom函数中,有如下两行代码:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

这意味着在片段着色器中,texture()将直接提取位于(U,V)坐标的纹素(texel)。

在这里插入图片描述

4.1 线性过滤(Linear filtering)

若采用线性过滤。texture()会查看周围的纹素,然后根据UV坐标距离各纹素中心的距离来混合颜色。这就避免了前面看到的锯齿状边缘。

在这里插入图片描述

线性过滤可以显著改善纹理质量,应用的也很多。但若想获得更高质量的纹理,可以采用各向异性过滤,不过速度有些慢。

4.2 各向异性过滤(Anisotropic filtering)

这种方法逼近了真正片断中的纹素区块。例如下图中稍稍旋转了的纹理,各向异性过滤将沿蓝色矩形框的主方向,作一定数量的采样(即所谓的”各向异性层级”),计算出其内的颜色。

在这里插入图片描述

4.3 Mipmaps

线性过滤和各向异性过滤都存在一个共同的问题。那就是如果从远处观察纹理,只对4个纹素作混合显得不够。实际上,如果3D模型位于很远的地方,屏幕上只看得见一个片断(像素),那计算平均值得出最终颜色值时,图像所有的纹素都应该考虑在内。很显然,这种做法没有考虑性能问题。撇开两种过滤方法不谈,这里要介绍的是mipmap技术:

在这里插入图片描述

  • 一开始,把图像缩小到原来的1/2,然后依次缩小,直到图像只有1x1大小(应该是图像所有纹素的平均值)
  • 绘制模型时,根据纹素大小选择合适的mipmap。
  • 可以选用nearest、linear、anisotropic等任意一种滤波方式来对mipmap采样。
  • 要想效果更好,可以对两个mipmap采样然后混合,得出结果。

好在这个比较简单,OpenGL都帮我们做好了,只需一个简单的调用:

// When MAGnifying the image (no bigger mipmap available), use LINEAR filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// When MINifying the image, use a LINEAR blend of two mipmaps, each filtered LINEARLY too
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// Generate mipmaps, by the way.
glGenerateMipmap(GL_TEXTURE_2D);

五、压缩纹理

学到这儿,您可能会问:那JPEG格式的纹理又该怎样加载呢?

简答:用不着考虑这些文件格式,您还有更好的选择。

5.1 创建压缩纹理

  • 下载The Compressonator,一款ATI工具
  • 用它加载一个二次幂纹理
  • 将其压缩成DXT1、DXT3或DXT5格式(这些格式之间的差别请参考Wikipedia):

在这里插入图片描述

  • 生成mipmap,这样就不用在运行时生成mipmap了。
  • 导出为.DDS文件。

至此,图像已压缩为可被GPU直接使用的格式。在着色中随时调用texture()均可以实时解压。这一过程看似很慢,但由于它节省了很多内存空间,传输的数据量就少了。传输内存数据开销很大;纹理解压缩却几乎不耗时(有专门的硬件负责此事)。一般情况下,采用压缩纹理可使性能提升20%。

5.2 使用压缩纹理

来看看怎样加载压缩纹理。这和加载BMP的代码很相似,只不过文件头的结构不一样:

GLuint loadDDS(const char * imagepath){  unsigned char header[124];  FILE *fp;  /* try to open the file */  fp = fopen(imagepath, "rb");  if (fp == NULL){  printf("%s could not be opened. Are you in the right directory ? Don't forget to read the FAQ !\n", imagepath); getchar();  return 0;  }  /* verify the type of file */  char filecode[4];  fread(filecode, 1, 4, fp);  if (strncmp(filecode, "DDS ", 4) != 0) {  fclose(fp);  return 0;  }  /* get the surface desc */  fread(&header, 124, 1, fp);  unsigned int height      = *(unsigned int*)&(header[8 ]);  unsigned int width       = *(unsigned int*)&(header[12]);  unsigned int linearSize  = *(unsigned int*)&(header[16]);  unsigned int mipMapCount = *(unsigned int*)&(header[24]);  unsigned int fourCC      = *(unsigned int*)&(header[80]);  unsigned char * buffer;  unsigned int bufsize;  /* how big is it going to be including all mipmaps? */  bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;  buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));  fread(buffer, 1, bufsize, fp);  /* close the file pointer */  fclose(fp);  unsigned int components  = (fourCC == FOURCC_DXT1) ? 3 : 4;  unsigned int format;  switch(fourCC)  {  case FOURCC_DXT1:  format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;  break;  case FOURCC_DXT3:  format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;  break;  case FOURCC_DXT5:  format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;  break;  default:  free(buffer);  return 0;  }  // Create one OpenGL texture  GLuint textureID;  glGenTextures(1, &textureID);  // "Bind" the newly created texture : all future texture functions will modify this texture  glBindTexture(GL_TEXTURE_2D, textureID);  glPixelStorei(GL_UNPACK_ALIGNMENT,1);  unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;  unsigned int offset = 0;  /* load the mipmaps */  for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)  {  unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;  glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,  0, size, buffer + offset);  offset += size;  width  /= 2;  height /= 2;  // Deal with Non-Power-Of-Two textures. This code is not included in the webpage to reduce clutter.  if(width < 1) width = 1;  if(height < 1) height = 1;  }  free(buffer);  return textureID;  }

加载压缩纹理得到的纹理,和之前一样,在创建、绑定好的纹理单元上传即可

六、完整代码

my_glwidget.h

//  
// Created by liuhang on 2025/9/16.  
//  #ifndef OPENGL_LEARNING_MY_GLWIDGET_H  
#define OPENGL_LEARNING_MY_GLWIDGET_H  #include<QOpenGLWidget>  
#include<QOpenGLFunctions>  
#include<QMatrix4x4>  
#include<QTimer>  class MyGLWidget : public QOpenGLWidget,protected QOpenGLFunctions  
{  Q_OBJECT  
public:  explicit MyGLWidget(QWidget* parent = nullptr);  ~MyGLWidget() override;  protected:  void initializeGL() override;  void paintGL() override;  void resizeGL(int w,int h) override;  void doMVP();  
private:  void loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path);  GLuint loadTexture();  private:  GLuint vertex_array_id;  GLuint vertex_buffer_id;  GLuint texture_buffer_id;  GLuint vertex_shader_id;  GLuint fragment_shader_id;  GLuint shader_program_id;  GLint uniform_model_matrix_location;  GLint uniform_view_matrix_location;  GLint uniform_projection_matrix_location;  GLint uniform_texture_sampler_location;  QImage texture_image;  GLuint texture_id;  //MVP矩阵  QVector3D camera_pos = {4,4,2};  QVector3D look_dir = {0,0,0};  QVector3D up_dir = {0,1,0};  float angle = 45;  float aspect;  //距离相机的位置,渲染范围:0.1-100  float near_plane = 0.1f;  float far_plane = 100.f;  
};  #endif //OPENGL_LEARNING_MY_GLWIDGET_H

my_glwidget.cpp

//  
// Created by liuhang on 2025/9/16.  
//  #include "my_glwidget.h"  
#include<string>  
#include<fstream>  
#include<sstream>  
#include<iostream>  
#include<filesystem>  
#include<QThread>  
#include<QImage>  
#include<QCursor>  // Our vertices. Three consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.  
// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices  
static const GLfloat vertex_buffer_data_cube[] = {  -1.0f,-1.0f,-1.0f, // triangle 1 : begin  -1.0f,-1.0f, 1.0f,  -1.0f, 1.0f, 1.0f, // triangle 1 : end  1.0f, 1.0f,-1.0f, // triangle 2 : begin  -1.0f,-1.0f,-1.0f,  -1.0f, 1.0f,-1.0f, // triangle 2 : end  1.0f,-1.0f, 1.0f,  -1.0f,-1.0f,-1.0f,  1.0f,-1.0f,-1.0f,  1.0f, 1.0f,-1.0f,  1.0f,-1.0f,-1.0f,  -1.0f,-1.0f,-1.0f,  -1.0f,-1.0f,-1.0f,  -1.0f, 1.0f, 1.0f,  -1.0f, 1.0f,-1.0f,  1.0f,-1.0f, 1.0f,  -1.0f,-1.0f, 1.0f,  -1.0f,-1.0f,-1.0f,  -1.0f, 1.0f, 1.0f,  -1.0f,-1.0f, 1.0f,  1.0f,-1.0f, 1.0f,  1.0f, 1.0f, 1.0f,  1.0f,-1.0f,-1.0f,  1.0f, 1.0f,-1.0f,  1.0f,-1.0f,-1.0f,  1.0f, 1.0f, 1.0f,  1.0f,-1.0f, 1.0f,  1.0f, 1.0f, 1.0f,  1.0f, 1.0f,-1.0f,  -1.0f, 1.0f,-1.0f,  1.0f, 1.0f, 1.0f,  -1.0f, 1.0f,-1.0f,  -1.0f, 1.0f, 1.0f,  1.0f, 1.0f, 1.0f,  -1.0f, 1.0f, 1.0f,  1.0f,-1.0f, 1.0f  
};  static const GLfloat vertex_texture_data_cube[] = {  0.000059f, 1.0f-0.000004f,  0.000103f, 1.0f-0.336048f,  0.335973f, 1.0f-0.335903f,  1.000023f, 1.0f-0.000013f,  0.667979f, 1.0f-0.335851f,  0.999958f, 1.0f-0.336064f,  0.667979f, 1.0f-0.335851f,  0.336024f, 1.0f-0.671877f,  0.667969f, 1.0f-0.671889f,  1.000023f, 1.0f-0.000013f,  0.668104f, 1.0f-0.000013f,  0.667979f, 1.0f-0.335851f,  0.000059f, 1.0f-0.000004f,  0.335973f, 1.0f-0.335903f,  0.336098f, 1.0f-0.000071f,  0.667979f, 1.0f-0.335851f,  0.335973f, 1.0f-0.335903f,  0.336024f, 1.0f-0.671877f,  1.000004f, 1.0f-0.671847f,  0.999958f, 1.0f-0.336064f,  0.667979f, 1.0f-0.335851f,  0.668104f, 1.0f-0.000013f,  0.335973f, 1.0f-0.335903f,  0.667979f, 1.0f-0.335851f,  0.335973f, 1.0f-0.335903f,  0.668104f, 1.0f-0.000013f,  0.336098f, 1.0f-0.000071f,  0.000103f, 1.0f-0.336048f,  0.000004f, 1.0f-0.671870f,  0.336024f, 1.0f-0.671877f,  0.000103f, 1.0f-0.336048f,  0.336024f, 1.0f-0.671877f,  0.335973f, 1.0f-0.335903f,  0.667969f, 1.0f-0.671889f,  1.000004f, 1.0f-0.671847f,  0.667979f, 1.0f-0.335851f  
};  static const float vertex_buffer_data_triangle[] ={  -1.0f, -1.0f, 0.0f,  1.0f, -1.0f, 0.0f,  0.0f,  1.0f, 0.0f  
};  static const GLfloat vertex_texture_data_triangle[] = {  0.0f,0.0f,  1.0f,0.0f,  0.5,1.0f  
};  MyGLWidget::MyGLWidget(QWidget *parent): QOpenGLWidget(parent)  
{  
#if 0  timer.setInterval(1);  connect(&timer,&QTimer::timeout,this,[this](){        static float count = 0;        count+= 0.01f;        global_i = std::fabs(sin(count));  this->update();  timer.setInterval(1);        timer.start();    });  
#endif  #if 0  timer.setInterval(1);  connect(&timer,&QTimer::timeout,this,[this](){        global_i+= 1;        if(fabs(global_i - 100) < 0.1f){            global_i = 0;        }        this->update();  timer.setInterval(1);        timer.start();    });  timer.start();#endif  aspect = this->width() * 1.0f / this->height();  
}  MyGLWidget::~MyGLWidget()  {  makeCurrent();  //vao  glDeleteVertexArrays(1,&vertex_array_id);  //坐标顶点vbo  glDeleteBuffers(1,&vertex_buffer_id);  //纹理顶点vbo  glDeleteBuffers(1,&texture_buffer_id);  //shader_program  glDeleteShader(shader_program_id);  //纹理单元  glDeleteTextures(0,&texture_id);  doneCurrent();  
}  void MyGLWidget::initializeGL() {  initializeOpenGLFunctions();  glClearColor(0.2f,0.3f,0.3f,1.0f);  //启动深度测试  glEnable(GL_DEPTH_TEST);  // Accept fragment if it closer to the camera than the former one  glDepthFunc(GL_LESS);  //加载着色器  loadShader("/Users/liuhang/CLionProjects/opengl-learning/opengl1-5-cube-texture/shader/shader.vert",  "/Users/liuhang/CLionProjects/opengl-learning/opengl1-5-cube-texture/shader/shader.frag");  //===============vao===============  glGenVertexArrays(1,&vertex_array_id);  glBindVertexArray(vertex_array_id);  //===============顶点坐标数据vbo===============  glGenBuffers(1,&vertex_buffer_id);  glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer_id);  
//    glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_buffer_data_triangle),vertex_buffer_data_triangle,GL_STATIC_DRAW);  glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_buffer_data_cube),vertex_buffer_data_cube,GL_STATIC_DRAW);  //===============顶点纹理数据vbo===============  glGenBuffers(1,&texture_buffer_id);  glBindBuffer(GL_ARRAY_BUFFER,texture_buffer_id);  glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_texture_data_triangle),vertex_texture_data_triangle,GL_STATIC_DRAW);  glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_texture_data_cube),vertex_texture_data_cube,GL_STATIC_DRAW);  //===============纹理单元===============  texture_id = loadTexture();  uniform_texture_sampler_location = glGetUniformLocation(shader_program_id,"texture_sampler");  //解绑vao  glBindVertexArray(0);  }  void MyGLWidget::paintGL() {  //清除颜色缓存和深度缓存  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  glBindVertexArray(vertex_array_id);  //使用着色器程序  glUseProgram(shader_program_id);  //上传MVP矩阵  doMVP();  //绑定vao  glBindVertexArray(vertex_array_id);  //加载layout = 0  glEnableVertexAttribArray(0);  glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer_id);  glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,nullptr);  //加载layout = 1  glEnableVertexAttribArray(1);  glBindBuffer(GL_ARRAY_BUFFER,texture_buffer_id);  glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,0,nullptr);  //绑定纹理单元0  glActiveTexture(GL_TEXTURE0);  glBindTexture(GL_TEXTURE_2D,texture_id);  glUniform1i(uniform_texture_sampler_location, 0);  //绘制  
//    glDrawArrays(GL_TRIANGLES,0,3);  glDrawArrays(GL_TRIANGLES,0,3*12);  //解绑layout = 0  glDisableVertexAttribArray(0);  //解绑layout = 1  glDisableVertexAttribArray(1);  
}  void MyGLWidget::resizeGL(int w, int h) {  glViewport(0, 0, w, h);  
}  void MyGLWidget::loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path) {  //创建顶点着色器  vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);  //读取shader.vert  std::string vertex_shader_code;  std::ifstream vertex_shader_stream(vertex_shader_path,std::ios::in);  if(vertex_shader_stream.is_open()){  std::stringstream ss;  ss << vertex_shader_stream.rdbuf();  vertex_shader_code = ss.str();  vertex_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read vertex_shader fail!" << std::endl;  return;  }  //加载vertex着色器程序代码  const char* vertex_shader_code_pointer = vertex_shader_code.c_str();  glShaderSource(vertex_shader_id,1,&vertex_shader_code_pointer,nullptr);  //编译vertex shader  glCompileShader(vertex_shader_id);  //查看vertex编译结果  GLint Result = GL_FALSE;  int infoLogLength;  glGetShaderiv(vertex_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(vertex_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> vertex_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(vertex_shader_id, infoLogLength, nullptr, &vertex_shader_error_message[0]);  std::cout << "vertex shader:" << &vertex_shader_error_message[0] << std::endl;  }  //创建片段着色器  fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);  //读取shader.frag  std::string fragment_shader_code;  std::ifstream fragment_shader_stream(fragment_shader_path,std::ios::in);  if(fragment_shader_stream.is_open()){  std::stringstream ss;  ss << fragment_shader_stream.rdbuf();  fragment_shader_code = ss.str();  fragment_shader_stream.close();  }  else{  std::cout << "current path = " << std::filesystem::current_path() << std::endl;  if (!std::filesystem::exists(vertex_shader_path)) {  std::cerr << "File not found: " << vertex_shader_path << std::endl;  }  std::cerr << "read fragment_shader fail!" << std::endl;  }  //加载fragment着色器程序代码  const char* fragment_shader_code_pointer = fragment_shader_code.c_str();  glShaderSource(fragment_shader_id,1,&fragment_shader_code_pointer,nullptr);  //编译fragment shader  glCompileShader(fragment_shader_id);  //查看fragment编译结果  glGetShaderiv(fragment_shader_id,GL_COMPILE_STATUS,&Result);  glGetShaderiv(fragment_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char> fragment_shader_error_message(infoLogLength+1);  glGetShaderInfoLog(fragment_shader_id, infoLogLength, nullptr, &fragment_shader_error_message[0]);  std::cout << "fragment shader compile error:" << &fragment_shader_error_message[0] << std::endl;  }  //创建着色器程序  shader_program_id = glCreateProgram();  //附加着色器到程序  glAttachShader(shader_program_id,vertex_shader_id);  glAttachShader(shader_program_id,fragment_shader_id);  //链接shader  glLinkProgram(shader_program_id);  //检查程序链接结果  glGetProgramiv(shader_program_id,GL_LINK_STATUS,&Result);  glGetProgramiv(shader_program_id,GL_INFO_LOG_LENGTH,&infoLogLength);  if(infoLogLength > 0){  std::vector<char>program_error_message(infoLogLength + 1);  glGetProgramInfoLog(shader_program_id,infoLogLength,nullptr,&program_error_message[0]);  std::cout << "program link:" << &program_error_message[0] << std::endl;  }  //删除着色器编译结果  glDeleteShader(vertex_shader_id);  glDeleteShader(fragment_shader_id);  
}  void MyGLWidget::doMVP()  
{  //模型矩阵  QMatrix4x4 model_matrix;  model_matrix.setToIdentity();  //lookAt  QMatrix4x4 view_matrix;  view_matrix.lookAt(camera_pos,look_dir,up_dir);  //投影矩阵  QMatrix4x4 projection_matrix;  projection_matrix.perspective(angle,aspect,near_plane,far_plane);  //获取uniform变量  uniform_model_matrix_location = glGetUniformLocation(shader_program_id,"model_matrix");  uniform_view_matrix_location = glGetUniformLocation(shader_program_id,"view_matrix");  uniform_projection_matrix_location = glGetUniformLocation(shader_program_id,"projection_matrix");  //矩阵传入shader  glUniformMatrix4fv(uniform_model_matrix_location,1,GL_FALSE,model_matrix.data());  glUniformMatrix4fv(uniform_view_matrix_location,1,GL_FALSE,view_matrix.data());  glUniformMatrix4fv(uniform_projection_matrix_location,1,GL_FALSE,projection_matrix.data());  }  GLuint MyGLWidget::loadTexture() {  
//  QString bmp_path = "/Users/liuhang/CLionProjects/opengl-learning/resource/uvtemplate.bmp";  QString bmp_path = "/Users/liuhang/CLionProjects/opengl-learning/resource/uvtemplate-cube.bmp";  QImage texture_image(bmp_path);  if (texture_image.isNull()) {  std::cerr << bmp_path .toStdString() << "路径错误或文件损坏!" << std::endl;  return 0;  }  // 转换为RGB格式并垂直翻转(OpenGL坐标原点在左下角)  QImage gl_image = texture_image.mirrored().convertToFormat(QImage::Format_RGB888);  GLuint textureID;  glGenTextures(1, &textureID);  glBindTexture(GL_TEXTURE_2D, textureID);  // 上传纹理数据  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, gl_image.width(), gl_image.height(), 0,  GL_RGB, GL_UNSIGNED_BYTE, gl_image.bits());  // 设置纹理参数  //===============设置纹理过滤===============  #if 0  //GL_NEAREST:最近像素点采样  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);#endif  #if 0  //GL_LINEAR:线性过滤  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);#endif  #if 1  //GL_MIPMAP_NEAREST: mipmap  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);  //生成mipmap,多加这一行  glGenerateMipmap(GL_TEXTURE_2D);  
#endif  //解绑  glBindTexture(GL_TEXTURE_2D, 0); // 解绑  return textureID;  
}

shader.vert

#version 330 core  layout(location = 0) in vec3 vertex_posion_modelspace;  
layout(location = 1) in vec2 vertex_uv;  
out vec2 uv;  uniform mat4 model_matrix;  
uniform mat4 view_matrix;  
uniform mat4 projection_matrix;  void main(){  uv  =  vertex_uv; //输出uv坐标给fragment shader  mat4 MVP_matrix = projection_matrix * view_matrix * model_matrix;  gl_Position = MVP_matrix * vec4(vertex_posion_modelspace,1.0);  
}

shader.frag

#version 330 core  in vec2 uv;  
out vec3 color;  //关键字  
uniform sampler2D texture_sampler;  void main(){  //texture返回rgba的vec4  color = texture(texture_sampler,uv).rgb;  }
http://www.dtcms.com/a/392386.html

相关文章:

  • agentscope以STUDIO方式调用MCP服务
  • 无公网 IP 访问群晖 NAS:神卓 N600 的安全解决方案(附其他方法风险对比)
  • Redis最佳实践——性能优化技巧之Pipeline 批量操作
  • Java-130 深入浅出 MySQL MyCat 深入解析 核心配置文件 server.xml 使用与优化
  • 业主信息查询功能测试指南
  • WinDivert学习文档之四-————卸载
  • 分布式链路追踪关键指标实战:精准定位服务调用 “慢节点” 全指南(二)
  • DuckDB客户端API之ADBC官方文档翻译
  • 区块链技术应用开发:智能合约进阶与多链生态落地实践
  • 分布式专题——13 RabbitMQ之高级功能
  • 神经风格迁移(Neural Style Transfer)
  • Chromium 138 编译指南 Ubuntu 篇:源码获取与版本管理(四)
  • R 语言入门实战|第九章 循环与模拟:用自动化任务解锁数据科学概率思维
  • [论文阅读] 人工智能 + 软件工程 | 4907个用户故事验证!SEEAgent:解决敏捷估计“黑箱+不协作”的终极方案
  • 鸿蒙Next ArkTS卡片开发指南:从入门到实战
  • 【绕过disable_function】
  • 使用云手机运行手游的注意事项有哪些?
  • 【数据结构】利用堆解决 TopK 问题
  • 2025陇剑杯现场检测
  • openharmony之充电空闲状态定制开发
  • 【开题答辩全过程】以 python的线上订餐系统为例,包含答辩的问题和答案
  • (附源码)基于Spring Boot的校园心理健康服务平台的设计与实现
  • 微信小程序开发教程(十八)
  • 寰宇光锥舟架构图
  • Spring Bean生命周期全面解析
  • [vibe code追踪] 侧边栏UI管理器 | showSidebarContent
  • 嵌入式ARM架构学习9——IIC
  • 多线程——线程安全的练习加感悟
  • 使用 TwelveLabs 的 Marengo 视频嵌入模型与 Amazon Bedrock 和 Elasticsearch
  • Windows 11 下 Notepad++ 等应用无法启动问题排查修复