pyrender icon indicating copy to clipboard operation
pyrender copied to clipboard

Core API and Structural Changes

Open SimantoR opened this issue 3 years ago • 1 comments

Description

The current implementation of pyrender uses a lot of hard-coded shader logic, such as binding textures and uniforms. This seriously limits the opportunity to use user-specified shaders.

Proposal

Abstract shader logic away from renderer to enable users to specify shaders and custom material to use those shaders, in a meaningful way. One way might be to abstract away shader logic from core Renderer class and allowing materials to manage shaders.

SimantoR avatar Apr 20 '21 23:04 SimantoR

# Bind textures
tf = material.tex_flags
if tf & TexFlags.NORMAL:
    self._bind_texture(material.normalTexture,
                       'material.normal_texture', program)
if tf & TexFlags.OCCLUSION:
    self._bind_texture(material.occlusionTexture,
                       'material.occlusion_texture', program)
if tf & TexFlags.EMISSIVE:
    self._bind_texture(material.emissiveTexture,
                       'material.emissive_texture', program)
if tf & TexFlags.BASE_COLOR:
    self._bind_texture(material.baseColorTexture,
                       'material.base_color_texture', program)
if tf & TexFlags.METALLIC_ROUGHNESS:
    self._bind_texture(material.metallicRoughnessTexture,
                       'material.metallic_roughness_texture',
                       program)
if tf & TexFlags.DIFFUSE:
    self._bind_texture(material.diffuseTexture,
                       'material.diffuse_texture', program)
if tf & TexFlags.SPECULAR_GLOSSINESS:
    self._bind_texture(material.specularGlossinessTexture,
                       'material.specular_glossiness_texture',
                       program)

# Bind other uniforms
b = 'material.{}'
program.set_uniform(b.format('emissive_factor'),
                    material.emissiveFactor)
if isinstance(material, MetallicRoughnessMaterial):
    program.set_uniform(b.format('base_color_factor'),
                        material.baseColorFactor)
    program.set_uniform(b.format('metallic_factor'),
                        material.metallicFactor)
    program.set_uniform(b.format('roughness_factor'),
                        material.roughnessFactor)
elif isinstance(material, SpecularGlossinessMaterial):
    program.set_uniform(b.format('diffuse_factor'),
                        material.diffuseFactor)
    program.set_uniform(b.format('specular_factor'),
                        material.specularFactor)
    program.set_uniform(b.format('glossiness_factor'),
                        material.glossinessFactor)

which is found in the Renderer._bind_and_draw_primitive function, will instead call

material = primitive.material
material.update_shader(self._bind_texture, program)

and Material.update_shader is an abstract function that each type of material will need to implement. This is what the update_shader function looks like for SpecularGlossinessMaterial

def update_shader(self, bindTex: Callable[[Any, str, ShaderProgram]], program: ShaderProgram):
    assert isinstance(
        program, ShaderProgram), 'material.update_shader requires ShaderProgram type'

    tf = self.tex_flags
    if tf & TexFlags.NORMAL:
        bindTex(self.normalTexture,     'material.normal_texture', program)
    if tf & TexFlags.OCCLUSION:
        bindTex(self.occlusionTexture,  'material.occlusion_texture', program)
    if tf & TexFlags.EMISSIVE:
        bindTex(self.emissiveTexture,   'material.emissive_texture', program)
    if tf & TexFlags.DIFFUSE:
        bindTex(self.diffuseTexture,    'material.diffuse_texture', program)
    if tf & TexFlags.SPECULAR_GLOSSINESS:
        bindTex(self.specularGlossinessTexture, 'material.specular_glossiness_texture', program)

    b = 'material.{}'
    program.set_uniform(b.format('diffuse_factor'), self.diffuseFactor)
    program.set_uniform(b.format('specular_factor'), self.specularFactor)
    program.set_uniform(b.format('glossiness_factor'), self.glossinessFactor)

while I understand the logic of user having to do alot of work to get something going, this allows user to create a custom ShaderProgram instance, load their own shaders, and have a programmatic way to use custom shaders and materials for those, without having to rewrite the library.

SimantoR avatar Apr 21 '21 12:04 SimantoR