终于把基本的框架搭完了,开始加一些零零碎碎的东西。材质方面,要在支持base color texture的基础上,再支持normal texture。
没什么难度,基本是在原来写的东西上,修修改改让代码复用性更强一点。
简单记录一下踩的坑。
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。渲染的时候直接用就可以。
上述写法在简单的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:
把
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,
)
)