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

相关推荐
CG_MAGIC2 天前
主流 3D 模型格式(FBX/OBJ/DAE/GLTF)材质支持与转换操作指南
3d·渲染·动画·材质·贴图·3d 模型格式·材质支持与转换操作指南
渲吧-云渲染7 天前
3ds MAX文件/贴图名称乱码?6大根源及解决方案
3d·贴图
benben0448 天前
《Unity Shader入门精要》学习笔记一
unity·shader
onthewaying12 天前
详解 Android GLSurfaceView 与 Renderer:开启你的 OpenGL ES 之旅
android·opengl
灿烂阳光g16 天前
OpenGL Camera
opengl
CG_MAGIC17 天前
3D 建模核心术语扫盲:拓扑、UV 展开、烘焙与 AO 贴图解析
3d·渲染·贴图·uv·拓扑·渲云渲染·ao 贴图
用户6195333341717 天前
为ChatGPT和API集成构建MCP服务器
openai·opengl
两水先木示17 天前
【Unity3D】Shader圆形弧度裁剪
unity·shader·圆形裁剪·弧度裁剪
哈市雪花22 天前
添加捕捉吸附标识(使用QT+OpenGL开发三维CAD)
qt·教程·opengl·cad·绘制·工业软件·捕捉吸附
爱看书的小沐1 个月前
【小沐学GIS】基于Rust绘制三维数字地球Earth(Rust、OpenGL、GIS)
3d·rust·gis·opengl·cargo·earth·3d earth