在上章3.QOpenGLWidget-通过着色器来渲染渐变三角形,我们为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像。但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色。这将会产生很多额外开销。

所以使用纹理(Texture)。纹理是一个2D图片(甚至也有1D和3D的纹理),你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了.
下面你会看到之前教程的那个三角形贴上了一张砖墙图片:
  • 除了图像以外,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上,比如传输大量RGB数据显示一幅画面

为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。

纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。使用纹理坐标获取纹理颜色叫做采样(Sampling)。纹理坐标起始于(0, 0),也就是纹理图片的左下角,终始于(1, 1),即纹理图片的右上角。

纹理坐标看起来就像这样:
float texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};
对纹理采样的解释非常宽松,它可以采用几种不同的插值方式。所以我们需要自己告诉OpenGL该怎样对纹理采样。
 
1.QOpenGLTexture纹理对象介绍
 在QT中,通过QOpenGLTexture类封装了一个OpenGL纹理对象,QOpenGLTexture可以很容易地使用OpenGL纹理和它们提供的无数特性和目标,这取决于你的OpenGL实现的能力。
 
QOpenGLTexture纹理的范围是从(0, 0)到(1, 1),如果超过范围后,opengl默认是重复纹理图像,当然也可以通过setWrapMode(CoordinateDirection direction, WrapMode mode)函数来重新设置,setWrapMode函数参数定义如下:
void QOpenGLTexture::setWrapMode(CoordinateDirection direction, WrapMode mode);
//direction:坐标方向,纹理的坐标系统和xyz坐标系统一样,s对应x,t对应y,r对应z(3D纹理时才设置z)
//mode:纹理模式,Repeat(超出部分重复纹理)MirroredRepeat(超出部分镜像重复纹理)ClampToEdge(超出部分显示纹理临近的边缘颜色值)、

QOpenGLTexture放大缩小的过滤方式是通过 setMinMagFilters(Filter minificationFilter, Filter magnificationFilter)函数实现,比如:

 m_texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Nearest);
//参数1:设置缩小方式 ,参数2:设置放大方式
//设置缩小和放大的方式,缩小图片采用LinearMipMapNearest线性过滤,并使用多级渐远纹理邻近过滤,放大图片采用:Nearest邻近过滤
具体可以设置的参数有:
  • Nearest :  邻近过滤,速度快,可能有锯齿,等同于opengl中的GL_NEAREST
  • Linear :  线性过滤,将最接近的2*2个颜色,计算出一个插值,速度慢,画面好,等同于opengl中的GL_LINEAR
  • //下面4个多级渐远纹理参数只能用在缩小方式参数1上面
  • NearestMipMapNearest :  使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样,等同于GL_NEAREST_MIPMAP_NEAREST
  • NearestMipMapLinear :  在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样,等同于GL_NEAREST_MIPMAP_LINEAR
  • LinearMipMapNearest :  使用最邻近的多级渐远纹理级别,并使用线性插值进行采样,等同于GL_LINEAR_MIPMAP_NEAREST
  • LinearMipMapLinear :  在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样,GL_LINEAR_MIPMAP_LINEAR
缩小之多级渐远纹理
当纹理大于渲染屏幕时,使用纹理缩小算法(minifying)来渲染屏幕,就可以设置NearestMipMapNearest 等4个参数,比如在一个场景中,由于远处的物体只占有很少的片段(近大远小,非常远的物体看起来就像一个点),OpenGL使用高分辨率纹理为这些片段后去正确的颜色值是很困难的,它需要对一个跨过纹理很大部分的片段只拾取一个颜色,比如一个物体太远,只占有1个像素值,而该物体对应的纹理是个高分辨率图片,那么到底选用图片中哪个像素值?
OpenGL使用一种叫做多级渐远纹理(Mipmap)的概念来解决这个问题,它简单来说就是将一个图像生成一系列的纹理图像,后一个纹理图像是前一个的二分之一,直到生成只有1个像素大小的图片为止,如下图所示:
然后绘制物体时,把摄像机到物体的距离与阙值作比较,在不同的距离空间内选用不同的纹理图像。由于距离远,解析度不高也不会被用户注意到。
所以多级渐远纹理只应用于纹理被缩小的情况下。
 
