OpenGL纹理

纹理采样就是把贴图按照UV坐标映射到三角形上
如何进行纹理采样呢?


1.首先能加载图片文件
stb_image.h是Sean Barrett的一个非常流行的单头文件图像加载库:传送门

如何通过stb_image加载图片?

#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

//通过定义STB_IMAGE_IMPLEMENTATION,预处理器会修改头文件,让其只包含相关的函数定义源码
//相当于让其变成cpp了

void test() {
	//使用stbi_load加载图片数据
	//	返回图像的宽度、高度、通道个数
	int width, height, nrChannels;
	unsigned char* data = stbi_load("test.png", &width, &height, &nrChannels, 0);
}

2.定义纹理
详解都在代码里

主要流程:
a)生成纹理
b)绑定当前纹理
c)设置环绕、过滤方式
d)通过图片数据载入纹理
e)生成多级渐远纹理 Mipmap

//定义纹理1
unsigned int texture1;
//通过glGenTextures生成纹理
//	参数1:生成纹理的数量
//	参数2:返回的纹理ID(多个就是数组)
glGenTextures(1, &texture1);

//通过Id绑定纹理,之后都操作这个纹理
glBindTexture(GL_TEXTURE_2D, texture1);

//设置环绕、过滤方式
//	参数1:纹理目标 GL_TEXTURE_2D		就是上面绑定的
//	参数2:类型
//	参数3:值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

//GL_TEXTURE_MIN_FILTER 和 GL_TEXTURE_MAG_FILTER 代表纹理被放大或者缩小时的采样方式
//	一般被缩小时,都会使用 多级渐远纹理Mipmap,否则会出现细节丢失和内存浪费
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//使用stbi_load加载图片数据
//	返回图像的宽度、高度、通道个数
int width, height, nrChannels;
unsigned char* data = stbi_load("icon2.png", &width, &height, &nrChannels, 0);

if (data)
{
	//通过图片数据载入纹理
	//	参数1:纹理目标 GL_TEXTURE_2D		就是上面绑定的
	//	参数2:Mipmap的级别
	//	参数3:纹理格式		这里就是RGB
	//	参数4:纹理的宽度
	//	参数5:纹理的高度
	//	参数6:没有意义,就写0
	//	参数7:原图格式		RGB格式
	//	参数8:数据格式		使用二进制加载的
	//	参数9:数据实际内容
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

	//自动生成所有需要的多级渐远纹理 Mipmap
	glGenerateMipmap(GL_TEXTURE_2D);

	//生成了纹理和相应的多级渐远纹理后,就可以释放源数据了
	stbi_image_free(data);

	//解除绑定纹理
	glBindTexture(GL_TEXTURE_2D, NULL);
}
else
{
	std::cout << "Failed to load texture" << std::endl;
}

相关定义
a.环绕(Wrap):就是采样到边界的处理
需要对XY分别设置,也就是:GL_TEXTURE_WRAP_S(X)和 GL_TEXTURE_WRAP_T(Y)
环绕方式有4种:
1)GL_REPEAT 对纹理的默认行为。重复纹理图像
2)GL_MIRRORED_REPEAT 和GL_REPEAT一样,但每次重复图片是镜像放置的
3)GL_CLAMP_TO_EDGE 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果
4)GL_CLAMP_TO_BORDER 超出的坐标为用户指定的边缘颜色

b.过滤(Filter):就是采样的取色方式
纹理坐标不依赖于分辨率(Resolution)是任意浮点值,所以OpenGL需要知道怎样将纹理像素映射到纹理坐标
过滤有2种比较重要的:
1)GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。
就取最靠近的那个像素颜色
2)GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。
附近的4像素颜色混合值

c.多级渐远纹理Mipmap:它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一
OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个

3.渲染纹理
详解都在代码里

主要流程:
a)激活纹理单元
b)绑定当前纹理给着色器的采样器
c)指定纹理单元
d)片段着色器里面进采样

//纹理怎么传到片段着色器?
// 
//纹理单元(Texture Unit):一个纹理的位置值通常称为一个纹理单元
//	使用glUniform1i,可以给纹理采样器分配一个位置值,可以在一个片段着色器中设置多个纹理
// 
//通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理
// 
// 在绑定纹理之前先激活纹理单元
glActiveTexture(GL_TEXTURE0);

//激活纹理单元之后,接下来的glBindTexture函数调用会绑定这个纹理到当前激活的纹理单元
//	纹理单元GL_TEXTURE0默认总是被激活,不写也行
//	OpenGL至少保证有16个纹理单元可使用 GL_TEXTURE0 - GL_TEXTRUE15
glBindTexture(GL_TEXTURE_2D, texture1);

//第二个纹理
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);

testShader.use();

//设置贴图,通过glUniform1i指定纹理单元
testShader.setI("texture1", 0);
testShader.setI("texture2", 1);

float timeValue = (float)glfwGetTime();
testShader.setF("time", timeValue);

//绑定VBO
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

4.shader源码

顶点着色器:

#version 330 core

layout (location=0) in vec3 aPos;		//0是顶点位置
layout (location=1) in vec3 aCol;		//1是顶点颜色
layout (location=2) in vec2 aTexcoord;	//2是UV坐标

uniform float time; //自定义参数,代码传递

out vec3 outCol; //返回给片段着色器的颜色
out vec2 Texcoord; //返回给片段着色器的颜色

void main()
{
	//根据传过来的center进行移动
	vec3 center = vec3(sin(time), cos(time), 0.0f);
	vec3 pos = aPos + (center)*0.5f;

	//翻转三角形
	pos.y *= -1;

	//返回裁剪坐标
	gl_Position = vec4(pos, 1.0f);

	//算距离当做mask影响颜色
	float dist = distance(pos, vec3(0.0f, 0.0f, 0.0f));
	outCol = aCol * dist;

	//返回UV
	Texcoord = aTexcoord;
}

片段着色器:

#version 330 core

in vec3 outCol; //接受顶点着色器返回的颜色值
in vec2 Texcoord; //接受顶点着色器返回的UV

out vec4 FragColor; //输出最后的颜色

uniform sampler2D texture1; //定义采样器(Sampler)
uniform sampler2D texture2; //定义采样器(Sampler)

void main()
{
    //使用UV采样纹理
    vec4 tex1Col = texture(texture1, Texcoord);
    vec4 tex2Col = texture(texture2, Texcoord);

    FragColor = (tex1Col + tex2Col) * vec4(outCol.rgb, 1.0f);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值