目录
在OpenGL中文手册上进行扩展和补充,采用QT进行开发,原生C++版本教程,请点击此处参考OpenGL CN 纹理
本章节源码请点击此处
一. 纹理作用
如果我们想要让图片看起来更加真实,颜色更加细节,我们就需要提供足够多的顶点然后分别为其提供足够多的颜色,但这样不但从开发上会很麻烦复杂,内存也不能一定能吃得消,但纹理可以有效的解决这些问题。
虽然我们不会直接在纹理上“手动画图”,但通过编程方式把纹理数据应用到渲染管线的不同阶段,实质上起到了类似绘图的效果,增强了OpenGL渲染的多样性和复杂度。
1.1 纹理贴图
通过定义纹理坐标并将纹理映射到几何体表面,比如3D模型的顶点上,这样当你渲染这个模型时,表面上就会显示纹理图片的内容,仿佛是“画”在模型表面一样。
- 本质: 纹理的本质是一个2D(1D或者3D)的图片,然后利用这个图片去绘制我们的数据模型。
1.2 纹理作为渲染目标
在现代OpenGL(包括OpenGL ES)中,可以将纹理用作帧缓冲对象(Framebuffer Object, FBO)的一部分,充当颜色附件或其他类型附件。这样,渲染的结果可以直接输出到纹理中,然后这个纹理可以被后续的渲染步骤当作图像数据读取或者进一步绘制到屏幕上,这种技术常用于实现镜面反射、阴影映射、后期处理特效等。
1.3 纹理采样
在片段着色器中,可以根据纹理坐标对纹理进行采样,获取纹理中对应位置的颜色或者其他数据,进而影响片段最终的颜色值。通过控制纹理采样器的状态,可以实现复杂的混合和过滤效果。
1.4 纹理作为数据容器
除了传统的图像数据外,纹理还可以存储其他类型的数据,比如高度图、法线贴图、环境光遮蔽图等,这些数据在着色器计算中起到关键作用,间接帮助“绘制”出丰富的视觉效果
二 纹理映射
图 2-1 未经过纹理处理的图像
图 2-1 经过纹理处理的图像
2.1 纹理坐标
将纹理映射到矩形图像上时,需要指定每个顶点所对应的纹理(2D图片)的那部分,这样每个顶点就会关联一个纹理坐标,用来告诉这个顶点应该从纹理图像的哪个部分进行采样(采集片段颜色)
纹理坐标的范围0-1之间
2.1.1 采样
- 使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。下面的图片展示了我们是如何把纹理坐标映射到矩形图像的。
2.2 纹理使用
这里我使用的是Qt中的方法,如果是c++则请参考OpenGL中文手册
- 创建QOpenGLTexture 纹理对象:
QOpenGLTexture *textureWall = nullptr;
textureWall = new QOpenGLTexture(QImage(":/wall.jpg"));
- 添加顶点数据属性
float vertices_data[32] = {
// 所有的值是在[-1, 1]之间的
// ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上
};
- 修改顶点格式:由于新增了一个额外的顶点属性,纹理属性,所以需要更新顶点解析函数
- 由于这里新加了纹理数据它是一个vec2的数据,所以顶点解析函数就需要变化。
// 开启VAO管理的第一个属性值
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8 * sizeof (float),(void*)0);
glEnableVertexAttribArray(0);
// 开启VAO管理的第二个属性值
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);
// 开启VAO管理的第三个属性值
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6* sizeof(float)));
glEnableVertexAttribArray(2);
// 在绘制前设置绑定纹理单元 这个在后面有说到
textureWall->bind(0);
glDrawElements(GL_TRIANGLES, 6,GL_UNSIGNED_INT,0);
index
:顶点属性数组的索引。OpenGL支持最多16个顶点属性数组,索引从0开始。size
:每个顶点属性的组件数量。例如,如果设置为2,则表示每个顶点有两个组件。type
:顶点属性的数据类型。例如,可以是GL_FLOAT
、GL_INT
、GL_UNSIGNED_SHORT
等。normalized
:一个布尔值,表示是否将顶点属性值归一化。如果设置为GL_TRUE
,则顶点属性值将被归一化到0到1的范围内。stride
:顶点属性之间的步长,以字节为单位。步长可以为0,表示顶点属性紧邻在一起。pointer
:指向顶点属性数据的指针。指针可以是const void *
类型,也可以是NULL
,表示顶点属性数据位于当前的顶点缓冲区中。
修改顶点着色器:
#version 330 core
layout(location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout (location = 2) in vec2 aTexCord;
out vec4 vertexColor;
out vec2 TexCord;
void main(){
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);
vertexColor = vec4(aColor,1.0f);
TexCord = aTexCord;
}
修改片段着色器:
#version 330 core
in vec4 vertexColor;
out vec4 FragColor;
in vec2 TexCord;
uniform sampler2D textWrap; // 纹理采样器
void main(){
FragColor = texture(textWrap,TexCord);
}
-
sampler2D:纹理采样器 它以纹理类型作为后缀,比如sampler1D、sampler3D
- texture函数:采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
三 纹理环绕方式
这部分解释转载至OpenGL中文手册 为了方便我也摘录了过来,顺便加上了自己的一些理解。
纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外会发生什么?OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:
环绕方式 | 描述 |
---|---|
GL_REPEAT | 对纹理的默认行为。重复纹理图像。 |
GL_MIRRORED_REPEAT | 和GL_REPEAT一样,但每次重复图片是镜像放置的。 |
GL_CLAMP_TO_EDGE | 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
GL_CLAMP_TO_BORDER | 超出的坐标为用户指定的边缘颜色。 |
当纹理坐标超出默认范围时,每个选项都有不同的视觉效果输出。我们来看看这些纹理图像的例子:
前面提到的每个选项都可以使用glTexParameter*函数对单独的一个坐标轴设置(s
、t
(如果是使用3D纹理那么还有一个r
)它们和x
、y
、z
是等价的):
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- 第一个参数指定了纹理目标;我们使用的是2D纹理,因此纹理目标是GL_TEXTURE_2D。
- 第二个参数需要我们指定设置的选项与应用的纹理轴。我们打算配置的是
WRAP
选项,并且指定S
和T
轴。 - 最后一个参数需要我们传递一个环绕方式(Wrapping),在这个例子中OpenGL会给当前激活的纹理设定纹理环绕方式为GL_MIRRORED_REPEAT。
如果我们选择GL_CLAMP_TO_BORDER选项,我们还需要指定一个边缘的颜色。这需要使用glTexParameter函数的fv
后缀形式,用GL_TEXTURE_BORDER_COLOR作为它的选项,并且传递一个float数组作为边缘的颜色值:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
3.1 纹理单元
一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元。
对于Qt中激活纹理单元和绑定纹理是集成在一起的,只需要调用对应的bind(n)即可,OpenGL保证至少有16个纹理单元供你使用,也就是n可以取0-15,这只是Qt中的使用方法,C++原生中请参考中文手册。
QOpenGLShaderProgram shaderProgramObject;
// 需要设置采样器对应的是哪个纹理单元 比如设置textureWall 对应纹理单元0
shaderProgramObject.setUniformValue("textureWall",0);
//然后再片段着色器中设置纹理采样器 uniform sampler2D textureWall; // 纹理采样器
QOpenGLTexture *textureWall;
textureWall->bind(0);
3.1 纹理环绕实现
创建纹理的方式同上面相同,在qt中唯一不同的是在调用 glTexParameteri函数时需要先绑定纹理单元,这样才能知道是基于哪个纹理单元实现的
// 为纹理单元0 设置环绕方式
textWrap->bind(1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 为纹理单元1 设置环绕方式
textSmile->bind(2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
四 纹理过滤
纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素(Texture Pixel,也叫Texel,译注1)映射到纹理坐标。当你有一个很大的物体但是纹理的分辨率很低的时候这就变得很重要了。你可能已经猜到了,OpenGL也有对于纹理过滤(Texture Filtering)的选项。纹理过滤有很多个选项,但是现在我们只讨论最重要的两种:GL_NEAREST和GL_LINEAR。
- 纹理坐标不依赖与分辨率,但OpenGL需要知道怎样将纹理像素映射到纹理坐标
- 纹理坐标的精度是无限的,可以是任意的浮点数
- 纹理像素数是有限的(图片的分辨率),一张图片它的纹理像素是确定的,当你把图片放大时,占用的屏幕的物理像素就不一样了,但它的纹理像素数是是不会变的,
- 注意纹理像素与屏幕像素不同
- 可以简单理解纹理像素,也就是这个作为纹理的图片大小它其实是不会变的
译注1
Texture Pixel也叫Texel,你可以想象你打开一张
.jpg
格式图片,不断放大你会发现它是由无数像素点组成的,这个点就是纹理像素;注意不要和纹理坐标搞混,纹理坐标是你给模型顶点设置的那个数组,OpenGL以这个顶点的纹理坐标数据去查找纹理图像上的像素,然后进行采样提取纹理像素的颜色。
当图片被放大时,他就会通过纹理坐标去采样,图片越大,它的纹理坐标的精度就会越高,就会不断的去采样,根据纹理坐标去向纹理像素中获取颜色(可以理解成向纹理图片获取颜色,并且这个图片是不会变的),比如你放的很大的时候,同一片区域去采样都获取到的是同一个颜色,这就会导致一块很大的区域和旁边的区域颜色相差很大,因为给的坐标采样到了哪个坐标他就会捕获这个坐标出的像素.但这样如果图片过大就会导致图片的边界干很强,图片不够光滑..这就是默认的采样方式。
可以通过纹理过滤来处理这种效果。
4.1 GL_NEAREST(邻近过滤)
纹理图片大于实际渲染图片时选择较好
- OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。下图中你可以看到四个像素,加号代表纹理坐标。左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:
4.2 GL_LINEAR(线性过滤)
纹理图片小于实际渲染图片时选择较好
- 基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。下图中你可以看到返回的颜色是邻近像素的混合色:
4.3 对比
那么这两种纹理过滤方式有怎样的视觉效果呢?让我们看看在一个很大的物体上应用一张低分辨率的纹理会发生什么吧(纹理被放大了,每个纹理像素都能看到):
- GL_NEAREST产生了颗粒状的图案,我们能够清晰看到组成纹理的像素
- GL_LINEAR能够产生更平滑的图案,很难看出单个的纹理像素。GL_LINEAR可以产生更真实的输出,但有些开发者更喜欢8-bit风格,所以他们会用GL_NEAREST选项。
4.3.1 个人理解:
一般来说,线性过滤更适合大多数情况,因为它提供了更好的视觉效果。然而,在某些情况下,例如需要快速渲染或保持像素艺术风格时,邻近过滤可能是更好的选择。
4.3.2 放大缩小
-
邻近过滤:
- 使用场景:当您需要快速渲染,或者希望保持原始纹理像素的清晰边缘时,可以使用邻近过滤。它是最简单的过滤方法,速度快,但可能会导致纹理看起来有锯齿状。
- 优点:快速,保持纹理像素的清晰边缘。
- 缺点:可能会产生明显的锯齿状边缘,特别是在放大纹理时。
-
线性过滤:
- 使用场景:当您需要更平滑的纹理外观,或者需要在纹理放大时减少锯齿状边缘时,可以使用线性过滤。线性过滤通过在相邻像素之间进行插值来计算采样点的颜色,从而提供更平滑的过渡效果。
- 优点:提供更平滑的纹理外观,减少锯齿状边缘。
- 缺点:比邻近过滤慢一些,特别是在纹理放大时。
当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。我们需要使用glTexParameter*函数为放大和缩小指定过滤方式。这段代码看起来会和纹理环绕方式的设置很相似:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
五 多级渐远纹理(MIP映射)
根据观察者与物体的距离,参考临界值,选择最适合物体的那个级别的纹理
简单来说,就是当一个物体离观察者较远时,它的大小在屏幕上会变小。这时,OpenGL可以选择一个较低分辨率的MIP级别来渲染该物体,这样既能保证视觉效果,又能减少内存使用和提高渲染效率。相反,如果物体离观察者较近,屏幕上的大小较大,OpenGL会选择一个较高分辨率的MIP级别来渲染,以提供更好的细节和纹理质量。
- 多级渐远纹理是一种预处理技术,它生成一系列不同分辨率的纹理,每个分辨率对应于原始纹理的一个缩放版本。当物体被缩放时,OpenGL会选择合适的MIP级别进行纹理采样,从而改善过滤效果并减少内存使用。
多级渐远纹理理念 :距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。让我们看一下多级渐远纹理是什么样子的:
5.1 切换多级渐远纹理级别
就如上如所示,两个不同级别的纹理层 之间会产生一些很明显的边界感,这样视觉上很明显会不适,所以在切换渐远纹理级别时也可以使用纹理过滤来避免这种情况。
过滤方式 | 描述 |
---|---|
GL_NEAREST_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样 |
GL_LINEAR_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样 |
GL_NEAREST_MIPMAP_LINEAR | 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样 |
GL_LINEAR_MIPMAP_LINEAR | 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样 |
就像纹理过滤一样,我们可以使用glTexParameteri将过滤方式设置为前面四种提到的方法之一:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
一个常见的错误是,将放大过滤的选项设置为多级渐远纹理过滤选项之一。这样没有任何效果,因为多级渐远纹理主要是使用在纹理被缩小的情况下的:纹理放大不会使用多级渐远纹理,为放大过滤设置多级渐远纹理的选项会产生一个GL_INVALID_ENUM错误代码。
5.2 Qt使用多级渐远纹理
QOpenGLTexture *textureWall = nullptr;
textureWall = new QOpenGLTexture(QImage(":/wall.jpg"));
textWrap->generateMipMaps();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
// 对于方大 不要使用渐远纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
六 练习
6.1 纹理颜色与顶点颜色混合
- 附上需要修改的代码,下载源码后,可更新展示效果是否与下面一样
// 构造加载纹理
QOpenGLTexture *container = new QOpenGLTexture(QImage(":/container.jpg").mirrored());
// 设置纹理采样器
shaderProgramObject.setUniformValue("textWrap",2);
// paintGL()绘制函数中
container->bind(2); 绑定纹理采样单元
glDrawElements(GL_TRIANGLES, 6,GL_UNSIGNED_INT,0);
// 顶点着色器
#version 330 core
layout(location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout (location = 2) in vec2 aTexCord;
out vec4 vertexColor;
out vec2 TexCord;
void main(){
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);
vertexColor = vec4(aColor,1.0f);
TexCord = aTexCord;
}
// 片段着色器
#version 330 core
in vec4 vertexColor;
out vec4 FragColor;
in vec2 TexCord;
uniform sampler2D textWrap; // 纹理采样器
void main(){
FragColor = texture(textWrap,TexCord);
}
- 将纹理颜色与顶点颜色混合,来获得更有取得效果
FragColor = texture(textWrap,TexCord)* vertexColor;
6.2 同时使用两个纹理绘制
// 构造纹理
textSmile = new QOpenGLTexture(QImage(":/awesomeface.png").mirrored());
shaderProgramObject.bind();
shaderProgramObject.setUniformValue("textureSmile",1);
// 绘制
textSmile->bind(1);
container->bind(2); // 设置cpu端
glDrawElements(GL_TRIANGLES, 6,GL_UNSIGNED_INT,0);
#version 330 core
in vec4 vertexColor;
out vec4 FragColor;
in vec2 TexCord;
uniform sampler2D textWrap; // 纹理采样器
uniform sampler2D textureSmile;
void main(){
FragColor = mix(texture(textWrap, TexCord), texture(textureSmile, TexCord), 0.2);
}
6.3 将笑脸朝另一个方向看
#version 330 core
layout(location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
layout (location = 2) in vec2 aTexCord;
out vec4 vertexColor;
out vec2 TexCord;
void main(){
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);
vertexColor = vec4(aColor,1.0f);
// 修改这里即可
TexCord = vec2(1-aTexCord.x,aTexCord.y);
}
或者在片段着色器中修改
#version 330 core
in vec4 vertexColor;
out vec4 FragColor;
in vec2 TexCord;
uniform sampler2D textWrap; // 纹理采样器
uniform sampler2D textureSmile;
void main(){
FragColor = mix(texture(textWrap, TexCord), texture(textureSmile, vec2(1 - TexCord.x,TexCord.y)), 0.2);
}
6.4 在箱子的角落放4个笑脸
- 尝试用不同的纹理环绕方式,设定一个从
0.0f
到2.0f
范围内的(而不是原来的0.0f
到1.0f
)纹理坐标。试试看能不能在箱子的角落放置4个笑脸。记得一定要试试其它的环绕方式。参考解答,结果。 -
float vertices_data[32] = { // 练习 6.4 // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // top right 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 2.0f // top left }; glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
6.5 显示单个像素效果
- 尝试在矩形上只显示纹理图像的中间一部分,修改纹理坐标,达到能看见单个的像素的效果。尝试使用GL_NEAREST的纹理过滤方式让像素显示得更清晰:参考解答
float vertices_data[32] = {
// 练习 6.5
// ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.55f, 0.55f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.55f, 0.45f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.45f, 0.45f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.45f, 0.55f // top left
};
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
6.6 使用上下键改变可见度
- 使用一个uniform变量作为mix函数的第三个参数来改变两个纹理可见度,使用上和下键来改变箱子或笑脸的可见度:参考解答。
#version 330 core
in vec4 vertexColor;
out vec4 FragColor;
in vec2 TexCord;
uniform sampler2D textWrap; // 纹理采样器
uniform sampler2D textureSmile;
uniform float ratio;
void main(){
FragColor = mix(texture(textWrap,TexCord),texture(textureSmile,TexCord),ratio);
}
COpenGlWidget::COpenGlWidget(QWidget *parent):QOpenGLWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
m_shape = Rect;
flag = false;
installEventFilter(this);
}
float ratio = 0.5;
bool COpenGlWidget::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
switch (keyEvent->key()) {
case Qt::Key_Up:
ratio += 0.1;
break;
case Qt::Key_Down:
ratio -= 0.1;
break;
default:
break;
}
if(ratio > 1) ratio = 1;
if(ratio < 0) ratio = 0;
shaderProgramObject.bind();
shaderProgramObject.setUniformValue("ratio",ratio);
update();
}
return QWidget::eventFilter(watched,event);
}