2.源码实现
具体代码如下所示:
#include "myglwidget.h"
#include <QtDebug> //GLSL3.0版本后,废弃了attribute关键字(以及varying关键字),属性变量统一用in/out作为前置关键字
#define GL_VERSION "#version 330 core\n" #define GLCHA(x) #@x //加单引号
#define GLSTR(x) #x //加双引号
#define GET_GLSTR(x) GL_VERSION#x const char *vsrc = GET_GLSTR( layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord; out vec3 ourColor;
out vec2 TexCoord; void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
} ); const char *fsrc =GET_GLSTR( out vec4 FragColor; in vec3 ourColor;
in vec2 TexCoord; uniform sampler2D ourTexture; void main()
{
FragColor = texture(ourTexture, TexCoord);
}
); myGlWidget::myGlWidget(QWidget *parent):QOpenGLWidget(parent)
{ } void myGlWidget::paintGL()
{
// 绘制
// glViewport(0, 0, width(), height());
glClear(GL_COLOR_BUFFER_BIT); // 渲染Shader
vao.bind(); //绑定激活vao
m_texture->bind();
glDrawArrays(GL_TRIANGLES, 0, 3); //绘制3个定点,样式为三角形 m_texture->release();
vao.release(); //解绑
} void myGlWidget::initializeGL()
{ // 为当前环境初始化OpenGL函数
initializeOpenGLFunctions(); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //设置背景色为白色 //初始化纹理对象
m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_texture->setData(QImage(":wall1"));
m_texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Nearest);
//设置缩小和放大的方式,缩小图片采用LinearMipMapLinear线性过滤,并使用多级渐远纹理邻近过滤,放大图片采用:Nearest邻近过滤

m_texture->setWrapMode(QOpenGLTexture::DirectionS,QOpenGLTexture::Repeat);
m_texture->setWrapMode(QOpenGLTexture::DirectionT,QOpenGLTexture::Repeat); //创建着色器程序 program = new QOpenGLShaderProgram;
program->addShaderFromSourceCode(QOpenGLShader::Vertex,vsrc);
program->addShaderFromSourceCode(QOpenGLShader::Fragment,fsrc); program->link();
program->bind();//激活Program对象 //初始化VBO,将顶点数据存储到buffer中,等待VAO激活后才能释放
float vertices[] = {
// 位置 // 颜色 //纹理坐标
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 0.0f,// 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 2.0f // 顶部
}; vbo.create();
vbo.bind(); //绑定到当前的OpenGL上下文,
vbo.allocate(vertices, sizeof(vertices));
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw); //设置为一次修改,多次使用 //初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
vao.create();
vao.bind(); // void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8 * sizeof(float)); //设置aPos顶点属性
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 8 * sizeof(float)); //设置aColor顶点颜色
program->setAttributeBuffer(2, GL_FLOAT, 6 * sizeof(float), 2, 8 * sizeof(float)); //设置纹理坐标 //offset:第一个数据的偏移量
//tupleSize:一个数据有多少个元素,比如位置为xyz,颜色为rgb,所以是3
//stride:步长,下个数据距离当前数据的之间距离,比如右下位置和左下位置之间间隔了:3个xyz值+3个rgb值,所以填入 6 * sizeof(float) program->enableAttributeArray(0); //使能aPos顶点属性
program->enableAttributeArray(1); //使能aColor顶点颜色
program->enableAttributeArray(2); //使能纹理坐标 //解绑所有对象
vao.release();
vbo.release(); }
void myGlWidget::resizeEvent(QResizeEvent *e)
{ }
 由于我们设置的三角形纹理坐标是

  //纹理坐标
