纹理单元的理解
Shader中要用到纹理:
uniform sampler2D texture1;
Main读取图片数据,创建了纹理:
unsigned int texture1;
怎么把c++里加载的纹理传给shader程序里呢?
这就要用到纹理单元。
glBindTexture(GL_TEXTURE_2D, texture1);
这条代码将纹理texture1传递给了正在使用的着色器程序里的uniform sampler2D texture1
中间隐含过程是,opengl有许多默认的纹理单元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2~GL_TEXTUREn),
其中,默认激活的是纹理单元0——GL_TEXTURE0,上面的代码就把纹理传递给了这个默认激活的纹理单元0;
另一方面,在shader里声明的采样器——unigorm sampler,他会从一个特定的纹理单元里取得纹理数据,而这个特定的纹理单元是GL_TEXTURE0~n中的哪一个呢?
答案是,在shader里声明的每一个采样器,其默认对应的纹理单元是GL_TEXTURE0;
所以,使用上面的一行代码,其中的过程是这样的:
1、没有选择激活纹理单元,所以使用默认的纹理单元0
2、把纹理texture1传递给了纹理单元0
3、没有设置shader里的采样器对应的纹理单元,所以采样器从默认的纹理单元0读取数据
这样会使shader里所有的采样器(如果声明多个的话)都从纹理单元0中获取纹理数据,很明显这样就会使声明的采样器都一样了。
但是我们声明多个采样器明显是想在一个着色器里使用多个不同的纹理,那么该怎么做?
着色器里有多个采样器,像是这样:
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D texture1; uniform sampler2D texture2; void main() { FragColor=mix(texture(texture1,TexCoord),texture(texture2,TexCoord),0.5); //FragColor=texture(texture1,TexCoord); }
回头看之前的步骤,只要完成原先省略做的事就好了
1、绑定纹理时,先选择激活相应的纹理单元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
2、设置shader里声明的采样器所对应的纹理单元
glUniform1i(glGetUniformLocation(myShader.ID, "texture1"), 0); myShader.setInt("texture2", 1); // 或者使用着色器类设置
两中方法,都是先找到shader中的采样器”texture1”/”texture2”的地址,然后设置其对应的纹理单元,0是指GL_TEXTURE0,1对应GL_TEXTURE1,2、3以此类推。
Texture类
模仿Shader类写了自己的Texture类(重复操作实在太多了
class lxlTexture { public: unsigned int Id; lxlTexture(){} lxlTexture(const GLchar* texPath,GLint format) { glGenTextures(1, &Id); glBindTexture(GL_TEXTURE_2D, Id); // 为当前绑定的纹理对象设置环绕、过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //读取图片文件 int width, height, nrChannels;//颜色通道个数 stbi_set_flip_vertically_on_load(false); unsigned char* data = stbi_load(texPath, &width, &height, &nrChannels, 0); //导入读取的图片文件数据,处理生成纹理 glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); ////-解释- //上面把纹理绑定到了GL_TEXTURE_2D,现在这个参数选择了GL_TEXTURE_2D为目标,处理绑定在上面的纹理 //参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。 //第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。 //第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。 //下个参数应该总是被设为0(历史遗留的问题)。 //第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组 //最后一个参数是真正的图像数据。 //自动生成多级纹理 glGenerateMipmap(GL_TEXTURE_2D); //释放 stbi_image_free(data); std::cout << Id << std::endl; } void bindTexture(int num) { switch (num) { case 0: glActiveTexture(GL_TEXTURE0); break; case 1: glActiveTexture(GL_TEXTURE1); break; case 2: glActiveTexture(GL_TEXTURE2); break; case 3: glActiveTexture(GL_TEXTURE3); break; case 4: glActiveTexture(GL_TEXTURE4); break; case 5: glActiveTexture(GL_TEXTURE5); break; case 6: glActiveTexture(GL_TEXTURE6); break; case 7: glActiveTexture(GL_TEXTURE7); break; default: std::cout << "纹理绑定支持0~7"<<std::endl; } glBindTexture(GL_TEXTURE_2D, Id); } void SetWrap(GLint pname) { if (pname == GL_REPEAT || pname == GL_MIRRORED_REPEAT || pname == GL_CLAMP_TO_EDGE || pname==GL_CLAMP_TO_BORDER) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, pname); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, pname); } else { std::cout << "输入不是设置环绕方式" << std::endl; } } void SetWrapS(GLint pname) { if (pname == GL_REPEAT || pname == GL_MIRRORED_REPEAT || pname == GL_CLAMP_TO_EDGE || pname == GL_CLAMP_TO_BORDER) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, pname); } else { std::cout << "输入不是设置环绕方式" << std::endl; } } void SetWrapT(GLint pname) { if (pname == GL_REPEAT || pname == GL_MIRRORED_REPEAT || pname == GL_CLAMP_TO_EDGE || pname == GL_CLAMP_TO_BORDER) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, pname); } else { std::cout << "输入不是设置环绕方式" << std::endl; } } void SetFilterMag(GLint pname) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, pname); } void SetFilterMin(GLint pname) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } };
使用
//初始化 mytexture1 = Texture("Resource/Texture/container.jpg",GL_RGB); mytexture2 = Texture("Resource/Texture/awesomeface.png",GL_RGBA); //传递 mytexture1.bindTexture(0); mytexture2.bindTexture(1);