【踩坑】 OpenGL texture 多纹理资源加载

终于把基本的框架搭完了,开始加一些零零碎碎的东西。材质方面,要在支持base color texture的基础上,再支持normal texture。
没什么难度,基本是在原来写的东西上,修修改改让代码复用性更强一点。
简单记录一下踩的坑。

多纹理加载设计1

glActiveTexture(GL_TEXTURE0); 
glBindTexture(GL_TEXTURE_2D, texture0);
glUniform1i(textureUniformLocation0, 0);
glActiveTexture(GL_TEXTURE1); 
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(textureUniformLocation1, 1);

从上面这段教程代码中不难发现,glActiveTexture的参数和glUniform1i函数的第二个参数有一个一一对应的关系。因此第一次改写的时候,使用了如下代码:

class Program:
    
    ...

    @property
    def id(self):
        return self.program

    def use(self):
        glUseProgram(self.program)
class Texture:
    _active_id = -1
    
    @staticmethod
    def get_active_id():
        Texture._active_id += 1
        return Texture._active_id
        
    ...
    
    def _use(self, program: Program):
            glActiveTexture(GL_TEXTURE0 + self.active_id)
            glBindTexture(GL_TEXTURE_2D, self.texture_id)
            glUniform1i(glGetUniformLocation(program.id, SAMPLER_NAME[self.type]), self.active_id)

在Texture资源初始化的时候,预先分配好texture id和active id。渲染的时候直接用就可以。

多纹理加载设计2

上述写法在简单的case上是能够跑通的,但当时因为写了个别的bug(见末尾),debug的时候突然意识到,这样写是有问题的。

问题出在active id上。glActiveTexture函数激活的是纹理单元(Texture Unit),区别于利用glGenTextures函数得到的texture id,active id的上限是很小的,因为纹理单元有数量限制。

LearnOpenGL:Textures

OpenGL should have a at least a minimum of 16 texture units for you to use which you can activate using GL_TEXTURE0 to GL_TEXTURE15.

glGenTextures - is there a limit to the number of textures?

The only limit of glGenTextures is given by the bit width of the texture name (GLint), which is 32 bit; indeed the number of texture names can be so great that you will probably never have problems when generating texture names.

如果按上述写法,当场景复杂的时候,active id一定会超出OpenGL能支持的纹理单元数量的上线。

加载gltf资源时,设计为每个primitive都预编译了一个shader,也就是说单个shader在运行的时候,只需要加载对应primitive需要的纹理贴图资源即可。因此将texture id和active id区分处理,改成如下写法:

class Program:
    
    ...

    @property
    def id(self):
        return self.program

    def use(self):
        glUseProgram(self.program)
        self.active_texture_id = -1

    def active_texture(self):
        self.active_texture_id += 1
        glActiveTexture(GL_TEXTURE0 + self.active_texture_id)
        return self.active_texture_id
class Texture:

    ...
    
    def _use(self, program: Program):
            active_id = program.active_texture()
            glBindTexture(GL_TEXTURE_2D, self.texture_id)
            glUniform1i(glGetUniformLocation(program.id, SAMPLER_NAME[self.type]), active_id)

迷幻bug

前面提到的bug:

glUniform1i(glGetUniformLocation(program.id, SAMPLER_NAME[self.type]), self.active_id)

写成了

glUniform1i(glGetUniformLocation(program.id, SAMPLER_NAME[self.type]), GL_TEXTURE0 + self.active_id)

蠢哭。

这个bug值得记录的原因是,报错很迷幻=-=

  File "/Users/.../Desktop/Projects/opengl_py/miniRender/vao.py", line 60, in render
    glDrawElements(GL_TRIANGLES, self.ebo.count, GL_UNSIGNED_SHORT, None)
  File "src/latebind.pyx", line 39, in OpenGL_accelerate.latebind.LateBind.__call__
  File "src/wrapper.pyx", line 318, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "src/wrapper.pyx", line 311, in OpenGL_accelerate.wrapper.Wrapper.__call__
  File "/usr/local/lib/python3.7/site-packages/OpenGL/platform/baseplatform.py", line 402, in __call__
    return self( *args, **named )
  File "src/errorchecker.pyx", line 58, in OpenGL_accelerate.errorchecker._ErrorChecker.glCheckError
OpenGL.error.GLError: GLError(
        err = 1282,
        description = b'invalid operation',
        baseOperation = glDrawElements,
        pyArgs = (
                GL_TRIANGLES,
                11364,
                GL_UNSIGNED_SHORT,
                None,
        ),
        cArgs = (
                GL_TRIANGLES,
                11364,
                GL_UNSIGNED_SHORT,
                None,
        ),
        cArguments = (
                GL_TRIANGLES,
                11364,
                GL_UNSIGNED_SHORT,
                None,
        )
)

你可能感兴趣的:(图形学)