2.0f, 0.0f,// 右下
0.0f, 0.0f, // 左下
1.0f, 2.0f // 顶部

所以是超过了范围(0, 0)到(1, 1),假如我们绘制mode改为QOpenGLTexture::ClampToEdge,就可以看出其实三角形是大于图片的,修改代码如下:

 m_texture->setWrapMode(QOpenGLTexture::DirectionS,QOpenGLTexture::ClampToEdge);
m_texture->setWrapMode(QOpenGLTexture::DirectionT,QOpenGLTexture::ClampToEdge);

显示界面如下所示:

在代码中,我们还保存了上章着色器颜色渲染相关代码,所以我们可以把得到的纹理颜色与顶点颜色混合,来获得更有趣的混合效果,修改fragment源码:

FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);

编译并运行,如下图所示:

3.纹理叠加

在上个源码实现中,我们在fragment源码中定义了一个uniform类型的ourTexture变量,但是我们却没有给它赋值就已经实现了纹理,这是因为如果有一个纹理的话,默认是激活的.
假如有多个纹理的话,我们就需要设置其纹理位置值(也称为一个纹理单元(Texture Unit))。然后再将对应的QOpenGLTexture绑定上.
设置如下所示:

program->setUniformValue("texture1", 0);
m_texture->bind(); //将m_texture绑定在"texture1"上
program->setUniformValue("texture2", 1);
m_texture2->bind(1);//将m_texture2绑定在"texture1"上
....

修改fragment源码:

#version 330 core
... uniform sampler2D texture1;
uniform sampler2D texture2; void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.7);
}

mix函数作用是将前两个纹理参数进行融合,根据第三个参数值来进行线性插值,如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.7表示返回30%的第一个输入颜色和70%的第二个输入颜色。

然后再加入一个我的大学图片:

最终和砖墙叠加后的效果如下所示:

具体源码如下所示:

