OpenGL超级宝典 纹理(一)

本文详细介绍了OpenGL中纹理的创建、初始化、更新数据、从着色器读取、采样器类型控制、纹理过滤设置等关键步骤,并提供了完整的代码示例,展示了如何使用stb_image库加载纹理。同时,讨论了纹理过滤的重要性,包括放大和缩小过滤,以及纹理坐标超出范围时的处理方式。

纹理

纹理是一种结构化的存储形式,可供着色器读写。
纹理通常用于存储图形数据,并有多种形式和布局。

创建并且初始化纹理

纹理的创建和vbo的创建差不多
先创建纹理对象,然后分配内存,绑定到OpenGL,将数据添加到缓存。

		GLuint texture;
		//1、创建纹理对象
		glCreateTextures(GL_TEXTURE_2D, 1, &texture);

		//2、分配纹理缓存空间
		glTextureStorage2D(texture,		//纹理对象
			1,							//mipmap层数,这个只是分配的内存!!!
			GL_RGB8,					//数据格式
			width,						//纹素的宽
			height);					//纹素的长

		//3、绑定纹理对象到OpenGL上下文,告诉OpenGL纹理的类型
		glBindTexture(GL_TEXTURE_2D, texture);

更新纹理数据

		//4、更新纹理数据
		glTextureSubImage2D(texture,	//纹理对象
			0,							//mipmap层数,这是纹理的!!!别和上面的弄混乱
			0, 0,						//开始坐标
			width, height,				//纹理宽和长
			GL_RGB,						//纹理数据格式
			GL_UNSIGNED_BYTE,			//数据类型
			image_data);				//数据

从着色器中读取数据

着色器中的纹理以采样器变量形式存在,通过sampler类型的统一变量与外界进行连接。
glsl中二维纹理的采样器类型为sampler2D

#version 450 core

out vec4 color;

uniform sampler2D s;

in vec2 txcoord;

void main()
{
	color = texture(s, txcoord);
}

采样器类型

在这里插入图片描述

控制纹理数据的读取方式

通常,纹理坐标都是标准化坐标,即坐标的范围为0.0~1.0。
通过OpenGL可以控制当提供的纹理坐标超出此范围时的情况,这称为采样器的包装模式(wrapping mode)。
同时,我们可以决定如何计算实际采样器之间的值,这称为采样器的过滤模式(filering mode)。
控制采样器包装和过滤模式的参数存储在采样器对象(sampler object)中。

创建采样器对象和绑定到纹理单元

		//5.1、创建采样器对象
		glCreateSamplers(1, &s);
		//绑定到纹理单元,使用多个纹理的时候用,一个纹理不需要用!
		//glBindSampler(0, s);

纹理过滤

纹理图中的纹素和屏幕上的像素之间几乎从来不存在一一对应的关系
因此,纹理图像在进行纹理贴图的时候总是被拉伸或收缩。
glsl中有一个函数texture(),可以通过从拉伸或收缩的纹理图中计算颜色片段来对贴图进行调节。这个函数有几种重载:
vec4 texture(sampler1D s, float P);
vec4 texture(sampler2D s, vec2 P);
ivec4 texture(isampler2D s, vec2 P);
uvec4 texture(usampler3D s, vec3 P);

从拉伸或收缩的纹理图中计算颜色片段的过程称为纹理过滤(texture filering)。
拉伸纹理也叫作放大(magnification),收缩纹理也叫作缩小(minification)

利用采样器的参数,OpenGL支持分别在放大和缩小情况下分别设置构造纹素值的方法。这些条件称为过滤器(filter)
这两个过滤器的名称分别为
GL_TEXTURE_MAG_FILTER
和GL_TEXTURE_MIN_FILTER,分别对应放大缩小过滤器
我们可以从GL_NEAREST和GL_LINEAR这两个基本纹理过滤器中进行选择,分别对应最近邻线性过滤

