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

5.OpenGL之uniform

在OpenGL中,uniform是一种着色器程序中的变量类型(存储限定符)。

简单来说,可以把 uniform 理解为:从CPU端(你的C++/Qt代码)向GPU端(着色器程序)发送的一个“全局只读”参数。

下面从几个维度来详细解释它的作用、特点和使用场景:

1. 核心作用:传递“不变”的数据

着色器(Shader)是运行在GPU上的小程序。当我们需要绘制一个物体时,有些数据是每个顶点都不同的(例如顶点的位置,这些通常用attributein传递),而有些数据是所有顶点或所有片段(像素)都共用的。

uniform 就是用来传递这些“共用”数据的。

  • 例子

    • 如果我们想让一个三角形显示为红色,这个“红色”的值只需要告诉GPU一次,三角形的所有像素都应该使用这个红色,这个颜色值就应该用uniform传递,就不需要给每个顶点都设置颜色。

    • 如果我们给模型加了一个“亮度”系数,这个系数也应该是 uniform。

2. 关键特性

  • 全局性:uniform 变量在着色器程序的每一次执行(即每一个顶点或每一个片段)中,都保持同一个值。

  • 只读性:你可以在着色器里读取它,但不能在着色器里修改它。修改它的唯一途径是通过CPU端的OpenGL API(应用程序编程接口)调用。

  • 持久性:一旦你设置了一个 uniform 的值,它会一直保存在着色器程序中,直到你下次修改它或者程序结束。

3. 在QOpenGL中的具体用法

在QOpenGL中,操作 uniform 通常遵循以下三步曲:

步骤 1:在着色器代码中声明

在顶点着色器(.vert)或片段着色器(.frag)中,你需要定义 uniform 变量。

片段着色器示例 (fragment shader):

#version 330 core uniform vec4 ourColor; // 声明一个uniform变量,类型为4维向量(RGBA) out vec4 FragColor; void main() { FragColor = ourColor; // 将这个全局颜色赋值给输出 }
步骤 2:获取 uniform 变量的位置(ID)

在C++代码中,你需要找到这个变量在着色器程序中的索引位置。

// 假设 m_program 是 QOpenGLShaderProgram 对象 int uniformLocation = m_program->uniformLocation("ourColor");
步骤 3:上传数据到 GPU

使用QOpenGLShaderProgram提供的便捷方法,将数据从CPU发送到这个位置。

// 设置颜色为半透明的绿色 GLfloat greenColor[4] = {0.0f, 1.0f, 0.0f, 0.5f}; // 绑定着色器程序 m_program->bind(); // 将数组数据上传到 uniform 位置 m_program->setUniformValue(uniformLocation, greenColor); // 或者直接使用 QColor m_program->setUniformValue(uniformLocation, QColor(0, 255, 0, 128)); // 现在开始绘制... 绘制的所有物体都会使用这个绿色

注意:

setUniformValue必须在shader调用bind后才会生效,且上传的数据类型必须和着色器内的代码保持一致。

4. uniform 到底传什么?

uniform 可以传递的数据类型非常丰富,涵盖了图形编程的大部分需求:

  • 基础类型int,float,bool

  • 向量类型vec2,vec3,vec4(对应QVector2D, QVector3D, QVector4D, QColor)

  • 矩阵类型mat2,mat3,mat4(对应QMatrix2x2, QMatrix4x4) —— 常用于传递模型、视图、投影矩阵。

  • 采样器sampler2D,samplerCube等。这是一种特殊的uniform,用于告诉着色器使用哪一个纹理单元。

5. 总结:为什么需要 uniform?

想象一下你要用3D画笔画一幅画:

  • 顶点属性 (Attribute)告诉你笔尖要经过的每一个点在哪里。

  • Uniform告诉你现在这支笔是什么颜色、用的是多粗的笔刷、当前画布旋转了多少度。

如果没有 uniform,你就只能把颜色值硬编码到着色器里,或者为每一个顶点都附带一个颜色信息(这会造成巨大的内存浪费和性能下降)。uniform 提供了一种高效、灵活的方式来控制渲染管线的全局状态。