#include "myglwidget.h"
#include <QtDebug> //GLSL3.0版本后,废弃了attribute关键字(以及varying关键字),属性变量统一用in/out作为前置关键字
#define GL_VERSION "#version 330 core\n" #define GLCHA(x) #@x //加单引号
#define GLSTR(x) #x //加双引号
#define GET_GLSTR(x) GL_VERSION#x const char *vsrc = GET_GLSTR( layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord; out vec3 ourColor;
out vec2 TexCoord; void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
} ); const char *fsrc =GET_GLSTR( out vec4 FragColor; in vec3 ourColor;
in vec2 TexCoord; uniform sampler2D texture1;
uniform sampler2D texture2; void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.7);
} ); myGlWidget::myGlWidget(QWidget *parent):QOpenGLWidget(parent)
{ } void myGlWidget::paintGL()
{
// 绘制
int w = width();
int h = height();
int side = qMin(w, h);
glViewport((w - side) / 2, (h - side) / 2, side, side);
glClear(GL_COLOR_BUFFER_BIT); // 渲染Shader
vao.bind(); //绑定激活vao
m_texture->bind(); program->setUniformValue("texture1", 0);
m_texture->bind();
program->setUniformValue("texture2", 1);
m_texture2->bind(1); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); //绘制3个定点,样式为三角形 m_texture->release();
m_texture2->release();
vao.release(); //解绑
} void myGlWidget::initializeGL()
{ // 为当前环境初始化OpenGL函数
initializeOpenGLFunctions(); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); //设置背景色为白色 //初始化纹理对象
m_texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_texture->setData(QImage(":wall")); //加载砖块图片
m_texture->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Nearest);
//设置缩小和放大的方式,缩小图片采用LinearMipMapLinear线性过滤,并使用多级渐远纹理邻近过滤,放大图片采用:Nearest邻近过滤 m_texture->setWrapMode(QOpenGLTexture::DirectionS,QOpenGLTexture::Repeat);
m_texture->setWrapMode(QOpenGLTexture::DirectionT,QOpenGLTexture::Repeat); //初始化纹理对象
m_texture2 = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_texture2->setData(QImage(":my").mirrored()); //返回图片的镜像,设置为Y轴反向,因为在opengl的Y坐标中,0.0对应的是图片底部 m_texture2->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Nearest);
//设置缩小和放大的方式,缩小图片采用LinearMipMapLinear线性过滤,并使用多级渐远纹理邻近过滤,放大图片采用:Nearest邻近过滤 m_texture2->setWrapMode(QOpenGLTexture::DirectionS,QOpenGLTexture::Repeat);
m_texture2->setWrapMode(QOpenGLTexture::DirectionT,QOpenGLTexture::Repeat); //创建着色器程序 program = new QOpenGLShaderProgram;
program->addShaderFromSourceCode(QOpenGLShader::Vertex,vsrc);
program->addShaderFromSourceCode(QOpenGLShader::Fragment,fsrc); program->link();
program->bind();//激活Program对象 //初始化VBO,将顶点数据存储到buffer中,等待VAO激活后才能释放
float vertices[] = {
// ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -
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 // 左上
}; vbo.create();
vbo.bind(); //绑定到当前的OpenGL上下文,
vbo.allocate(vertices, sizeof(vertices));
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw); //设置为一次修改,多次使用 //初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
vao.create();
vao.bind(); // void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8 * sizeof(float)); //设置aPos顶点属性
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 8 * sizeof(float)); //设置aColor顶点颜色
program->setAttributeBuffer(2, GL_FLOAT, 6 * sizeof(float), 2, 8 * sizeof(float)); //设置aColor顶点颜色 //offset:第一个数据的偏移量
//tupleSize:一个数据有多少个元素,比如位置为xyz,颜色为rgb,所以是3
//stride:步长,下个数据距离当前数据的之间距离,比如右下位置和左下位置之间间隔了:3个xyz值+3个rgb值,所以填入 6 * sizeof(float) program->enableAttributeArray(0); //使能aPos顶点属性
program->enableAttributeArray(1); //使能aColor顶点颜色
program->enableAttributeArray(2); //使能aColor顶点颜色 //解绑所有对象
vao.release();
vbo.release(); }
void myGlWidget::resizeEvent(QResizeEvent *e)
{ }

4.QOpenGLWidget-对三角形进行纹理贴图、纹理叠加的更多相关文章

  1. Unity3D ShaderLab压缩混合纹理贴图

    Unity3D ShaderLab压缩混合纹理贴图 纹理可以用于存储大量的数据,我们可以把多个图像打包存储在单一的RGBA纹理上,然后通过着色器代码提取这些元素, 我们就可以使用每个图片的RGBA通道 ...

  2. 从零开始的openGL——四、纹理贴图与n次B样条曲线

    前言 在上篇文章中,介绍了如何加载绘制模型以及鼠标交互的实现,并且遗留了个问题,就是没有模型表面没有纹理,看起来很丑.这篇文章将介绍如何贴纹理,以及曲线的绘制. 纹理贴图 纹理加载 既然是贴图,那首先 ...

  3. Directx11学习笔记【十七】纹理贴图

    本文由zhangbaochong原创,转载请注明出处http://www.cnblogs.com/zhangbaochong/p/5596180.html 在之前的例子中,我们实现了光照和材质使得场景 ...

  4. android ndk调用OpenGL 实现纹理贴图Texture

    android ndk调用OpenGL 实现纹理贴图Texture 时间 2014-06-25 05:24:39  CSDN博客 原文  http://blog.csdn.net/chrisfxs/a ...

  5. Android OpenGL ES 开发(九): OpenGL ES 纹理贴图

    一.概念 一般说来,纹理是表示物体表面的一幅或几幅二维图形,也称纹理贴图(texture).当把纹理按照特定的方式映射到物体表面上的时候,能使物体看上去更加真实.当前流行的图形系统中,纹理绘制已经成为 ...

  6. Opengl ES 1.x NDK实例开发之六:纹理贴图

    开发框架介绍请參见:Opengl ES NDK实例开发之中的一个:搭建开发框架 本章在第三章(Opengl ES 1.x NDK实例开发之三:多边形的旋转)的基础上演示怎样使用纹理贴图,分别实现了三角 ...

  7. 【Aladdin Unity3D Shader编程】之四 贴图纹理

    关于纹理贴图介绍 纹理坐标也叫UV坐标,UV坐标都是0~1,并不是我们所理解的像素坐标,相当于是一个百分比. 编写shader映射纹理 将纹理的颜色取代漫反射的颜色 Shader "Alad ...

  8. OpenGL入门1.4:纹理/贴图Texture

    每一个小步骤的源码都放在了Github 的内容为插入注释,可以先跳过 前言 游戏玩家对Texture这个词应该不陌生,我们已经知道了怎么为每个顶点添加颜色来增加图形的细节,但,如果想让图形看起来更真实 ...

  9. (转)使用OpenGL显示图像(七)Android OpenGLES2.0——纹理贴图之显示图片

    转:http://blog.csdn.net/junzia/article/details/52842816 前面几篇博客,我们将了Android中利用OpenGL ES 2.0绘制各种形体,并在上一 ...

