OpenGL排坑指南—贴图纹理绑定和使用

一、前言

OpenGL学习 的纹理这一章中讲述了纹理贴图的使用方式,主要步骤是先创建一个纹理的对象,和创建顶点VAO类似,然后就开始绑定这个纹理,最后在循环中使用,有时候可能还要用到激活纹理单元的函数。然而,对于何时应该激活如何和shader里的纹理编号进行绑定没有详细的说明,导致在使用的时候产生了不少困惑。比如何时应该绑定,绑定后的索引如何匹配。

在这一章的第一个效果案例:显示一个带贴图纹理的箱子时只用到了绑定而没有用到激活,部分主要代码如下:即使这样,在shader中也能让贴图获取到这里写进去的数据。

cpp 复制代码
   while (!glfwWindowShouldClose(window))
    {
        -----
        glBindVertexArray(VAO);
        -----
    }

其shader的片段着色器代码如下,这个片段的"texture1"数据在上述循环中并未明确指定传过来的

cpp 复制代码
// texture sampler
uniform sampler2D texture1;

void main()
{
	FragColor = texture(texture1, TexCoord);
}

这是我第一个猜想:如果C++代码中只创建了一个纹理数据,不用激活和指定传输到片段着色器中,使用" glBindTexture(GL_TEXTURE_2D, texture);"函数可以默认将片段着色器中的贴图都填充同样的数据。带着这个猜想我开始验证我的想法。

二、实现

2.1、如果只有一个贴图数据,默认传输到片段着色器的每一个"uniform sampler2D"

其他都不变,我只修改片段着色器代码,首先我创建了两个uniform sampler2D变量,分别为texture1和texture2,然后稍微修改其片段着色器代码如下:如果这两个片段都获取到同样的数据则显示两倍的贴图效果,如果不相等则屏幕显示一个绿色的片。

cpp 复制代码
// texture sampler
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{

	vec4 tempColor1=texture(texture1, TexCoord);
	vec4 tempColor2=texture(texture2,TexCoord);
	if(tempColor1==tempColor2)
	{
		FragColor =tempColor1+tempColor2;
	}
	else
		FragColor=vec4(0,1,0,1);
}

试验的结果得到如下,明显比之前要亮一点,很显然两个贴图都获取到循环里传输过来的贴图数据,而且是一样的。
图2.1.1

2.2、如果C++代码只有一个贴图,但我就想传给片段着色器中指定的纹理

**1)只激活第一个并不管用:**这个时候我尝试使用激活,我只激活第一个试试,结果和图2.1.1是一样的,两个纹理还是被传输一样的纹理数据。

cpp 复制代码
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture);

**2)两个都激活,但只赋值给第二个贴图,结果两个都没有得到数据:**再尝试不同的激活方式,保持只传输一个纹理数据的逻辑,将第一个激活然后不赋值,激活第二个并且赋值贴图纹理,理论上这样应该是可以的啊,结果变成了黑的,如图2.2.1所示

cpp 复制代码
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture);

图2.2.1

是不是两个贴图都没有获取到数据呢,我还不确定,再修改片段着色器判断一下:如果确实没有获取到数据则让屏幕显示"黄色"的箱子,结果如图2.2.2所示,确实显示了黄色的箱子,这说明我们

cpp 复制代码
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{

	vec4 tempColor1=texture(texture1, TexCoord);
	vec4 tempColor2=texture(texture2,TexCoord);
	if(tempColor1==tempColor2)
	{
		if(tempColor1==vec4(0,0,0,1))
		{
			FragColor=vec4(1,1,0,1);
		}
		else
			FragColor =tempColor1+tempColor2;
	}
	else
		FragColor=vec4(0,1,0,1);
}

图2.2.2

贴图数据始终还是以默认的方式进行传输,第二个激活并传输并没有奏效,始终还是第一个激活才奏效。

3)再增加一个贴图的索引,结果可以精准的将数据传输到第二个贴图:代码如下,在激活之前给

cpp 复制代码
		ourShader.setInt("texture2", 1);
		// bind Texture
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, texture);

当前的shader指定一个索引1,并且这个索引数值"1"一定要和后续绑定的"GL_TEXTURE1"对应,最后得到的结果是绿色的一个箱子,说明在片段着色器中贴图的数据不一样了。

三、总结

3.1、如果只有一个贴图数据,在C++中只用默认的绑定操作,默认传输到片段着色器的每一个"uniform sampler2D"变量中。

3.2、如果要指定传输给片段着色器中一定要使用激活贴图 glActiveTexture(GL_TEXTURE1);和 设置索引号ourShader.setInt("texture2", 1);同时使用,并且激活的序列号和设置索引值要相等,只使用索引也不行。

3.3、激活和设置索引以及绑定都要在循环里每帧都使用,尝试将索引设置不放在循环里也不行。

3.4、激活和绑定都是默认的情况下,设置索引没有任何作用,可以不用。 比如在文章后续做一个2D游戏中的一篇文章里渲染精灵 其最后的游戏类代码中初始化时设置了纹理"image"的数据索引为0,但是其着色器代码中并没有"image"这个变量,而且此处

cpp 复制代码
void Game::Init()
{
   ......
    ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
    .......
}

无论你修改"image"为任何变量,比如"imageeeeeeeeeeeeeeee",它依然能显示正确的结果,因为这一篇文章中就是一个默认的贴图,而且激活和绑定都是执行默认的0,这里的索引就不会发生任何作用。

相关推荐
刘好念15 小时前
[OpenGL]使用 Compute Shader 实现矩阵点乘
c++·计算机图形学·opengl·glsl
神码编程1 天前
【Unity功能集】TextureShop纹理工坊(五)选区
unity·游戏引擎·shader·ps选区
阳光开朗_大男孩儿2 天前
为什么glfwWindowHint设置的属性,glfwCreateWindow可以直接使用?
前端·数据库·opengl
刘好念4 天前
[OpenGL]使用TransformFeedback实现粒子效果
c++·计算机图形学·opengl
Tiger Z4 天前
R 语言科研绘图第 6 期 --- 散点图-基础
r语言·贴图
橘子遇见BUG5 天前
Unity Shader学习日记 part 2 线性代数--矩阵
学习·线性代数·unity·矩阵·shader
程序员_三木5 天前
Three.js资源-贴图材质网站推荐
javascript·webgl·three.js·材质·贴图
吃豆腐长肉6 天前
着色器 (三)
opengl·着色器
吃豆腐长肉6 天前
opengl 着色器 (四)最终章收尾
opengl·着色器
德林恩宝7 天前
WebGPU、WebGL 和 OpenGL/Vulkan对比分析
web·webgl·opengl·webgpu