Пример #1
0
    def setup_resources(self):
        self.common_buffer = Common.CommonBuffer()
        positions = [
            1.0,
            1.0,
            0.0,
            1.0,
            -1.0,
            0.0,
            -1.0,
            -1.0,
            0.0,
            -1.0,
            1.0,
            0.0,
        ]
        indices = [
            0,
            1,
            3,
            1,
            2,
            3,
        ]
        self.quad = Mesh(positions, indices)

        if Pipeline.BLEND_SHADER is None:
            source = '''#include "Passes/BlendTexture.glsl"'''
            Pipeline.BLEND_SHADER = self.compile_shader_from_source(source)
        self.blend_shader = Pipeline.BLEND_SHADER

        if Pipeline.COPY_SHADER is None:
            source = '''#include "Passes/CopyTextures.glsl"'''
            Pipeline.COPY_SHADER = self.compile_shader_from_source(source)
        self.copy_shader = Pipeline.COPY_SHADER
Пример #2
0
    def __init__(self):
        self.parameters = PipelineParameters()
        self.parameters.mesh['double_sided'] = Parameter(False, Type.BOOL)
        self.parameters.mesh['precomputed_tangents'] = Parameter(
            False, Type.BOOL)

        shader_dir = path.join(path.dirname(__file__), 'Shaders')
        if shader_dir not in Pipeline.SHADER_INCLUDE_PATHS:
            Pipeline.SHADER_INCLUDE_PATHS.append(shader_dir)

        self.resolution = None
        self.sample_count = 0

        self.result = None

        positions = [
            1.0,
            1.0,
            0.0,
            1.0,
            -1.0,
            0.0,
            -1.0,
            -1.0,
            0.0,
            -1.0,
            1.0,
            0.0,
        ]
        indices = [
            0,
            1,
            3,
            1,
            2,
            3,
        ]

        self.quad = Mesh(positions, indices)

        if Pipeline.BLEND_SHADER is None:
            source = '''#include "Passes/BlendTexture.glsl"'''
            Pipeline.BLEND_SHADER = self.compile_shader_from_source(source)
        self.blend_shader = Pipeline.BLEND_SHADER

        if Pipeline.COPY_SHADER is None:
            source = '''#include "Passes/CopyTextures.glsl"'''
            Pipeline.COPY_SHADER = self.compile_shader_from_source(source)
        self.copy_shader = Pipeline.COPY_SHADER

        self.default_shader = None