随机推荐

  1. 2017年PHP程序员未来路在何方(转)

    PHP 从诞生到现在已经有20多年历史,从Web时代兴起到移动互联网退潮,互联网领域各种编程语言和技术层出不穷, Node.js . GO . Python 不断地在挑战 PHP 的地位.这些技术的推 ...

  2. linux 多进程

    Linux下的多进程编程初步 Linux下的多进程编程初步 多进程编程 1 Linux下进程的结构 2 Linux下的进程控制 21 僵尸进程 22 fork 23 exec 函数族 3 Linux下 ...

  3. Oracle数据库sqlldr工具的使用

    sqlldr导入文本内容到数据库表时,需要指定一个ctl文件(控制文件),通过该文件来完成数据的导入. 1 首先创建一个表student create table student( stu_id nu ...

  4. Burger King使用RayOnSpark进行基于实时情景特征的快餐食品推荐

    作者:Luyang Wang, Kai Huang, Jiao Wang, Shengsheng Huang, Jason Dai 基于深度学习的推荐模型已广泛应用于各种电商平台中,为用户提供推荐.目 ...

  5. Java审计之文件操作漏洞

    Java审计之文件操作漏洞篇 0x00 前言 本篇内容打算把Java审计中会遇到的一些文件操作的漏洞,都给叙述一遍.比如一些任意文件上传,文件下载,文件读取,文件删除,这些操作文件的漏洞. 0x01 ...

  6. 树莓派Raspberry Pi OS(原Raspbian)系统常用配置

    首次开机自动连接WIFI 在资源浏览器中打开刚写好Raspberry Pi OS(之前叫Raspbian)系统的SD卡,如果有boot目录则在boot目录中新建一个名为wpa_supplicant.c ...

  7. C#实现创建、编辑NX表达式

    在NX8.5中使用C#编辑表达式中有个坑,part.Expressions.Edit该方法鲁棒性很差,当表达式有错时也能编辑成功(手动在NX中增加错误表达式会有弹框,无法创建,而该方法却可以,疑是bu ...

  8. PHP_Code_Challenge 1~30

    1. 1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY= <?php error_reporting(0); require __DIR__ ...

  9. 从GitHub建站迁移到服务器(Java环境)

    一.购买域名和服务器 域名:阿里云:lookabc.cn 服务器:腾讯云,学生价格便宜 二.域名解析 注意:由于域名和服务器不在同一家,需要域名迁入和迁出 三.搭建服务器环境 1.下载xftp6和xs ...

  10. element弹框的的this.$alert、this.$prompt方法用法

    调用$alert方法即可打开消息提示,它模拟了系统的 alert,无法通过按下 ESC 或点击框外关闭 调用$prompt方法即可打开消息提示,它模拟了系统的 prompt