6.Qt OpenGL 与 GLSL 数据类型对应表

1. 向量类型(最常用)

Qt 类型GLSL 类型分量含义(XYZW/RGBA)作用
QVector2Dvec2(x, y)2D 坐标、纹理坐标
QVector3Dvec3(x, y, z)3D 坐标、RGB 颜色
QVector4Dvec4(x, y, z, w)带 Alpha 的颜色、齐次坐标

2. 矩阵类型

Qt 类型GLSL 类型作用
QMatrix4x4mat44x4 矩阵(MVP 矩阵、变换矩阵)
QMatrix3x3mat33x3 矩阵(旋转、法线矩阵)

3. 标量(普通数值)

Qt 类型GLSL 类型作用
floatfloat小数、强度、透明度
intint整数、索引
boolbool开关

赋值方法

// 2D坐标 QVector2D pos(100, 200); shader->setUniformValue("u_pos", pos); // 颜色 QVector4D color(1.0f, 0.0f, 0.0f, 1.0f); shader->setUniformValue("u_color", color); // 矩阵 QMatrix4x4 mat; mat.setToIdentity(); shader->setUniformValue("u_matrix", mat);

7.具体应用示例:

1.uniform传颜色

之前shader程序:像流水线一样,颜色从顶点shader传到片段shader

#version 330 core layout(location=0) in vec2 aPos; layout(location=1) in vec3 aColor; out vec4 fragColor; void main() { gl_Position=vec4(aPos,0.0,1.0); fragColor=(aColor,1.0); } //像流水线一样 #version 330 core in vec4 fragColor; out vec4 u_Color; void main() { u_Color=fragColor; }

或下面这样,把颜色写死了,shader初始化后颜色就不能被改变了:

#version 330 core layout(location=0) in vec2 aPos; void main() { gl_Position=vec4(aPos,0.0,1.0); } #version 330 core out vec4 u_Color; void main() { u_Color=vec4(1.0,0.0,0.0,1.0); }

现在使用uniform,对比之前就像全局变量对比函数参数。直接外部设置全局变量。

#version 330 core layout(location=0) in vec2 aPos; void main() { gl_Position=vec4(aPos,0.0,1.0); } #version 330 core uniform vec4 ourColor; out vec4 outColor; void main() { outColor=ourColor; }
m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); m_loc=m_shader->uniformLocation("ourColor");//获取uniform的位置,方便后面直接通过loc设置

完整代码:

创建定时器来改变颜色:

#ifndef GLWIDGET_H #define GLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions_3_3_Core> #include <QVector4D> class GLWidget : public QOpenGLWidget,private QOpenGLFunctions_3_3_Core { public: GLWidget(QWidget *parent); void initializeGL(); void paintGL(); void resizeGL(int w,int h); void initRect(); void initShader(); private: QOpenGLBuffer *m_vbo; QOpenGLVertexArrayObject *m_vao; QOpenGLShaderProgram *m_shader; int m_loc; QVector4D m_color=QVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRed=false; }; #endif // GLWIDGET_H
#include "GLWidget.h" #include <QTimer> GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timer=new QTimer(this); timer->setInterval(1000); connect(timer,&QTimer::timeout,this,[&](){ if(m_isRed) { m_color=QVector4D(0.0f,1.0f,0.0f,1.0f); m_isRed=false; } else { m_color=QVector4D(1.0f,0.0f,0.0f,1.0f); m_isRed=true; } update(); }); timer->start(); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); initRect(); initShader(); } void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_loc,m_color); glDrawArrays(GL_TRIANGLE_FAN,0,4); } void GLWidget::resizeGL(int w,int h) { m_shader->bind(); m_vao->bind(); } void GLWidget::initRect() { float vertex[]={ -0.5f,0.5f, 0.5f,0.5f, 0.5f,-0.5f, -0.5f,-0.5f }; m_vao=new QOpenGLVertexArrayObject(this); m_vao->create(); m_vao->bind(); m_vbo=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); m_vbo->create(); m_vbo->bind(); m_vbo->allocate(vertex,sizeof(vertex)); glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*2,(void*)0); glEnableVertexAttribArray(0); m_vbo->release(); m_vao->release(); } void GLWidget::initShader() { char *vertexShader=R"( #version 330 core layout(location=0) in vec2 aPos; void main() { gl_Position=vec4(aPos,0.0,1.0); } )"; char *fragShader=R"( #version 330 core uniform vec4 ourColor; out vec4 outColor; void main() { outColor=ourColor; } )"; m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); m_loc=m_shader->uniformLocation("ourColor"); }

