1. 概述

本文基于C++语言,描述OpenGL的纹理

前置知识可参考:

笔者这里不过多描述每个名词、函数和细节,更详细的文档可以参考:

2. 纹理使用流程

参考:纹理 - LearnOpenGL CN (learnopengl-cn.github.io)

OpenGL中纹理使用流程大致如下:

  • 加载图片数据
  • 创建纹理对象
  • 绑定纹理对象
  • 使用图片数据生成纹理
  • 设置纹理坐标
  • 在顶点着色器中传递纹理
  • 在片段着色器中采用纹理
  • (绘制时)激活纹理并绑定纹理

3. 具体流程

3.1 加载图片数据

使用stb_image.h实现图片数据的读取

参考官方说明:nothings/stb: stb single-file public domain libraries for C/C++ (github.com)

使用以下方式导入:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

加载图片数据:

int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);

3.2 创建纹理对象

纹理对象也是使用ID进行引用:

unsigned int texture;
glGenTextures(1, &texture);

3.3 绑定纹理对象

绑定纹理对象,进行之后的纹理配置:

glBindTexture(GL_TEXTURE_2D, texture);

进行纹理配置:

// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

3.4 使用图片数据生成纹理

通过glTexImage2D()生成纹理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);

3.5 设置纹理坐标

纹理坐标:

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 // 左上
};

现在内存中的坐标格式:

指定纹理坐标属性:

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);

3.6 在顶点着色器中传递纹理坐标

在顶点着色器中编写GLSL实现数据传递:

#version 330 core
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;
}

3.7 在片段着色器中采用纹理

在片段着色器中接收纹理坐标与纹理:

#version 330 core
out vec4 FragColor; in vec3 ourColor;
in vec2 TexCoord; uniform sampler2D ourTexture; void main()
{
FragColor = texture(ourTexture, TexCoord);
}

使用GLSL内置的texture函数来采样纹理的颜色

3.8 激活纹理并绑定纹理(绘制时)

激活纹理单元并绑定纹理数据:

glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

4. 代码总结

一个简单的纹理绘制流程完整代码如下:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <math.h>
#include "Shader.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void process_input(GLFWwindow *window);
unsigned int *renderInit();
void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture);
bool checkCompile(unsigned int shader);
bool checkProgram(unsigned int shaderProgram); int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow *window = glfwCreateWindow(800, 600, "texture", nullptr, nullptr); if (window == nullptr)
{
std::cout << "Faild to create window" << std::endl;
glfwTerminate();
}
glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Faild to initialize glad" << std::endl;
return -1;
}
glad_glViewport(0, 0, 800, 600);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); unsigned int *arr = renderInit();
while (!glfwWindowShouldClose(window))
{
process_input(window); // render std::cout << arr[0] << " " << arr[1] << " " << arr[2] << " " << arr[3] << std::endl;
render(arr[0], arr[1], arr[3]); glfwSwapBuffers(window);
glfwPollEvents();
} glDeleteProgram(arr[0]);
glDeleteVertexArrays(1, &arr[1]);
glDeleteBuffers(1, &arr[2]); glfwTerminate();
return 0;
} void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
} void process_input(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
} unsigned int *renderInit()
{
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("../container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data); 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 // 左上
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(6 * sizeof(float)));
glEnableVertexAttribArray(2); Shader shaderProgram = Shader("../test.vs", "../test.fs");
shaderProgram.use(); glUniform1i(glGetUniformLocation(shaderProgram.ID, "texture1"), 0); // 手动设置
return new unsigned int[4]{shaderProgram.ID, VAO, VBO, texture};
}
void render(unsigned int shaderProgram, unsigned int VAO, unsigned int texture)
{
glClearColor(0.2, 0.3, 0.3, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture); glUseProgram(shaderProgram); glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}

顶点着色器test.vs

#version 330 core
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;
}

片段着色器test.fs

#version 330 core
out vec4 FragColor; in vec3 ourColor;
in vec2 TexCoord; uniform sampler2D texture1; void main()
{
FragColor = texture(texture1, TexCoord);
}

编译代码并运行:

5. 参考资料

[1]纹理 - LearnOpenGL CN (learnopengl-cn.github.io)

[2]nothings/stb: stb single-file public domain libraries for C/C++ (github.com)

[3]OpenGL学习笔记(五)纹理 - 知乎 (zhihu.com)

