关于纹理

一般游戏里的物体不一定都是纯色的物体,物体上面会有一些图片贴在上面,比如墙壁,箱子,地板,可以看到砖头、木板和大理石组成的图片,要把图片贴到计算机里的几何图形的话,就要把图片的颜色采样贴到几何图形上,采样是计算机经常干的工作,计算机要处理自然中的数据就需要对数据进行采样,比如说对声音采样就是采集声音的频率和频幅,分别代表声音的音色和声量,当然,采集到的是一个模拟量,然而计算机无法处理模拟量,所以需要将模拟量量化数字量,也就是二进制数进行存储,同理,颜色也是一样,现代计算机中颜色一般以RGB的形式,分别代表Red,Green,Blud三种颜色在这个颜色中的占比,一般使用3个字节存储,也就是说颜色的数量可以达到2^24种,已经足够用了,想要把采集到的颜色贴到物体上的话,我们需要指定顶点的纹理坐标,告诉着色器要从纹理的哪个点开始采样, 纹理坐标的范围是0到1

项目的代码

learnOpenGL里的图解

纹理环绕

纹理坐标的范围是0到1,假如超出这个范围的话,在OpenGL里会有几种方式来贴图,这些方式叫做纹理环绕方式

  • GL_REPEAT 对纹理的默认行为。重复纹理图像。
  • GL_MIRRORED_REPEAT 和GL_REPEAT一样,但每次重复图片是镜像放置的。
  • GL_CLAMP_TO_EDGE 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
  • GL_CLAMP_TO_BORDER 超出的坐标为用户指定的边缘颜色。

在我的工程里有用来测试的函数SurroundTest,把环绕方式和过滤模式封装成枚举方便调试

//环绕模式
enum class SurroundMode
{
Repeat = GL_REPEAT, //重复纹理图像
MirroredRepeat = GL_MIRRORED_REPEAT, //镜像重复纹理图像
ClampToEdge = GL_CLAMP_TO_EDGE, //将边缘拉伸
ClampToBoreder = GL_CLAMP_TO_BORDER //超出的部分为指定边缘颜色
};
//过滤方式
enum class FilteringMode
{
Nearest = GL_NEAREST,
Linear = GL_LINEAR,
};

将纹理坐标的分量值设置到[0,1]之外,进行测试

	float vertices[] = {
// ---- 位置 ---- - 纹理坐标 -
0.5f, 0.5f, 0.0f, 2.0f, 2.0f, // 右上
0.5f, -0.5f, 0.0f, 2.0f, -1.0f, // 右下
-0.5f, -0.5f, 0.0f, -1.0f, -1.0f, // 左下
-0.5f, 0.5f, 0.0f, -1.0f, 2.0f // 左上
};

测试结果

过滤模式

可以看到环绕方式测试的图片是有一丢丢模糊的(其实是用错过滤模式了),这是因为用了线性过滤模式(Linear),使用线性过滤模式采样的时候,每个像素的颜色会和周围的颜色进行混合算出一个插值,这个插值近似于这些像素

而采用 邻近过滤(Nearest) 的话,那么像素的颜色就是采样器采到的颜色,如果分辨率小的图片贴到大的物体上的话,就会出现颗粒状的图案,但使用线性过滤模式的话这些颗粒就会变得比较平滑

测试一下



是不是可以很明显地看出Linear方式采样的珂朵莉的眼神比较温柔啊,这就是经过平滑处理过的图片。在这里顺便说下unity的三种过滤模式

  • Point 点像素过滤,纹理像素会在附近变为块状。
  • Bilinear 双线性过滤,平均纹理样本
  • Trilinear 三线性过滤,平均纹理样本,同时也在多级渐远纹理级别之间混合

测试代码

首先是读取图片用的代码,对 learnOpenGL 里的代码进行了一次封装,针对不同位深度的图片进行处理,位深度就是图片记录每个像素点的位数,24位表示3个float,也就是RGB值,32位则是4个float,即RGBA,读取图片用了官方介绍的stb_image