效果:

2.uniform开关-2选1

通过外部传入bool变量,在shader决定跑哪个分支,参考上面示例,主要修改以下代码:

connect(timer,&QTimer::timeout,this,[&](){ if(m_isRed) { m_isRed=false; } else { m_isRed=true; } update(); });
void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_loc,m_isRed); glDrawArrays(GL_TRIANGLE_FAN,0,4); }
void GLWidget::initShader() { char *vertexShader=R"( #version 330 core layout(location=0) in vec2 aPos; void main() { gl_Position=vec4(aPos,0.0,1.0); } )"; char *fragShader=R"( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColor=vec4(1.0,0.0,0.0,1.0); } else { outColor=vec4(0.0,1.0,0.0,1.0); } } )"; m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); m_loc=m_shader->uniformLocation("u_isRed"); }

效果跟上面示例一样的

3.uniform矩阵-控制位置

MVP引入:

MVP是什么意思?

M=Model 模型变换(位移,旋转,缩放)

V=View 相机 (2D一般不用)

P=Projection 投影 (正交投影,2D必备)

QMatrix4x4 mvp;

创建了一个4x4变换矩阵,刚创建时是单位矩阵(相当于数字1,乘了等于没乘)

mvp.ortho( 0, //left 左边x=0 width(), //right 右边x=窗口宽度 height(), //bottom 底部y=窗口高度 0, //top 顶部y=0 -1, //zNear Z近平面 1, //zFar Z远平面 );

最后两个参数是z轴方向上的可见范围,在2D渲染里,所有的Z都是0,所以写-1,1永远没问题。

定义了一个“像素坐标系”:

窗口左上角=(0,0)

窗口右下角=(width(),height())

和Qt原生坐标完全一致(和图像坐标也一致)

shader->setUniformValue("u_mvp",mvp);

作用:把CPU的QMatrix4x4——>传给GPU着色器里面的mat4 u_map

对应:

uniform vec4 u_mvp;

不是传的具体坐标,而是位置变换规则,用于给aPos进行坐标变换。

物体本身的顶点坐标永远不变 → 用矩阵把它 “移动 / 旋转 / 缩放” 到目标位置

#version 330 core layout(location=0)in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Position=u_mvp*vec4(aPos,0.0,1.0); }

含义:顶点像素坐标xMVP矩阵=OpenGL能识别的标准坐标。

只要传:

float rect[]={ 50,50, 150,50, 150,150, 50,150 };

就可以直接在窗口的(50,50)位置画一个100*100的矩形。

#ifndef GLWIDGET_H #define GLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions_3_3_Core> #include <QVector4D> #include <QMatrix4x4> class GLWidget : public QOpenGLWidget,private QOpenGLFunctions_3_3_Core { public: GLWidget(QWidget *parent); void initializeGL(); void paintGL(); void resizeGL(int w,int h); void initRect(); void initShader(); private: QOpenGLBuffer *m_vbo; QOpenGLVertexArrayObject *m_vao; QOpenGLShaderProgram *m_shader; int m_loc; QVector4D m_color=QVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRed=false; int m_loc1; QMatrix4x4 m_mvp; }; #endif // GLWIDGET_H
#include "GLWidget.h" #include <QTimer> GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timer=new QTimer(this); timer->setInterval(1000); connect(timer,&QTimer::timeout,this,[&](){ if(m_isRed) { m_isRed=false; } else { m_isRed=true; } update(); }); timer->start(); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); initRect(); initShader(); m_mvp.ortho(0,width(),height(),0,-1,1); } void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_loc,m_isRed); m_shader->setUniformValue(m_loc1,m_mvp); glDrawArrays(GL_TRIANGLE_FAN,0,4); } void GLWidget::resizeGL(int w,int h) { } void GLWidget::initRect() { float vertex[]={ 50,50, 150,50, 150,150, 50,150 }; m_vao=new QOpenGLVertexArrayObject(this); m_vao->create(); m_vao->bind(); m_vbo=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); m_vbo->create(); m_vbo->bind(); m_vbo->allocate(vertex,sizeof(vertex)); glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*2,(void*)0); glEnableVertexAttribArray(0); m_vbo->release(); m_vao->release(); } void GLWidget::initShader() { char *vertexShader=R"( #version 330 core layout(location=0) in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Position=u_mvp*vec4(aPos,0.0,1.0); } )"; char *fragShader=R"( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColor=vec4(1.0,0.0,0.0,1.0); } else { outColor=vec4(0.0,1.0,0.0,1.0); } } )"; m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); m_loc=m_shader->uniformLocation("u_isRed"); m_loc1=m_shader->uniformLocation("u_mvp"); }

再来看下面这个例子:

在窗体的右下角1/4区域,以这个区域作为一个窗体,显示一个矩形:

float vertex[]={ -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f };

这个适合就需要坐标变换了,传入的是相对的位置,新的原点到了右下角1/4区域的中点,就是把原来的原点,移动到右下角:translate(0.5f,-0.5f):x+0.5f(右移0.5f),y=0.5f(下移0.5f)。

大小也需要变成原来的1/4,即宽高各缩小1/2,scale( 0.5f, 0.5f );

这个时候再传入-1,1之间的数就会在右下角显示了:

#ifndef GLWIDGET_H #define GLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions_3_3_Core> #include <QVector4D> #include <QMatrix4x4> class GLWidget : public QOpenGLWidget,private QOpenGLFunctions_3_3_Core { public: GLWidget(QWidget *parent); void initializeGL(); void paintGL(); void resizeGL(int w,int h); void initRect(); void initShader(); private: QOpenGLBuffer *m_vbo; QOpenGLVertexArrayObject *m_vao; QOpenGLShaderProgram *m_shader; int m_loc; QVector4D m_color=QVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRed=false; int m_loc1; QMatrix4x4 m_mvp; }; #endif // GLWIDGET_H
#include "GLWidget.h" #include <QTimer> GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timer=new QTimer(this); timer->setInterval(1000); connect(timer,&QTimer::timeout,this,[&](){ if(m_isRed) { m_isRed=false; } else { m_isRed=true; } update(); }); timer->start(); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); initRect(); initShader(); // 第一步:把取景框缩放到 1/4 大小,并移动到右下角 m_mvp.translate( 0.5f, -0.5f ); // 坐标原点移到右下角 m_mvp.scale( 0.5f, 0.5f ); // 视口大小变成 1/4 // 第二步:在这个右下角小窗口里,使用 -1 ~ 1 坐标系 m_mvp.ortho( -1, 1, 1, -1, -1, 1 );// } void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_loc,m_isRed); m_shader->setUniformValue(m_loc1,m_mvp); glDrawArrays(GL_TRIANGLE_FAN,0,4); } void GLWidget::resizeGL(int w,int h) { } void GLWidget::initRect() { float vertex[]={ -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5 }; m_vao=new QOpenGLVertexArrayObject(this); m_vao->create(); m_vao->bind(); m_vbo=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); m_vbo->create(); m_vbo->bind(); m_vbo->allocate(vertex,sizeof(vertex)); glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*2,(void*)0); glEnableVertexAttribArray(0); m_vbo->release(); m_vao->release(); } void GLWidget::initShader() { char *vertexShader=R"( #version 330 core layout(location=0) in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Position=u_mvp*vec4(aPos,0.0,1.0); } )"; char *fragShader=R"( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColor=vec4(1.0,0.0,0.0,1.0); } else { outColor=vec4(0.0,1.0,0.0,1.0); } } )"; m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); m_loc=m_shader->uniformLocation("u_isRed"); m_loc1=m_shader->uniformLocation("u_mvp"); }

效果:

再添加一个m_mvp1:

void GLWidget::initializeGL() { initializeOpenGLFunctions(); initRect(); initShader(); // 第一步:把取景框缩放到 1/4 大小,并移动到右下角 m_mvp.translate( 0.5f, -0.5f ); // 坐标原点移到右下角 m_mvp.scale( 0.5f, 0.5f ); // 视口大小变成 1/4 // 第二步:在这个右下角小窗口里,使用 -1 ~ 1 坐标系 m_mvp.ortho( -1, 1, 1, -1, -1, 1 );// m_mvp1.translate( -0.5f, -0.5f ); // 坐标原点移到左下角 m_mvp1.scale( 0.5f, 0.5f ); // 视口大小变成 1/4 // 第二步:在这个右下角小窗口里,使用 -1 ~ 1 坐标系 m_mvp1.ortho( -1, 1, 1, -1, -1, 1 );// }
void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_loc,m_isRed); m_shader->setUniformValue(m_loc1,m_mvp); glDrawArrays(GL_TRIANGLE_FAN,0,4); m_shader->setUniformValue(m_loc,m_isRed); m_shader->setUniformValue(m_loc1,m_mvp1); glDrawArrays(GL_TRIANGLE_FAN,0,4); }