基于C++的OpenGL 03 之纹理的更多相关文章

  1. 基于Cocos2d-x学习OpenGL ES 2.0之多纹理

    没想到原文出了那么多错别字,实在对不起观众了.介绍opengl es 2.0的不多.相信介绍基于Cocos2d-x学习OpenGL ES 2.0之多纹理的,我是独此一家吧.~~ 子龙山人出了一个系列: ...

  2. 基于Cocos2d-x学习OpenGL ES 2.0系列——纹理贴图(6)

    在上一篇文章中,我们介绍了如何绘制一个立方体,里面涉及的知识点有VBO(Vertex Buffer Object).IBO(Index Buffer Object)和MVP(Modile-View-P ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——使用VBO索引(4)

    在上一篇文章中,我们介绍了uniform和模型-视图-投影变换,相信大家对于OpenGL ES 2.0应该有一点感觉了.在这篇文章中,我们不再画三角形了,改为画四边形.下篇教程,我们就可以画立方体了, ...

  4. Opengl ES之纹理贴图

    纹理可以理解为一个二维数组,它可以存储大量的数据,这些数据可以发送到着色器上.一般情况下我们所说的纹理是表示一副2D图,此时纹理存储的数据就是这个图的像素数据. 所谓的纹理贴图,就是使用Opengl将 ...

  5. Android OpenGL ES(八)----纹理编程框架

    1.把纹理载入进OpenGL中 我们的第一个任务就是把一个图像文件的数据载入到一个OpenGL的纹理中. 作为開始.让我们又一次舍弃第二篇的框架.又一次创建一个程序,新建一个util工具包,在该包下创 ...

  6. Linux OpenGL 实践篇-5 纹理

    纹理 在之前的实践中,我们所渲染的物体的表面颜色都是纯色或者根据顶点位置计算出的一个颜色,这种方式在表现物体细节方面是比较吃资源的,因为我们每增加一个细节,我们就需要定义更多的顶点及其属性.所以美术人 ...

  7. OpenGL 多线程共享纹理

    1:opengl 多线程共享纹理纹理: //解码时候使用opengl进行绘制,需要构建队列和两个线程,分别用于解码数据并且填充纹理和渲染. 主线程常见两个共享上下文: main() { ⋯⋯⋯⋯ gH ...

  8. C#处理医学图像(一):基于Hessian矩阵的血管肺纹理骨骼增强对比

    在医院实际环境中,经常遇到有问题的患者,对于一些特殊的场景,比如骨折,肺结节,心脑血管问题 需要图像对比增强来更为清晰的显示病灶助于医生确诊,先看效果: 肺纹理增强: 肺结节增强: 血管对比增强: 骨 ...

  9. OpenGL ES 压缩纹理

    什么是压缩纹理 在实际应用特别是游戏中纹理占用了相当大的包体积,而且GPU无法直接解码目前流行的图片格式,图片必须转换为RGB等类型的格式才能上传到GPU内存,这显然增加了GPU内存的占用.为了处理这 ...

  10. 【转】OpenGL多线程创建纹理,附加我的测试结果

    原文地址 http://www.cnblogs.com/mazhenyu/archive/2010/04/29/1724190.html 关于这个问题以前只知道多个线程不能同时使用一个RC,结果为了能 ...

随机推荐

  1. 对一个序列双重argsort的含义

    学习笔记:由numpy.argsort()引发的思考 一.numpy.argsort() 函数定义 函数的定义 首先函数的定义比较简洁: argsort()函数是将x中的元素从小到大排列,提取其对应的 ...

  2. python之路52 ORM查询、ORM事务、查询优化、常用字段及参数、ajax方法

    Q查询进阶操作 from django.db.models import Q q_obj = Q() # 1.产生q对象 q_obj.connector = 'or' # 默认多个条件的连接是and可 ...

  3. Java程序员用代码,计算最大公约数和最小公倍数

    作者:小傅哥 博客:https://bugstack.cn 源码:https://github.com/fuzhengwei/java-algorithms 沉淀.分享.成长,让自己和他人都能有所收获 ...

  4. Spring框架初学习

    Spring框架初学习   摘要:今天我终于开始学习大名鼎鼎的Spring框架了,在上大学的时候,经常看见一些课设大佬Spring,Spring的,什么Spring boot,Spring MVC的, ...

  5. C++Vector源码解析(侯捷STL)

    vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新的元素.vector的实现技术,关键在于对大小的控制以及重新配置时的数据移动效率. 一.vector的数据结构 vector采 ...

  6. 解决微信小程序 自定义tabBar 切换时候闪烁问题

    这个闪烁真的很迷 我搜了一些资料,进行了以下步骤的操作 第一种解决办法 ,把tabbar自定义组件的this.setData中的代码注释掉 显示tabbar中的页面中,添加下面的:这个好像没什么用啊 ...

  7. 【Regex】判断密码强度的正则表达式

    原文地址 https://www.cnblogs.com/younShieh/p/17082522.html 如果本文对你有所帮助,不妨点个关注和推荐呀,这是对笔者最大的支持~     需求   最近 ...

  8. 时间日期相关总结-System类的常用方法

    时间日期相关总结 Date类 A.构造方法 Date();根据当前系统时间创建日期对象 Date(long time);根据传入的毫秒值时间创建日期对象 B.成员方法 long getTime(); ...

  9. 【Rust学习】内存安全探秘:变量的所有权、引用与借用

    作者:京东零售 周凯 一.前言 Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月,是一种高效.可靠的通用高级语言.其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有 ...

  10. Idea导入本地Mavenue项目

    转https://www.cnblogs.com/kaola8023/p/14069519.html 一.导入Maven项目的问题 1. 安装后运行idea会直接打开如下页面 , 我们点击 Open  ...