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


所以使用纹理(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#xconst 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);// 渲染Shadervao.bind();       //绑定激活vaom_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);

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

texture()函数意义

其中的texture函数主要是来采样不同坐标的纹理颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。

texture函数会根据当前所在纹理坐标去获取对应的颜色。然后输出到FragColor 显示颜色.

3.纹理叠加

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

设置如下所示:

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

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

修改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#xconst 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);// 渲染Shadervao.bind();       //绑定激活vaom_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】纹理贴图 ( 纹理 Texture 简介 | 为 3D 模型设置纹理贴图 )

    文章目录 一.纹理 Texture 简介 二.为 3D 模型设置纹理贴图 一.纹理 Texture 简介 上一篇博客 [Unity3D]材质 Material ( 材质简介 | 创建材质 | 设置材质 ...

  2. 计算机图形学与opengl C++版 学习笔记 第5章 纹理贴图

    目录 5.1 加载纹理图像文件 5.2 纹理坐标 5.3 创建纹理对象 5.4 构建纹理坐标 5.5 将纹理坐标载入缓冲区 5.6 在着色器中使用纹理:采样器变量和纹理单元 5.7 纹理贴图:示例程序 ...

  3. Windows 8 Directx 开发学习笔记(十一)地形纹理贴图

    前一篇实现木箱贴图时,木箱的六个面都正好用一整张纹理图,即六个面的纹理坐标均在[0,1]内.然而在为比较大的模型贴图时,像山峰河谷模型,如果只用一张纹理图,那么每个三角形只得到几个纹理元素,无法为提供 ...

  4. Windows 8 Directx 开发学习笔记(十)纹理贴图实现旋转的木箱

    纹理贴图映射(texturemapping)是可以显著提高场景细节和真实感的一种技术,基本原理是将图像数据映射到3D三角形表面(之前的文章提到过,三维模型其实是由很多个三角形拼接而成).当使用纹理资源 ...

  5. opengl生成图片php,(转)使用OpenGL显示图像(七)Android OpenGLES2.0——纹理贴图之显示图片...

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

  6. 基于VC++的3D地形绘制与纹理贴图

    前言 随着地理信息系统产业的发展,三维产品也在生活中处处吸引着我们的眼球.作为数字城市的核心内容,城市模型的构建成为了目前研究的热点.OpenGL是独立于操作系统和硬件环境的三维图形库,其为实现逼真的 ...

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

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

  8. OpenGL ES实现三棱锥纹理贴图

    这是老师布置的课后作业,闲来无事分享出来,也加深一遍自己的印象~ 自己定义一个MyRenderer.java类: package com.example.shiyan3_2;import androi ...

  9. Android Studio OpenGL ES绘制三棱锥/四面体的多纹理贴图 每个面使用一张图片渲染

    本文参考了王刚的<疯狂Android讲义(第3版)>P554-P559 要求:利用OpenGL ES绘制一个三棱锥,并对每个面进行纹理贴图,每个面使用不同的图片进行渲染. 环境:Andro ...

最新文章

  1. MySQL · myrocks · MyRocks之memtable切换与刷盘
  2. java如何查看调用记录_查看Java记录
  3. JavaScript进阶(一)--执行上下文
  4. web前端好学吗?如何能提高CSS编写技巧 提高Web前端开发效率
  5. 自动量策略的开发和优化
  6. Red5 9的安装配置以及AS3连接red5简单示例 .
  7. GooFlow入门使用
  8. caffe 安装教程(一)
  9. Android 面试必备 - 线程
  10. 爱奇艺,美团打车Java岗面试经历,这些问题我是真没抗住
  11. Web前端HTML、CSS测试:世界地球日
  12. js实现局部打印,并处理浏览器提示Avoid using document.write()导致无法打印问题
  13. 孙宇晨为恶俗炒作道歉;华为回应美国子公司裁员 600
  14. 如何通过手机话费余额充值Q币?
  15. 架构君公众号推荐 第一期
  16. OPPO的低代码实践:InnerEye低代码大屏
  17. 不积跬步无以至千里016
  18. mac下#include nested too deeply错误处理
  19. 初级产品经理需要具备哪些核心能力
  20. 汇总一下Intellij IDEA常用的牛逼插件

热门文章

  1. transmac装黑苹果_黑苹果安装LionGM版!!完全摆脱MacDriver、TransMac等工具
  2. 深度解析微信生态下8000万中老年网民的电商创新机会
  3. 被惊艳到了,4款精美绝伦的黑科技软件,用一次就会爱上
  4. x战警 天启高清完整版下载
  5. 威漫哨兵机器人_漫威:天启VS哨兵机器人谁更胜一筹?
  6. 泊松融合vs图像和谐化
  7. 广色域图片Android,ios 9.0系统App因广色域图片而导致的随机崩溃
  8. 服务器禁止某台电脑访问网站代码,禁止通过ip直接访问(apache,nginx,iis)Windows服务器操作系统 -电脑资料...
  9. 2021年第十二届蓝桥杯 Java B 组省赛填空题解析
  10. ZOJ-1203-Swordfish