效果就变成:

ortho代表你要建立什么样的坐标系:

1.(0,800,0,600,-1,1)

(0,600) (800,600) ┌──────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ (0,0) (800,0)

2.(0,800,600,0,-1,1)

(0,0) (800,0) ┌──────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ (0,600) (800,600)
重要:矩阵变换

在着色器代码中矩阵变换采用左乘,在代码里面的顺序就是按照相反的顺序执行的:

比如下面这几行代码:

QMatrix4x4 mat4; mat4.setToIdentity();//重置 mat4.translate(...); mat4.scale(...); mat4.ortho(...);

那么着色器中实际的生效顺序就是ortho(正交投影)->scale(缩放)->translate(y移动)

你可能会疑惑?不都是执行了移动,缩放,投影吗?最后结果还能不一样?

还真是不一样。

举个实际的例子,假如窗口的宽为800,高为600,在正中间有一个小框,宽为300,需要进行缩放到右边,咋一看是先缩放再平移,或者先平移再缩放,最后都能得到这个结果,但是在矩阵数学计算中,不是想当然的,而是要看数据的实际变化。

原左上角顶点位置300,200,宽:200 高 :200

先投影看看正常的位置:

m_mvp.ortho( 0, 800, 600, 0, -1, 1 );// float vertex[]={ 300,200, 500,200, 500,400, 300,400 };

