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,这里的索引就不会发生任何作用。

相关推荐
凌云行者3 天前
OpenGL入门004——使用EBO绘制矩形
c++·cmake·opengl
闲暇部落3 天前
Android OpenGL ES详解——模板Stencil
android·kotlin·opengl·模板测试·stencil·模板缓冲·物体轮廓
阿赵3D4 天前
Unity引擎材质球残留贴图引用的处理
unity·材质·贴图
小彭努力中5 天前
114. 精灵模型标注场景(贴图)
前端·3d·webgl·贴图
小彭努力中5 天前
109. 工厂光源(环境贴图和环境光)
前端·深度学习·3d·webgl·贴图
凌云行者5 天前
OpenGL入门003——使用Factory设计模式简化渲染流程
c++·cmake·opengl
凌云行者6 天前
OpenGL入门002——顶点着色器和片段着色器
c++·cmake·opengl
Raina_H6 天前
Blender进阶:贴图与UV
经验分享·3d·blender·贴图·uv
Raina_H6 天前
Blender进阶:着色器节点
3d·blender·材质·贴图·着色器·uv
闲暇部落7 天前
Android OpenGL ES详解——裁剪Scissor
android·kotlin·opengl·窗口·裁剪·scissor·视口