//加载纹理
template <typename S1 = std::string>
void LoadTexture(unsigned int &texture, S1&& pic)
{
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;
stbi_set_flip_vertically_on_load(true);
unsigned char *data = stbi_load((TEXTURE_PATH + std::forward<std::string>(pic)).c_str(), &width, &height, &nrChannels, 0);
std::cout << "nrChannels = " << nrChannels << endl;
if (data)
{
//位深度为24,3个通道(jpg
if (nrChannels == 3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
//位深度为32,4个通道(png
else if (nrChannels == 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
data = nullptr;
}

着色器代码

顶点着色器

vertex_3.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; void main()
{
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
}

位置0是顶点的坐标,位置1是输入的纹理坐标,要通过顶点着色器将纹理坐标输出到片段着色器

片段着色器

fragment_3.fs

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

TexCoord是从顶点着色器接收的纹理坐标,使用texture函数来采样,第一个参数是采样器(Sampler),第二个参数是纹理坐标,着色器输出过滤后的颜色值FragColor

接下来是就是正常的测试代码了

void NormalTest()
{
float vertices[] = {
// ---- 位置 ---- - 纹理坐标 -
0.9f, 0.9f, 0.0f, 1.0f, 1.0f, // 右上
0.9f, -0.9f, 0.0f, 1.0f, 0.0f, // 右下
-0.9f, -0.9f, 0.0f, 0.0f, 0.0f, // 左下
-0.9f, 0.9f, 0.0f, 0.0f, 1.0f // 左上
};
//索引
unsigned int indices[] = {
0,1,3,
1,2,3
};
//编译着色器
Shader ourShader("vertex_3.vs", "fragment_3.fs");
ourShader.use();//glUseProgram(shaderProgram);
unsigned int VAO, VBO, EBO; //顶点数组
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); //绑定顶点数组缓存
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //索引缓存
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //生成纹理
unsigned int texture1;
LoadTexture(texture1, std::move("picture4.png")); //加载纹理
glBindTexture(GL_TEXTURE_2D, texture1);//绑定纹理 // 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 纹理属性
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); //最后一个参数是数据的起点
glEnableVertexAttribArray(1); while (!glfwWindowShouldClose(glWindow))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//draw
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glfwPollEvents();
glfwSwapBuffers(glWindow);
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glfwTerminate();
}

测试结果



珂朵莉好可爱~

OpenGL学习笔记(3) 纹理的更多相关文章

  1. OpenGL学习笔记3——缓冲区对象

    在GL中特别提出了缓冲区对象这一概念,是针对提高绘图效率的一个手段.由于GL的架构是基于客户——服务器模型建立的,因此默认所有的绘图数据均是存储在本地客户端,通过GL内核渲染处理以后再将数据发往GPU ...

  2. OpenGL学习笔记:拾取与选择

    转自:OpenGL学习笔记:拾取与选择 在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当 ...

  3. OpenGL学习笔记(四)纹理

    目录 要完成的纹理效果 纹理环绕方式 纹理过滤 多级渐远纹理 加载与创建纹理 stb_image库的使用方法 生成纹理对象 应用纹理 纹理单元 参考资料:OpenGL中文翻译 要完成的纹理效果 纹理是 ...

  4. webgl学习笔记五-纹理

    写在前面 建议先阅读下前面我的三篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 webgl学习笔记三-平移旋转缩放 术语 : 纹理 :图像 图形装配区域 :顶点着色器顶点坐标 ...

  5. OpenGL学习笔记2017/8/29

    OpenGL学习日志: 感谢doing5552 的OpenGL入门学习:http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html 相 ...

  6. opengl学习笔记(四):openCV读入图片,openGL实现纹理贴图

    在opengl中实现三维物体的纹理贴图的第一步就是要读入图片,然后指定该图片为纹理图片. 首先利用opencv的cvLoadImage函数把图像读入到内存中 img = cvLoadImage(); ...

  7. opengl学习笔记(三):经过纹理贴图的棋盘

    opengl纹理贴图的步骤: 1:创建纹理对象,并为它指定一个纹理 2:确定纹理如何应用到每个像素上 3:启用纹理贴图功能 4:绘制场景,提供纹理坐标和几何图形坐标 注意:纹理坐标必须在RGBA模式下 ...

  8. OpenGL学习笔记(1) 画一个三角形

    最近找实习有一丢丢蛋疼,沉迷鬼泣5,四周目通关,又不想写代码,写篇笔记复习一下,要好好学图形学啊 用OpenGL画一个三角形 项目的简介 记录一下跟着learnOpenGL学习的过程 笔记里的代码放在 ...

  9. opengl学习笔记(一)

    ubuntu下opengl的安装及配置 OpenGL 是一套由SGI公司发展出来的绘图函数库,它是一组 C 语言的函数,用于 2D 与 3D 图形应用程序的开发上.OpenGL 让程序开发人员不需要考 ...

随机推荐

  1. python3编程的一些实用技巧1

    1.choice函数:返回一个列表,元组,字符串的随机项   :   调用时应导入random模块,如from random import choice 2.print 两个字符串, 逗号,+号进行连 ...

  2. 2015 ICL, Finals, Div. 2【ABFGJK】

    [题外话:我......不补了......] 2015 ICL, Finals, Div. 2:http://codeforces.com/gym/100637 G. #TheDress[水] (st ...

  3. Selenium2+python-unittest之装饰器(@classmethod)

    原文地址:http://www.cnblogs.com/yoyoketang/p/6685416.html 前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量 ...

  4. 清除浮动元素的margin-top失效原因(更改之前的错误)

    //样式代码body,div{ margin:; padding:; } .box1{ background:#900; width:200px; height:200px; margin:20px ...

  5. mysql 跑存储过程没有权限的问题

    1.赋予权限 GRANT ALL PRIVILEGES ON *.* TO root@"%" IDENTIFIED BY "root"; 2.刷新权限 FLUS ...

  6. 关于 class helper for ... 语法

    转载于万一的博客https://www.cnblogs.com/del/archive/2009/10/13/1582789.html class helper 可能是从 Delphi 2007 增加 ...

  7. C中级 - 文件辅助操作

    引言 - 业务有点麻烦 C 功能很强大, 同样书写起来会谨慎(拖泥带水). 不妨通过一个小问题来描述裹脚的 C 需求: 用 C 创建一个文件! 难点在于 1. 文件路径切割成 目录 + 文件名 2. ...

  8. Linux下安装Beego:go install: cannot install cross-compiled binaries when GOBIN is set

    Linux下安装Beego出错 问题:go install: cannot install cross-compiled binaries when GOBIN is set 遇到这个问题一般是在环境 ...

  9. Django model字段类型(转)

    AutoField     一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.(参阅 _自 ...

  10. inux下进程的最大线程数、进程最大数、进程打开的文件数

    inux下进程的最大线程数.进程最大数.进程打开的文件数 2008-12-07 23:48 =========================    如下转载自这里. linux 系统中单个进程的最大 ...