300,200先平移?显然不是,因为现在由于比例问题,不太好移动

300,200先缩放?必然会缩放到原来的一半,即150,100,到了窗口的第一块区域的正中间位置,但是这个时候,窗口坐标是-1,1之间,所以要进行正交投影。

将150,100在800,600上的相对位置,转到-1,1上来。

经过上面变换,现在坐标的范围就在-1,1之间了,然后再进行平移,右移1.0,下移动1.0,就可以得到右边的效果了。

m_mvp.translate( 1.0, -1.0 ); // 坐标原点移到右下角 m_mvp.ortho( 0, 800, 600, 0, -1, 1 );// m_mvp.scale( 0.5f, 0.5f ); // 视口大小变成 1/4

那如果是先投影呢?300,200在800 600上的投影,得到了具体的图像位置,这个时候,点大概是-0.5,0.66左右,也就是左边图像的效果,然后尝试进行平移,但是发现平移在缩放之前不好判断,那就先缩放到原来的1/2,这个时候,点来到-0.25,0.33左右,这个时候再去平移,看中点的坐标变化就知道该怎么移动啦,中点0,0——>0.5,-0.5,那就是x移动0.5,y移动-0.5,最后也可以得到右边的效果。

m_mvp.translate( 0.5, -0.5 ); m_mvp.scale( 0.5f, 0.5f ); // 视口大小变成 1/4 m_mvp.ortho( 0, 800, 600, 0, -1, 1 );//