Пример #3
0
class Pipeline(object):

    GLSL_HEADER = '''
        #version 410 core
        #extension GL_ARB_shading_language_include : enable
    '''
    SHADER_INCLUDE_PATHS = []

    BLEND_SHADER = None
    COPY_SHADER = None

    def __init__(self):
        self.parameters = PipelineParameters()
        self.parameters.mesh['double_sided'] = Parameter(False, Type.BOOL)
        self.parameters.mesh['precomputed_tangents'] = Parameter(
            False, Type.BOOL)

        shader_dir = path.join(path.dirname(__file__), 'Shaders')
        if shader_dir not in Pipeline.SHADER_INCLUDE_PATHS:
            Pipeline.SHADER_INCLUDE_PATHS.append(shader_dir)

        self.resolution = None
        self.sample_count = 0

        self.result = None

        positions = [
            1.0,
            1.0,
            0.0,
            1.0,
            -1.0,
            0.0,
            -1.0,
            -1.0,
            0.0,
            -1.0,
            1.0,
            0.0,
        ]
        indices = [
            0,
            1,
            3,
            1,
            2,
            3,
        ]

        self.quad = Mesh(positions, indices)

        if Pipeline.BLEND_SHADER is None:
            source = '''#include "Passes/BlendTexture.glsl"'''
            Pipeline.BLEND_SHADER = self.compile_shader_from_source(source)
        self.blend_shader = Pipeline.BLEND_SHADER

        if Pipeline.COPY_SHADER is None:
            source = '''#include "Passes/CopyTextures.glsl"'''
            Pipeline.COPY_SHADER = self.compile_shader_from_source(source)
        self.copy_shader = Pipeline.COPY_SHADER

        self.default_shader = None

    def setup_render_targets(self, resolution):
        pass

    def draw_screen_pass(self, shader, target, blend=False):
        #Allow screen passes draw to gl_FragDepth
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_ALWAYS)
        glDisable(GL_CULL_FACE)
        if blend:
            glEnable(GL_BLEND)
        else:
            glDisable(GL_BLEND)
        target.bind()
        shader.bind()
        self.quad.draw()

    def blend_texture(self, blend_texture, target, opacity):
        self.blend_shader.textures['blend_texture'] = blend_texture
        glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA)
        glBlendEquation(GL_FUNC_ADD)
        glBlendColor(0, 0, 0, opacity)
        self.draw_screen_pass(self.blend_shader, target, True)

    def copy_textures(self, target, color_sources=[], depth_source=None):
        for i, texture in enumerate(color_sources):
            self.copy_shader.textures['IN_' + str(i)] = texture
        self.copy_shader.textures['IN_DEPTH'] = depth_source
        self.draw_screen_pass(self.copy_shader, target)

    def build_scene_batches(self, objects):
        result = {}
        for obj in objects:
            if obj.material not in result:
                result[obj.material] = {}
            if obj.mesh not in result[obj.material]:
                result[obj.material][obj.mesh] = {
                    'normal_scale': [],
                    'mirror_scale': [],
                }
            if obj.mirror_scale:
                result[obj.material][obj.mesh]['mirror_scale'].append(obj)
            else:
                result[obj.material][obj.mesh]['normal_scale'].append(obj)

        # Assume at least 64kb of UBO storage (d3d11 requirement) and max element size of mat4
        max_instances = 1000
        models = (max_instances * (ctypes.c_float * 16))()
        ids = (max_instances * ctypes.c_float)()

        for material, meshes in result.items():
            for mesh, scale_groups in meshes.items():
                for scale_group, objs in scale_groups.items():
                    batches = []
                    scale_groups[scale_group] = batches

                    i = 0
                    batch_length = len(objs)

                    while i < batch_length:
                        instance_i = i % max_instances
                        models[instance_i] = objs[i].matrix
                        ids[instance_i] = objs[i].parameters['ID']

                        i += 1
                        instances_count = instance_i + 1

                        if i == batch_length or instances_count == max_instances:
                            local_models = ((ctypes.c_float * 16) *
                                            instances_count).from_address(
                                                ctypes.addressof(models))
                            local_ids = (ctypes.c_float *
                                         instances_count).from_address(
                                             ctypes.addressof(ids))

                            models_UBO = UBO()
                            ids_UBO = UBO()

                            models_UBO.load_data(local_models)
                            ids_UBO.load_data(local_ids)

                            batches.append({
                                'instances_count': instances_count,
                                'BATCH_MODELS': models_UBO,
                                'BATCH_IDS': ids_UBO,
                            })

        return result

    def draw_scene_pass(self,
                        render_target,
                        batches,
                        pass_name=None,
                        default_shader=None,
                        uniform_blocks={},
                        uniforms={},
                        textures={},
                        shader_callbacks=[]):
        glDisable(GL_BLEND)
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LEQUAL)
        glDepthMask(GL_TRUE)
        glDepthRange(0, 1)

        render_target.bind()

        for material, meshes in batches.items():
            shader = default_shader
            if material and pass_name in material.shader and material.shader[
                    pass_name]:
                shader = material.shader[pass_name]

            for name, uniform in uniforms.items():
                if name in shader.uniforms:
                    shader.uniforms[name].set_value(uniform)

            for name, texture in textures.items():
                if name in shader.textures:
                    shader.textures[name] = texture

            for callback in shader_callbacks:
                callback(shader)

            shader.bind()

            for name, block in uniform_blocks.items():
                if name in shader.uniform_blocks:
                    block.bind(shader.uniform_blocks[name])

            for mesh, scale_groups in meshes.items():

                mesh.mesh.bind()

                for scale_group, batches in scale_groups.items():
                    if mesh.parameters['double_sided']:
                        glDisable(GL_CULL_FACE)
                    else:
                        glEnable(GL_CULL_FACE)
                        glCullFace(GL_BACK)
                    if scale_group == 'normal_scale':
                        glFrontFace(GL_CCW)
                        shader.uniforms['MIRROR_SCALE'].bind(False)
                    else:
                        glFrontFace(GL_CW)
                        shader.uniforms['MIRROR_SCALE'].bind(True)

                    for batch in batches:
                        batch['BATCH_MODELS'].bind(
                            shader.uniform_blocks['BATCH_MODELS'])
                        batch['BATCH_IDS'].bind(
                            shader.uniform_blocks['BATCH_IDS'])
                        glDrawElementsInstanced(GL_TRIANGLES,
                                                mesh.mesh.index_count,
                                                GL_UNSIGNED_INT, NULL,
                                                batch['instances_count'])

    def get_parameters(self):
        return self.parameters

    def get_samples(self):
        return [(0, 0)]

    def needs_more_samples(self):
        return self.sample_count < len(self.get_samples())

    def find_shader_path(self, path, search_paths=[]):
        if os.path.exists(path):
            return path
        else:
            for shader_path in self.SHADER_INCLUDE_PATHS + search_paths:
                full_path = os.path.join(shader_path, path)
                if os.path.exists(full_path):
                    return full_path
        return None

    def compile_shader_from_source(self,
                                   shader_source,
                                   include_paths=[],
                                   defines=[]):
        shader_source = Pipeline.GLSL_HEADER + shader_source
        include_paths = include_paths + Pipeline.SHADER_INCLUDE_PATHS

        vertex = shader_preprocessor(shader_source, include_paths,
                                     ['VERTEX_SHADER'] + defines)
        pixel = shader_preprocessor(shader_source, include_paths,
                                    ['PIXEL_SHADER'] + defines)

        return Shader(vertex, pixel)

    def compile_material_from_source(self,
                                     material_type,
                                     source,
                                     include_paths=[]):
        return {}

    def compile_material(self, shader_path, search_paths=[]):
        try:
            file_dir = path.dirname(shader_path)
            material_type = shader_path.split('.')[-2]
            source = '#include "{}"'.format(shader_path)
            return self.compile_material_from_source(material_type, source,
                                                     search_paths + [file_dir])
        except:
            import traceback
            return traceback.format_exc()

    def render(self, resolution, scene, is_final_render, is_new_frame):
        if self.resolution != resolution:
            self.resolution = resolution
            self.setup_render_targets(resolution)
            self.sample_count = 0

        if is_new_frame:
            self.sample_count = 0

        if self.needs_more_samples() == False:
            return self.result

        self.result = self.do_render(resolution, scene, is_final_render,
                                     is_new_frame)

        self.sample_count += 1

        return self.result

    def do_render(self, resolution, scene, is_final_render, is_new_frame):
        return {}