设置过滤器
		//5.2、设置纹理过滤方式,多个纹理的时候用这个
		/*glSamplerParameterf(s, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glSamplerParameterf(s, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glSamplerParameterf(s, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glSamplerParameterf(s, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
		
		//采用纹理嵌入的采样器对象,只有一个纹理的时候使用这个
		glTextureParameterf(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTextureParameterf(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTextureParameterf(texture, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTextureParameterf(texture, GL_TEXTURE_WRAP_T, GL_REPEAT);

加载纹理

这里我使用的加载纹理方式是使用stb_image库进行加载
可以通过此链接下载
https://github.com/nothings/stb/blob/master/stb_image.h

完整代码展示

shader

vertex shader

#version 450 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 tex_coord;

out vec2 txcoord;

uniform mat4 mvp;

void main()
{
	txcoord = tex_coord;
	gl_Position = vec4(position, 1.0);
}

fragment shader

#version 450 core

out vec4 color;

uniform sampler2D s;

in vec2 txcoord;

void main()
{
	color = texture(s, txcoord);
}

Shader

和上篇文章的一样

源代码

#define STB_IMAGE_IMPLEMENTATION
#include "sb7.h"
#include "Shader.h"
#include "stb_image.h"
#include "sb7ktx.h"
#include <iostream>

using std::cout;
using std::endl;

class my_applicaiton : public sb7::application
{
public:
	void startup()
	{
		//加载纹理
		unsigned char* image_data = stbi_load("wall.jpg", &width, &height, &nrChannel, 0);
		if (!image_data)
		{
			cout << "load image fail" << endl;
		}
		cout << "channel: " << nrChannel << endl;
		cout << "width: " << width << endl;
		cout << "height: " << height << endl;

		float data[] =
		{ 
			//vertex_coord			uv_coord
			-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
			 0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
			-0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

			-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
			 0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			 0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
			-0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
			-0.5f,  0.5f, -0.5f,  0.0f, 1.0f
		};

		//create shader instance
		shader = new Shader("vs.vert", "fs.frag");

		//create shader object; 
		glCreateVertexArrays(1, &vao);
		//bind vao to content
		glBindVertexArray(vao);
		
		//create vbo object
		glCreateBuffers(1, &vbo);
		//bind vbo to content 
		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		//分配内存
		glNamedBufferStorage(vbo,	//缓存目标
			sizeof(data),			//缓存大小
			nullptr,				//无数据
			GL_DYNAMIC_STORAGE_BIT);//模式

		glNamedBufferSubData(vbo,	//缓存
			0,						//偏移量
			sizeof(data),			//数据大小
			data);					//数据

		//绑定缓存到vao
		glVertexArrayVertexBuffer(vao,	//绑定的vao
			0,							//顶点属性
			vbo,						//绑定的vbo
			0,							//绑定点
			sizeof(float) * 5);			//每个顶点之间的距离

		//绑定到vao
		glVertexArrayAttribBinding(vao, //vao
			0,							//顶点属性
			0);							//绑定点
		glVertexArrayAttribFormat(vao,	//vao
			0,							//顶点属性
			3,							//顶点分量
			GL_FLOAT,					//数据类型
			GL_FALSE,					//是否标准化
			0);							//顶点属性的顶点与起始顶点的距离你

		glVertexArrayAttribBinding(vao, 1, 0);
		glVertexArrayAttribFormat(vao, 1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 3);
		//启用顶点属性
		glEnableVertexArrayAttrib(vao, 0);
		glEnableVertexArrayAttrib(vao, 1);

		//1、创建纹理对象
		glCreateTextures(GL_TEXTURE_2D, 1, &texture);

		//2、分配纹理缓存空间
		glTextureStorage2D(texture,		//纹理对象
			1,							//mipmap层数,这个只是分配的内存!!!
			GL_RGB8,					//数据格式
			width,						//纹素的宽
			height);					//纹素的长

		//3、绑定纹理对象到OpenGL上下文,告诉OpenGL纹理的类型
		glBindTexture(GL_TEXTURE_2D, texture);

		//4、更新纹理数据
		glTextureSubImage2D(texture,	//纹理对象
			0,							//mipmap层数,这是纹理的!!!别和上面的弄混乱
			0, 0,						//开始坐标
			width, height,				//纹理宽和长
			GL_RGB,						//纹理数据格式
			GL_UNSIGNED_BYTE,			//数据类型
			image_data);				//数据

		//5、控制纹理数据的读取方式
		//5.1、创建采样器对象
		glCreateSamplers(1, &s);
		//绑定到纹理单元,使用多个纹理的时候用
		glBindSampler(0, s);

		//5.2、设置纹理过滤方式
		/*glSamplerParameterf(s, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glSamplerParameterf(s, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glSamplerParameterf(s, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glSamplerParameterf(s, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
		
		//采用纹理嵌入的采样器对象
		glTextureParameterf(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTextureParameterf(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTextureParameterf(texture, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTextureParameterf(texture, GL_TEXTURE_WRAP_T, GL_REPEAT);
	}

	void render(double currentTime)
	{
		GLfloat colors[] = { 0.0, 0.5, 0.0, 1.0 };
		glClearBufferfv(GL_COLOR, 0, colors);

		shader->use();
		
		glDrawArrays(GL_TRIANGLES, 0, 36);
		//delete[] data;
	}

	void shutdown()
	{

	}

private:
	//vao object and vbo object
	GLuint vao;
	GLuint vbo;
	//shader object
	Shader* shader;
	//texture object
	GLuint texture;
	//sampler
	GLuint s;
	int width, height, nrChannel;
private:

};

DECLARE_MAIN(my_applicaiton);

效果

在这里插入图片描述

总结

  1. 创建纹理对象
  2. 分配纹理缓存空间
  3. 绑定纹理对象到OpenGL上下文,告诉OpenGL纹理的类型
  4. 更新纹理数据
  5. 控制纹理数据的读取方式
    5.1 创建采样器对象
    5.2 设置纹理过滤方式
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ht巷子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值