效果跟上面一样的。

4.uniform传纹理

这里传uniform并不是传的真正的纹理,因为纹理对象是单独的对象保存,纹理对象自身调用setDate时,会在GPU上分配一块显存保存纹理信息,然后需要调用bind(n),将纹理信息绑定到n号纹理单元,这个n就与着色器相关,着色器怎么知道去GPU上面找哪块纹理单元,就靠这个uniform来确定操作哪个纹理单元了。

比如:

shader->setUniform("u_tex0",0);

在着色器中,使用这个u_tex0全局变量,就表示去0号纹理单元找那个纹理。

shader->setUniform("u_tex1",1);

在着色器中,使用这个u_tex1全局变量,就表示去1号纹理单元找那个纹理。

之前没有给着色器设置这个,是因为纹理对象默认bind会绑定到0号纹理单元,着色器程序也默认去0号纹理单元取出纹理。

实际应用:

如果着色器只需要同时操作一个纹理,不需要对他们进行融合等操作,那么就不用设置这个纹理uniform了,下面这个例子,就展示了如何使用这个uniform传纹理的实际运用。

你看到了吗,图片里面有一只猫和一辆车的融合,就是一次shader跑出来的,代码见下方。

#include "GLWidget.h" #include <QTimer> GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { } void GLWidget::initializeGL() { initializeOpenGLFunctions(); initRect(); initShader(); m_tex0=new QOpenGLTexture(QOpenGLTexture::Target2D); m_tex0->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); m_tex0->setMagnificationFilter(QOpenGLTexture::Linear); m_tex0->setWrapMode(QOpenGLTexture::ClampToEdge); m_tex0->setData(QImage("car.jpg")); m_tex1=new QOpenGLTexture(QOpenGLTexture::Target2D); m_tex1->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); m_tex1->setMagnificationFilter(QOpenGLTexture::Linear); m_tex1->setWrapMode(QOpenGLTexture::ClampToEdge); m_tex1->setData(QImage("cat.jpg")); } void GLWidget::paintGL() { glClearColor(0.1,0.1,0.1,1.0); glClear(GL_COLOR_BUFFER_BIT); m_tex0->bind(0); m_tex1->bind(1); m_vao->bind(); m_shader->bind(); m_shader->setUniformValue(m_shader->uniformLocation("u_tex0"),0); m_shader->setUniformValue(m_shader->uniformLocation("u_tex1"),1); glDrawArrays(GL_TRIANGLE_FAN,0,4); } void GLWidget::resizeGL(int w,int h) { } void GLWidget::initRect() { float vertex[]={ -0.5, -0.5, 0.0,1.0, 0.5, -0.5, 1.0,1.0, 0.5, 0.5, 1.0,0.0, -0.5, 0.5, 0.0,0.0 }; m_vao=new QOpenGLVertexArrayObject(this); m_vao->create(); m_vao->bind(); m_vbo=new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); m_vbo->create(); m_vbo->bind(); m_vbo->allocate(vertex,sizeof(vertex)); glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,sizeof(float)*4,(void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,sizeof(float)*4,(void*)(sizeof(float)*2)); glEnableVertexAttribArray(1); m_vbo->release(); m_vao->release(); } void GLWidget::initShader() { char *vertexShader=R"( #version 330 core layout(location=0) in vec2 aPos; layout(location=1) in vec2 aTexcoord; out vec2 ourTexcoord; void main() { gl_Position=vec4(aPos,0.0,1.0); ourTexcoord=aTexcoord; } )"; char *fragShader=R"( #version 330 core in vec2 ourTexcoord; uniform sampler2D u_tex0; uniform sampler2D u_tex1; out vec4 outColor; void main() { vec4 color0=texture(u_tex0,ourTexcoord); vec4 color1=texture(u_tex1,ourTexcoord); outColor=color0+color1; } )"; m_shader=new QOpenGLShaderProgram(this); m_shader->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader->addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader->link(); }
#ifndef GLWIDGET_H #define GLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions_3_3_Core> #include <QOpenGLTexture> class GLWidget : public QOpenGLWidget,private QOpenGLFunctions_3_3_Core { public: GLWidget(QWidget *parent); void initializeGL(); void paintGL(); void resizeGL(int w,int h); void initRect(); void initShader(); private: QOpenGLBuffer *m_vbo; QOpenGLVertexArrayObject *m_vao; QOpenGLShaderProgram *m_shader; QOpenGLTexture *m_tex0; QOpenGLTexture *m_tex1; }; #endif // GLWIDGET_H
5.其他.....(用到再学吧)