Пример #4
0
class Pipeline():

    SHADER_INCLUDE_PATHS = []

    BLEND_SHADER = None
    COPY_SHADER = None

    def __init__(self, plugins=[]):
        from multiprocessing.dummy import Pool
        self.pool = Pool(16)

        if SHADER_DIR not in Pipeline.SHADER_INCLUDE_PATHS:
            Pipeline.SHADER_INCLUDE_PATHS.append(SHADER_DIR)

        self.resolution = None
        self.sample_count = 0
        self.result = None
        self.is_final_render = None

        plugins = [plugin for plugin in plugins if plugin.poll_pipeline(self)]
        self.setup_parameters()
        for plugin in plugins:
            plugin.register_pipeline_parameters(self.parameters)
        self.setup_graphs()
        for plugin in plugins:
            for graph in plugin.register_pipeline_graphs():
                self.add_graph(graph)
        for plugin in plugins:
            plugin.register_graph_libraries(self.graphs)
        for graph in self.graphs.values():
            graph.setup_reflection()
        self.setup_resources()

    def setup_parameters(self):
        self.parameters = PipelineParameters()
        self.parameters.mesh['double_sided'] = Parameter(False, Type.BOOL)
        self.parameters.mesh['precomputed_tangents'] = Parameter(
            False, Type.BOOL)
        self.parameters.world['Material.Default'] = MaterialParameter(
            '', '.mesh.glsl')
        self.parameters.world['Material.Override'] = MaterialParameter(
            '', '.mesh.glsl')

    def get_parameters(self):
        return self.parameters

    def setup_graphs(self):
        self.graphs = {}

    def add_graph(self, graph):
        if graph.file_extension.endswith('glsl'):
            graph.include_paths += self.SHADER_INCLUDE_PATHS
        self.graphs[graph.name] = graph

    def get_graphs(self):
        result = {}
        for name, graph in self.graphs.items():
            result[name] = graph.get_serializable_copy()
        return result

    def setup_resources(self):
        self.common_buffer = Common.CommonBuffer()
        positions = [
            1.0,
            1.0,
            0.0,
            1.0,
            -1.0,
            0.0,
            -1.0,
            -1.0,
            0.0,
            -1.0,
            1.0,
            0.0,
        ]
        indices = [
            0,
            1,
            3,
            1,
            2,
            3,
        ]
        self.quad = Mesh(positions, indices)

        if Pipeline.BLEND_SHADER is None:
            source = '''#include "Passes/BlendTexture.glsl"'''
            Pipeline.BLEND_SHADER = self.compile_shader_from_source(source)
        self.blend_shader = Pipeline.BLEND_SHADER

        if Pipeline.COPY_SHADER is None:
            source = '''#include "Passes/CopyTextures.glsl"'''
            Pipeline.COPY_SHADER = self.compile_shader_from_source(source)
        self.copy_shader = Pipeline.COPY_SHADER

    def get_render_outputs(self):
        return {
            'COLOR': GL_RGBA32F,
            'DEPTH': GL_R32F,
        }

    def get_samples(self):
        return [(0, 0)]

    def needs_more_samples(self):
        return self.sample_count < len(self.get_samples())

    def setup_render_targets(self, resolution):
        pass

    def find_shader_path(self, path, search_paths=[]):
        if os.path.exists(path):
            return path
        else:
            for shader_path in self.SHADER_INCLUDE_PATHS + search_paths:
                full_path = os.path.join(shader_path, path)
                if os.path.exists(full_path):
                    return full_path
        return None

    def compile_shader_from_source(self, source, include_paths=[], defines=[]):
        vertex_src = shader_preprocessor(
            source, include_paths + self.SHADER_INCLUDE_PATHS,
            defines + ['VERTEX_SHADER'])
        pixel_src = shader_preprocessor(
            source, include_paths + self.SHADER_INCLUDE_PATHS,
            defines + ['PIXEL_SHADER'])
        return Shader(vertex_src, pixel_src)

    def compile_material_from_source(self,
                                     material_type,
                                     source,
                                     include_paths=[]):
        return self.graphs[material_type].compile_material(
            source, include_paths)

    def compile_material(self, shader_path, search_paths=[]):
        try:
            file_dir = path.dirname(shader_path)
            source = '#include "{}"'.format(path.basename(shader_path))
            material_type = shader_path.split('.')[-2]
            for graph in self.graphs.values():
                if shader_path.endswith(graph.file_extension):
                    material_type = graph.name
            return self.compile_material_from_source(material_type, source,
                                                     [file_dir] + search_paths)
        except Exception as e:
            import traceback
            traceback.print_exc()
            return str(e)

    def draw_screen_pass(self, shader, target, blend=False):
        #Allow screen passes draw to gl_FragDepth
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_ALWAYS)
        glDisable(GL_CULL_FACE)
        if blend:
            glEnable(GL_BLEND)
        else:
            glDisable(GL_BLEND)
        target.bind()
        shader.bind()
        self.quad.draw()

    def blend_texture(self, blend_texture, target, opacity):
        self.blend_shader.textures['blend_texture'] = blend_texture
        glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA)
        glBlendEquation(GL_FUNC_ADD)
        glBlendColor(0, 0, 0, opacity)
        self.draw_screen_pass(self.blend_shader, target, True)

    def copy_textures(self, target, color_sources=[], depth_source=None):
        for i, texture in enumerate(color_sources):
            self.copy_shader.textures[f'IN[{str(i)}]'] = texture
        self.copy_shader.textures['IN_DEPTH'] = depth_source
        self.draw_screen_pass(self.copy_shader, target)

    def build_scene_batches(self, objects):
        result = {}
        for obj in objects:
            if obj.material not in result:
                result[obj.material] = {}
            if obj.mesh not in result[obj.material]:
                result[obj.material][obj.mesh] = {}
            mesh_dict = result[obj.material][obj.mesh]
            if obj.mirror_scale:
                if 'mirror_scale' not in mesh_dict:
                    mesh_dict['mirror_scale'] = []
                mesh_dict['mirror_scale'].append(obj)
            else:
                if 'normal_scale' not in mesh_dict:
                    mesh_dict['normal_scale'] = []
                mesh_dict['normal_scale'].append(obj)

        # Assume at least 64kb of UBO storage (d3d11 requirement) and max element size of mat4
        max_instances = 1000
        models = (max_instances * (ctypes.c_float * 16))()
        ids = (max_instances * ctypes.c_uint)()

        for material, meshes in result.items():
            for mesh, scale_groups in meshes.items():
                for scale_group, objs in scale_groups.items():
                    batches = []
                    scale_groups[scale_group] = batches

                    i = 0
                    batch_length = len(objs)

                    while i < batch_length:
                        instance_i = i % max_instances
                        models[instance_i] = objs[i].matrix
                        ids[instance_i] = objs[i].parameters['ID']

                        i += 1
                        instances_count = instance_i + 1

                        if i == batch_length or instances_count == max_instances:
                            local_models = ((ctypes.c_float * 16) *
                                            instances_count).from_address(
                                                ctypes.addressof(models))
                            local_ids = (ctypes.c_uint *
                                         instances_count).from_address(
                                             ctypes.addressof(ids))

                            models_UBO = UBO()
                            ids_UBO = UBO()

                            models_UBO.load_data(local_models)
                            ids_UBO.load_data(local_ids)

                            batches.append({
                                'instances_count': instances_count,
                                'BATCH_MODELS': models_UBO,
                                'BATCH_IDS': ids_UBO,
                            })

        return result

    def draw_scene_pass(self,
                        render_target,
                        scene_batches,
                        pass_name=None,
                        default_shader=None,
                        shader_resources={},
                        depth_test_function=GL_LEQUAL):
        glDisable(GL_BLEND)
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(depth_test_function)
        glDepthMask(GL_TRUE)
        glDepthRange(0, 1)

        render_target.bind()

        _double_sided = None

        for material in scene_batches.keys():
            shader = default_shader
            if material and pass_name in material.shader and material.shader[
                    pass_name]:
                shader = material.shader[pass_name]

            for resource in shader_resources.values():
                resource.shader_callback(shader)

            shader.bind()

            precomputed_tangents_uniform = shader.uniforms.get(
                'PRECOMPUTED_TANGENTS')
            _precomputed_tangents = None
            _scale_group = None

            meshes = scene_batches[material]
            for mesh in meshes.keys():
                mesh.mesh.bind()

                double_sided = mesh.parameters['double_sided']
                if double_sided != _double_sided:
                    _double_sided = double_sided
                    if _double_sided:
                        glDisable(GL_CULL_FACE)
                    else:
                        glEnable(GL_CULL_FACE)
                        glCullFace(GL_BACK)

                if precomputed_tangents_uniform:
                    precomputed_tangents = mesh.parameters[
                        'precomputed_tangents']
                    if _precomputed_tangents != precomputed_tangents:
                        _precomputed_tangents = precomputed_tangents
                        precomputed_tangents_uniform.bind(precomputed_tangents)

                for scale_group, batches in meshes[mesh].items():
                    if scale_group != _scale_group:
                        _scale_group = scale_group
                        if scale_group == 'normal_scale':
                            glFrontFace(GL_CCW)
                            if 'MIRROR_SCALE' in shader.uniforms:
                                shader.uniforms['MIRROR_SCALE'].bind(False)
                        else:
                            glFrontFace(GL_CW)
                            if 'MIRROR_SCALE' in shader.uniforms:
                                shader.uniforms['MIRROR_SCALE'].bind(True)

                    for batch in batches:
                        batch['BATCH_MODELS'].bind(
                            shader.uniform_blocks['BATCH_MODELS'])
                        batch['BATCH_IDS'].bind(
                            shader.uniform_blocks['BATCH_IDS'])
                        glDrawElementsInstanced(GL_TRIANGLES,
                                                mesh.mesh.index_count,
                                                GL_UNSIGNED_INT, NULL,
                                                batch['instances_count'])

    def render(self, resolution, scene, is_final_render, is_new_frame):
        self.is_final_render = is_final_render
        if self.resolution != resolution:
            self.resolution = resolution
            self.setup_render_targets(resolution)
            self.sample_count = 0

        if is_new_frame:
            self.sample_count = 0

        if self.needs_more_samples() == False:
            return self.result

        self.common_buffer.load(scene, resolution)
        self.result = self.do_render(resolution, scene, is_final_render,
                                     is_new_frame)

        self.sample_count += 1

        return self.result

    def do_render(self, resolution, scene, is_final_render, is_new_frame):
        return {}