一、前言
在OpenGL学习 的纹理这一章中讲述了纹理贴图的使用方式,主要步骤是先创建一个纹理的对象,和创建顶点VAO类似,然后就开始绑定这个纹理,最后在循环中使用,有时候可能还要用到激活纹理单元的函数。然而,对于何时应该激活如何和shader里的纹理编号进行绑定没有详细的说明,导致在使用的时候产生了不少困惑。比如何时应该绑定,绑定后的索引如何匹配。
在这一章的第一个效果案例:显示一个带贴图纹理的箱子时只用到了绑定而没有用到激活,部分主要代码如下:即使这样,在shader中也能让贴图获取到这里写进去的数据。
while (!glfwWindowShouldClose(window))
{
-----
glBindVertexArray(VAO);
-----
}
其shader的片段着色器代码如下,这个片段的“texture1”数据在上述循环中并未明确指定传过来的
// 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,然后稍微修改其片段着色器代码如下:如果这两个片段都获取到同样的数据则显示两倍的贴图效果,如果不相等则屏幕显示一个绿色的片。
// 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.2、如果C++代码只有一个贴图,但我就想传给片段着色器中指定的纹理
1)只激活第一个并不管用:这个时候我尝试使用激活,我只激活第一个试试,结果和图2.1.1是一样的,两个纹理还是被传输一样的纹理数据。
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
2)两个都激活,但只赋值给第二个贴图,结果两个都没有得到数据:再尝试不同的激活方式,保持只传输一个纹理数据的逻辑,将第一个激活然后不赋值,激活第二个并且赋值贴图纹理,理论上这样应该是可以的啊,结果变成了黑的,如图2.2.1所示
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture);
是不是两个贴图都没有获取到数据呢,我还不确定,再修改片段着色器判断一下:如果确实没有获取到数据则让屏幕显示“黄色”的箱子,结果如图2.2.2所示,确实显示了黄色的箱子,这说明我们
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);
}
贴图数据始终还是以默认的方式进行传输,第二个激活并传输并没有奏效,始终还是第一个激活才奏效。
3)再增加一个贴图的索引,结果可以精准的将数据传输到第二个贴图:代码如下,在激活之前给
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“这个变量,而且此处
void Game::Init()
{
......
ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
.......
}
无论你修改”image"为任何变量,比如“imageeeeeeeeeeeeeeee",它依然能显示正确的结果,因为这一篇文章中就是一个默认的贴图,而且激活和绑定都是执行默认的0,这里的索引就不会发生任何作用。