一、作用原理
1、作用:将一张图片(纹理)映射到几何体表面,提升视觉真实感,不增加几何复杂度。
2、原理:加载图片为纹理 → 上传到 GPU;为顶点设置纹理坐标(如 0~1 范围);GPU 在渲染时 插值纹理坐标;片元着色器中采样纹理颜色 → 显示图案。简单说,就是"把图贴上去"。
二、实现效果
三、参考代码
读取本地的png图片贴到四边形几何体表面
cpp
//TextureRenderer 类
#pragma once
#include "RenderModuleInterface.h"
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QImage>
#include <QMatrix4x4>
// 简单纹理渲染器:负责初始化 OpenGL 资源、加载纹理并进行屏幕渲染
class TextureRenderer : public RenderModuleInterface, protected QOpenGLFunctions
{
public:
TextureRenderer();
~TextureRenderer();
// 初始化 OpenGL 状态与资源,只执行一次
void initialize() override;
// 设置视口大小(通常来自窗口resize事件)
void resize(int w, int h) override;
// 执行渲染流程(清屏、绑定shader和纹理、绘制)
void render() override;
// 设置外部纹理图像(功能扩展)
void setTextureImage(const QImage& image);
// 设置 MVP(模型视图投影)矩阵,可用于变换控制
void setModelMatrix(const QMatrix4x4& model);
private:
QOpenGLShaderProgram shader; // 着色器程序
QOpenGLTexture* texture = nullptr;// 纹理对象
QOpenGLBuffer vbo; // 顶点缓冲对象
QOpenGLVertexArrayObject vao; // 顶点数组对象
QMatrix4x4 mvpMatrix; // 模型视图投影矩阵
int viewportWidth = 0; // 视口宽度
int viewportHeight = 0; // 视口高度
bool isInitialized = false; // 初始化标志
};
#include "TextureRenderer.h"
#include <QDebug>
// 顶点着色器源码:传递位置与纹理坐标,并应用 MVP 变换
static const char* vertexShaderSrc = R"(
#version 330 core
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 texCoord;
out vec2 TexCoord;
uniform mat4 u_mvp;
void main()
{
TexCoord = texCoord;
gl_Position = u_mvp * vec4(position, 0.0, 1.0);
})";
// 片段着色器源码:根据纹理坐标采样颜色输出
static const char* fragmentShaderSrc = R"(
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D u_texture;
void main()
{
FragColor = texture(u_texture, TexCoord);
})";
TextureRenderer::TextureRenderer() {}
TextureRenderer::~TextureRenderer()
{
if (texture) {
delete texture;
texture = nullptr;
}
vao.destroy();
vbo.destroy();
}
void TextureRenderer::initialize()
{
if (isInitialized) return;
initializeOpenGLFunctions();
if (shader.isLinked()) return;
// 设置清除背景色
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
// 默认创建蓝色纹理(128x128)
QImage img(128, 128, QImage::Format_RGBA8888);
img.fill(QColor(0, 0, 255, 255));
texture = new QOpenGLTexture(img.mirrored());
// 编译并链接着色器
bool vs = shader.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSrc);
bool fs = shader.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSrc);
if (!vs || !fs || !shader.link()) {
qDebug() << "Shader Compile Error:" << shader.log();
return;
}
// 顶点数据(位置 + 纹理坐标)
GLfloat vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 0.0f
};
shader.bind();
vao.create();
vao.bind();
vbo.create();
vbo.bind();
vbo.allocate(vertices, sizeof(vertices));
// 配置顶点属性指针
shader.enableAttributeArray(0); // position
shader.setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(float));
shader.enableAttributeArray(1); // texCoord
shader.setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(float), 2, 4 * sizeof(float));
vao.release();
vbo.release();
shader.release();
// 设置默认正交投影矩阵
mvpMatrix.ortho(-1, 1, -1, 1, -1, 1);
isInitialized = true;
}
void TextureRenderer::resize(int w, int h)
{
glViewport(0, 0, w, h);
viewportWidth = w;
viewportHeight = h;
}
void TextureRenderer::render()
{
if (!isInitialized) return;
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader.bind();
vao.bind();
// 设置变换矩阵
shader.setUniformValue("u_mvp", mvpMatrix);
// 绑定纹理并设置纹理单元
glActiveTexture(GL_TEXTURE0);
texture->bind();
shader.setUniformValue("u_texture", 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
texture->release();
vao.release();
shader.release();
}
void TextureRenderer::setTextureImage(const QImage& image)
{
if (!image.isNull()) {
if (texture) {
delete texture;
texture = nullptr;
}
texture = new QOpenGLTexture(image.mirrored());
}
}
void TextureRenderer::setModelMatrix(const QMatrix4x4& model)
{
mvpMatrix = model;
}
//使用,这里默认创建的是蓝色纹理,设置图片后将其覆盖了
modules["纹理贴图"] = new TextureRenderer();
// 加载砖墙纹理图片
QImage image(":/images/wall.png");
modules["纹理贴图"]->initialize();
if (!image.isNull()) {
dynamic_cast<TextureRenderer*>(modules["纹理贴图"])->setTextureImage(image);
}
欢迎关注我,一起交流!