5.OpenGL之uniform

news2026/3/21 19:43:37
在OpenGL中uniform是一种着色器程序中的变量类型存储限定符。简单来说可以把 uniform 理解为从CPU端你的C/Qt代码向GPU端着色器程序发送的一个“全局只读”参数。下面从几个维度来详细解释它的作用、特点和使用场景1. 核心作用传递“不变”的数据着色器Shader是运行在GPU上的小程序。当我们需要绘制一个物体时有些数据是每个顶点都不同的例如顶点的位置这些通常用attribute或in传递而有些数据是所有顶点或所有片段像素都共用的。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(location0) in vec2 aPos; layout(location1) in vec3 aColor; out vec4 fragColor; void main() { gl_Positionvec4(aPos,0.0,1.0); fragColor(aColor,1.0); } //像流水线一样 #version 330 core in vec4 fragColor; out vec4 u_Color; void main() { u_ColorfragColor; }或下面这样把颜色写死了shader初始化后颜色就不能被改变了#version 330 core layout(location0) in vec2 aPos; void main() { gl_Positionvec4(aPos,0.0,1.0); } #version 330 core out vec4 u_Color; void main() { u_Colorvec4(1.0,0.0,0.0,1.0); }现在使用uniform对比之前就像全局变量对比函数参数。直接外部设置全局变量。#version 330 core layout(location0) in vec2 aPos; void main() { gl_Positionvec4(aPos,0.0,1.0); } #version 330 core uniform vec4 ourColor; out vec4 outColor; void main() { outColorourColor; }m_shadernew QOpenGLShaderProgram(this); m_shader-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader-addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader-link(); m_locm_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_colorQVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRedfalse; }; #endif // GLWIDGET_H#include GLWidget.h #include QTimer GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timernew QTimer(this); timer-setInterval(1000); connect(timer,QTimer::timeout,this,[](){ if(m_isRed) { m_colorQVector4D(0.0f,1.0f,0.0f,1.0f); m_isRedfalse; } else { m_colorQVector4D(1.0f,0.0f,0.0f,1.0f); m_isRedtrue; } 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_vaonew QOpenGLVertexArrayObject(this); m_vao-create(); m_vao-bind(); m_vbonew 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 *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; void main() { gl_Positionvec4(aPos,0.0,1.0); } ); char *fragShaderR( #version 330 core uniform vec4 ourColor; out vec4 outColor; void main() { outColorourColor; } ); m_shadernew QOpenGLShaderProgram(this); m_shader-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader-addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader-link(); m_locm_shader-uniformLocation(ourColor); }效果2.uniform开关-2选1通过外部传入bool变量在shader决定跑哪个分支参考上面示例主要修改以下代码connect(timer,QTimer::timeout,this,[](){ if(m_isRed) { m_isRedfalse; } else { m_isRedtrue; } 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 *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; void main() { gl_Positionvec4(aPos,0.0,1.0); } ); char *fragShaderR( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColorvec4(1.0,0.0,0.0,1.0); } else { outColorvec4(0.0,1.0,0.0,1.0); } } ); m_shadernew QOpenGLShaderProgram(this); m_shader-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader-addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader-link(); m_locm_shader-uniformLocation(u_isRed); }效果跟上面示例一样的3.uniform矩阵-控制位置MVP引入MVP是什么意思MModel 模型变换位移旋转缩放VView 相机 2D一般不用PProjection 投影 正交投影2D必备QMatrix4x4 mvp;创建了一个4x4变换矩阵刚创建时是单位矩阵相当于数字1乘了等于没乘mvp.ortho( 0, //left 左边x0 width(), //right 右边x窗口宽度 height(), //bottom 底部y窗口高度 0, //top 顶部y0 -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(location0)in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Positionu_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_colorQVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRedfalse; int m_loc1; QMatrix4x4 m_mvp; }; #endif // GLWIDGET_H#include GLWidget.h #include QTimer GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timernew QTimer(this); timer-setInterval(1000); connect(timer,QTimer::timeout,this,[](){ if(m_isRed) { m_isRedfalse; } else { m_isRedtrue; } 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_vaonew QOpenGLVertexArrayObject(this); m_vao-create(); m_vao-bind(); m_vbonew 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 *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Positionu_mvp*vec4(aPos,0.0,1.0); } ); char *fragShaderR( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColorvec4(1.0,0.0,0.0,1.0); } else { outColorvec4(0.0,1.0,0.0,1.0); } } ); m_shadernew QOpenGLShaderProgram(this); m_shader-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader-addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader-link(); m_locm_shader-uniformLocation(u_isRed); m_loc1m_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区域的中点就是把原来的原点移动到右下角:translate0.5f,-0.5f:x0.5f(右移0.5f),y0.5f(下移0.5f)。大小也需要变成原来的1/4即宽高各缩小1/2scale( 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_colorQVector4D(1.0f,0.0f,0.0f,1.0f); bool m_isRedfalse; int m_loc1; QMatrix4x4 m_mvp; }; #endif // GLWIDGET_H#include GLWidget.h #include QTimer GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent) { QTimer *timernew QTimer(this); timer-setInterval(1000); connect(timer,QTimer::timeout,this,[](){ if(m_isRed) { m_isRedfalse; } else { m_isRedtrue; } 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_vaonew QOpenGLVertexArrayObject(this); m_vao-create(); m_vao-bind(); m_vbonew 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 *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; uniform mat4 u_mvp; void main() { gl_Positionu_mvp*vec4(aPos,0.0,1.0); } ); char *fragShaderR( #version 330 core uniform bool u_isRed; out vec4 outColor; void main() { if(u_isRed) { outColorvec4(1.0,0.0,0.0,1.0); } else { outColorvec4(0.0,1.0,0.0,1.0); } } ); m_shadernew QOpenGLShaderProgram(this); m_shader-addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShader); m_shader-addShaderFromSourceCode(QOpenGLShader::Fragment,fragShader); m_shader-link(); m_locm_shader-uniformLocation(u_isRed); m_loc1m_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.08000600-11(0,600) (800,600) ┌──────────────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ └──────────────────────┘ (0,0) (800,0)2.08006000-11(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需要进行缩放到右边咋一看是先缩放再平移或者先平移再缩放最后都能得到这个结果但是在矩阵数学计算中不是想当然的而是要看数据的实际变化。原左上角顶点位置300200宽200 高 200先投影看看正常的位置m_mvp.ortho( 0, 800, 600, 0, -1, 1 );// float vertex[]{ 300,200, 500,200, 500,400, 300,400 };300200先平移显然不是因为现在由于比例问题不太好移动300200先缩放必然会缩放到原来的一半即150100到了窗口的第一块区域的正中间位置但是这个时候窗口坐标是-11之间所以要进行正交投影。将150100在800600上的相对位置转到-11上来。经过上面变换现在坐标的范围就在-11之间了然后再进行平移右移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那如果是先投影呢300200在800 600上的投影得到了具体的图像位置这个时候点大概是-0.50.66左右也就是左边图像的效果然后尝试进行平移但是发现平移在缩放之前不好判断那就先缩放到原来的1/2这个时候点来到-0.250.33左右这个时候再去平移看中点的坐标变化就知道该怎么移动啦中点00——0.5-0.5那就是x移动0.5y移动-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_tex0new 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_tex1new 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_vaonew QOpenGLVertexArrayObject(this); m_vao-create(); m_vao-bind(); m_vbonew 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 *vertexShaderR( #version 330 core layout(location0) in vec2 aPos; layout(location1) in vec2 aTexcoord; out vec2 ourTexcoord; void main() { gl_Positionvec4(aPos,0.0,1.0); ourTexcoordaTexcoord; } ); char *fragShaderR( #version 330 core in vec2 ourTexcoord; uniform sampler2D u_tex0; uniform sampler2D u_tex1; out vec4 outColor; void main() { vec4 color0texture(u_tex0,ourTexcoord); vec4 color1texture(u_tex1,ourTexcoord); outColorcolor0color1; } ); m_shadernew 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_H5.其他.....(用到再学吧写的太乱了这篇后面想单独把矩阵变换做一章内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2434461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…