写的太乱了这篇,后面想单独把矩阵变换做一章内容

http://www.jsqmd.com/news/513478/

相关文章:

  • 口碑见证实力:2026年不锈钢油罐优质厂家推荐,不锈钢容器/不锈钢油罐/储罐/油罐/水泥罐/不锈钢储罐,油罐厂家有哪些 - 品牌推荐师
  • Pixel Dimension Fissioner可部署实践:从HuggingFace模型到像素工坊镜像封装
  • 国产DSP芯片十大品牌推荐:如何选择高性能实时控制芯片?
  • 华为ENSP实战:旁挂AC的Web界面快速部署多SSID无线网络
  • Phi-4-mini-reasoning×ollama惊艳效果:自动将中文应用题转化为SQL查询语句
  • 腾讯云服务器选OpenCloudOS还是CentOS?实测对比告诉你答案
  • Akagi智能麻将助手:5个步骤掌握你的AI牌局教练
  • 2026年头部电机微控制器原厂推荐:高可靠 MCU 芯片甄选
  • Step3-VL-10B-Base系统资源优化:C盘清理与模型存储空间管理
  • 2026年3月上海多媒体科技公司最新推荐:沉浸式空间、数字影像、虚拟漫游,VR、AR、幻影成像、全息影像、二维动画、三维动画、数字作品,城市形象片、企业宣传片、微电影等领域选择指南 - 海棠依旧大
  • 2026年上海多媒体解决方案优质服务商推荐:沉浸式空间、CAVE沉浸式影院、L幕裸眼3D影院、U型幕、3D影院、4D影院、上海观联多媒体科技沉浸式体验与数字内容服务标杆 - 海棠依旧大
  • 计算机毕业设计 | springboot+vue大学城水电管理系统 校园学校物业水电管理(附源码+文档)
  • 老码农和你一起学AI系列:关于LLaMA解码器
  • IndexTTS-2-LLM语音降噪处理:后处理优化实战指南
  • 深度学习(5)
  • 告别第三方内网穿透服务:用DDNS-Go+华为云自建动态域名解析,飞牛OS实测
  • Pixel Dimension Fissioner案例集:TikTok脚本、播客开场白、Newsletter标题裂变库
  • UWB室内定位技术:从原理到实践的全方位指南
  • QML anchors(锚定)详解(从入门到精通,附实战示例)
  • STM32F103C8T6入门实战:从零搭建LED闪烁工程
  • Qt开发必看:如何用多参数优化QString::arg()性能(附Clazy警告修复实战)
  • OpenClaw知识库整合:Qwen3-32B连接本地文档实现精准问答
  • 读2025世界前沿技术发展报告17航天技术发展(上)
  • Mistral AI本地部署 C++无需Nvidiad独立显卡也能运行(CPU推理)
  • OpenClaw+GLM-4.7-Flash智能监控:服务器日志异常检测与告警推送
  • 若依框架的@Excel注解,我只用这4个属性就玩转了多Sheet导出(附完整工具类)
  • Linux网络数据包收发全流程深度解析
  • 芯片流片前必看:一文搞懂Corner Wafer测试如何帮你守住良率底线
  • OpenClaw权限控制:GLM-4.7-Flash模型服务的访问限制方案
  • R语言专栏的网站 https://bestmd.coze.site/ ,我们升级了护眼模式!