コード例 #1
0
ファイル: Texture.py プロジェクト: idkwim/PyEngine3D
class Texture:
    target = GL_TEXTURE_2D
    default_wrap = GL_REPEAT
    use_glTexStorage = False

    def __init__(self, **texture_data):
        self.name = texture_data.get('name')
        self.attachment = False
        self.image_mode = "RGBA"
        self.internal_format = GL_RGBA8
        self.texture_format = GL_RGBA
        self.sRGB = False
        self.clear_color = None
        self.multisample_count = 0

        self.width = 0
        self.height = 0
        self.depth = 1
        self.data_type = GL_UNSIGNED_BYTE
        self.min_filter = GL_LINEAR_MIPMAP_LINEAR
        self.mag_filter = GL_LINEAR
        self.enable_mipmap = False

        self.wrap = self.default_wrap
        self.wrap_s = self.default_wrap
        self.wrap_t = self.default_wrap
        self.wrap_r = self.default_wrap
        self.buffer = -1
        self.sampler_handle = -1
        self.attribute = Attributes()

        self.create_texture(**texture_data)

    def create_texture(self, **texture_data):
        if self.buffer != -1:
            self.delete()

        self.attachment = False
        self.image_mode = texture_data.get('image_mode')
        self.internal_format = texture_data.get('internal_format')
        self.texture_format = texture_data.get('texture_format')
        self.sRGB = texture_data.get('sRGB', False)
        self.clear_color = texture_data.get('clear_color')
        self.multisample_count = 0

        if self.internal_format is None and self.image_mode:
            self.internal_format = get_internal_format(self.image_mode)
        if self.texture_format is None and self.image_mode:
            self.texture_format = get_texture_format(self.image_mode)
        if self.image_mode is None and self.texture_format:
            self.image_mode = get_image_mode(self.texture_format)

        # Convert to sRGB
        if self.sRGB:
            if self.internal_format == GL_RGB:
                self.internal_format = GL_SRGB8
            elif self.internal_format == GL_RGBA:
                self.internal_format = GL_SRGB8_ALPHA8

        if GL_RGBA == self.internal_format:
            self.internal_format = GL_RGBA8
        if GL_RGB == self.internal_format:
            self.internal_format = GL_RGB8

        self.width = int(texture_data.get('width', 0))
        self.height = int(texture_data.get('height', 0))
        self.depth = int(max(1, texture_data.get('depth', 1)))
        self.data_type = texture_data.get('data_type', GL_UNSIGNED_BYTE)
        self.min_filter = texture_data.get('min_filter', GL_LINEAR_MIPMAP_LINEAR)
        self.mag_filter = texture_data.get('mag_filter', GL_LINEAR)  # GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_NEAREST

        mipmap_filters = (GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST,
                          GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_NEAREST)
        self.enable_mipmap = self.min_filter in mipmap_filters
        if self.target == GL_TEXTURE_2D_MULTISAMPLE:
            self.enable_mipmap = False

        self.wrap = texture_data.get('wrap', self.default_wrap)  # GL_REPEAT, GL_CLAMP
        self.wrap_s = texture_data.get('wrap_s')
        self.wrap_t = texture_data.get('wrap_t')
        self.wrap_r = texture_data.get('wrap_r')
        self.buffer = -1
        self.sampler_handle = -1

        # texture parameter overwrite
        # self.sampler_handle = glGenSamplers(1)
        # glSamplerParameteri(self.sampler_handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        # glBindSampler(0, self.sampler_handle)

        logger.info("Create %s : %s %dx%dx%d %s mipmap(%s)." % (
            GetClassName(self), self.name, self.width, self.height, self.depth, str(self.internal_format),
            'Enable' if self.enable_mipmap else 'Disable'))

        self.attribute = Attributes()

    def __del__(self):
        pass

    def delete(self):
        logger.info("Delete %s : %s" % (GetClassName(self), self.name))
        glDeleteTextures([self.buffer, ])
        self.buffer = -1

    def get_texture_info(self):
        return dict(
            texture_type=self.__class__.__name__,
            width=self.width,
            height=self.height,
            depth=self.depth,
            image_mode=self.image_mode,
            internal_format=self.internal_format,
            texture_format=self.texture_format,
            data_type=self.data_type,
            min_filter=self.min_filter,
            mag_filter=self.mag_filter,
            wrap=self.wrap,
            wrap_s=self.wrap_s,
            wrap_t=self.wrap_t,
            wrap_r=self.wrap_r,
        )

    def get_save_data(self):
        save_data = self.get_texture_info()
        data = self.get_image_data()
        if data is not None:
            save_data['data'] = data
        return save_data

    def get_image_data(self):
        if self.target not in (GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D):
            return None

        dtype = get_numpy_dtype(self.data_type)

        try:
            glBindTexture(self.target, self.buffer)
            data = OpenGLContext.glGetTexImage(self.target, 0, self.texture_format, self.data_type)
            # convert to numpy array
            if type(data) is bytes:
                data = np.fromstring(data, dtype=dtype)
            else:
                data = np.array(data, dtype=dtype)
            glBindTexture(self.target, 0)
            return data
        except:
            logger.error(traceback.format_exc())
            logger.error('%s failed to get image data.' % self.name)
            logger.info('Try to glReadPixels.')

        glBindTexture(self.target, self.buffer)
        fb = glGenFramebuffers(1)
        glBindFramebuffer(GL_FRAMEBUFFER, fb)

        data = []
        for layer in range(self.depth):
            if GL_TEXTURE_2D == self.target:
                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.buffer, 0)
            elif GL_TEXTURE_3D == self.target:
                glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, self.buffer, 0, layer)
            elif GL_TEXTURE_2D_ARRAY == self.target:
                glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, self.buffer, 0, layer)
            glReadBuffer(GL_COLOR_ATTACHMENT0)
            pixels = glReadPixels(0, 0, self.width, self.height, self.texture_format, self.data_type)
            # convert to numpy array
            if type(pixels) is bytes:
                pixels = np.fromstring(pixels, dtype=dtype)
            data.append(pixels)
        data = np.array(data, dtype=dtype)
        glBindTexture(self.target, 0)
        glBindFramebuffer(GL_FRAMEBUFFER, 0)
        glDeleteFramebuffers(1, [fb, ])
        return data

    def get_mipmap_count(self):
        factor = max(max(self.width, self.height), self.depth)
        return math.floor(math.log2(factor)) + 1

    def generate_mipmap(self):
        if self.enable_mipmap:
            glBindTexture(self.target, self.buffer)
            glGenerateMipmap(self.target)
        else:
            logger.warn('%s disable to generate mipmap.' % self.name)

    def texure_wrap(self, wrap):
        glTexParameteri(self.target, GL_TEXTURE_WRAP_S, wrap)
        glTexParameteri(self.target, GL_TEXTURE_WRAP_T, wrap)
        glTexParameteri(self.target, GL_TEXTURE_WRAP_R, wrap)

    def bind_texture(self, wrap=None):
        if self.buffer == -1:
            logger.warn("%s texture is invalid." % self.name)
            return

        glBindTexture(self.target, self.buffer)

        if wrap is not None:
            self.texure_wrap(wrap)

    def bind_image(self, image_unit, level=0, access=GL_READ_WRITE):
        if self.buffer == -1:
            logger.warn("%s texture is invalid." % self.name)
            return
        # flag : GL_READ_WRITE, GL_WRITE_ONLY, GL_READ_ONLY
        glBindImageTexture(image_unit, self.buffer, level, GL_FALSE, 0, access, self.internal_format)

    def is_attached(self):
        return self.attachment

    def set_attachment(self, attachment):
        self.attachment = attachment

    def get_attribute(self):
        self.attribute.set_attribute("name", self.name)
        self.attribute.set_attribute("target", self.target)
        self.attribute.set_attribute("width", self.width)
        self.attribute.set_attribute("height", self.height)
        self.attribute.set_attribute("depth", self.depth)
        self.attribute.set_attribute("image_mode", self.image_mode)
        self.attribute.set_attribute("internal_format", self.internal_format)
        self.attribute.set_attribute("texture_format", self.texture_format)
        self.attribute.set_attribute("data_type", self.data_type)
        self.attribute.set_attribute("min_filter", self.min_filter)
        self.attribute.set_attribute("mag_filter", self.mag_filter)
        self.attribute.set_attribute("multisample_count", self.multisample_count)
        self.attribute.set_attribute("wrap", self.wrap)
        self.attribute.set_attribute("wrap_s", self.wrap_s)
        self.attribute.set_attribute("wrap_t", self.wrap_t)
        self.attribute.set_attribute("wrap_r", self.wrap_r)
        return self.attribute

    def set_attribute(self, attribute_name, attribute_value, parent_info, attribute_index):
        if hasattr(self, attribute_name) and "" != attribute_value:
            setattr(self, attribute_name, eval(attribute_value))

        if 'wrap' in attribute_name:
            glBindTexture(self.target, self.buffer)
            glTexParameteri(self.target, GL_TEXTURE_WRAP_S, self.wrap_s or self.wrap)
            glTexParameteri(self.target, GL_TEXTURE_WRAP_T, self.wrap_t or self.wrap)
            glTexParameteri(self.target, GL_TEXTURE_WRAP_R, self.wrap_r or self.wrap)
            glBindTexture(self.target, 0)

        return self.attribute
コード例 #2
0
ファイル: Model.py プロジェクト: idkwim/PyEngine3D
class Model:
    def __init__(self, name, **data):
        self.name = name
        self.mesh = None
        self.material_instances = []
        self.set_mesh(data.get('mesh'))

        for i, material_instance in enumerate(data.get('material_instances', [])):
            self.set_material_instance(material_instance, i)

        self.attributes = Attributes()

    def set_mesh(self, mesh):
        if mesh:
            self.mesh = mesh
            default_material_instance = CoreManager.instance().resource_manager.get_default_material_instance(
                skeletal=mesh.has_bone())
            material_instances = [default_material_instance, ] * len(mesh.geometries)
            for i in range(min(len(self.material_instances), len(material_instances))):
                material_instances[i] = self.material_instances[i]
            self.material_instances = material_instances

    def get_save_data(self):
        save_data = dict(
            object_type=GetClassName(self),
            mesh=self.mesh.name if self.mesh is not None else '',
            material_instances=[material_instance.name for material_instance in self.material_instances]
        )
        return save_data

    def get_material_count(self):
        return len(self.material_instances)

    def get_material_instance(self, index):
        return self.material_instances[index]

    def get_material_instance_name(self, index):
        material_instance = self.get_material_instance(index)
        return material_instance.name if material_instance else ''

    def get_material_instance_names(self):
        return [self.get_material_instance_name(i) for i in range(self.get_material_count())]

    def set_material_instance(self, material_instance, attribute_index):
        if attribute_index < len(self.material_instances):
            self.material_instances[attribute_index] = material_instance

    def get_attribute(self):
        self.attributes.set_attribute('name', self.name)
        self.attributes.set_attribute('mesh', self.mesh)
        self.attributes.set_attribute('material_instances', self.get_material_instance_names())
        return self.attributes

    def set_attribute(self, attribute_name, attribute_value, parent_info, attribute_index):
        if attribute_name == 'mesh':
            mesh = CoreManager.instance().resource_manager.get_mesh(attribute_value)
            if mesh and self.mesh != mesh:
                self.set_mesh(mesh)
        elif attribute_name == 'material_instances':
            material_instance = CoreManager.instance().resource_manager.get_material_instance(
                attribute_value[attribute_index])
            self.set_material_instance(material_instance, attribute_index)
コード例 #3
0
class VectorFieldTexture3D:
    def __init__(self, **data):
        self.name = self.__class__.__name__
        self.texture_name = 'vector_field_3d'
        self.texture_width = data.get('texture_width', 256)
        self.texture_height = data.get('texture_height', 256)
        self.texture_depth = data.get('texture_depth', 256)
        self.attribute = Attributes()

    def generate_texture(self):
        logger.info("Generate VectorFieldTexture3D.")

        core_manager = CoreManager.getInstance()
        resource_manager = core_manager.resource_manager
        renderer = core_manager.renderer

        texture = CreateTexture(
            name=self.texture_name,
            texture_type=Texture3D,
            width=self.texture_width,
            height=self.texture_height,
            depth=self.texture_depth,
            internal_format=GL_RGBA16F,
            texture_format=GL_RGBA,
            min_filter=GL_LINEAR,
            mag_filter=GL_LINEAR,
            data_type=GL_FLOAT,
            wrap=GL_CLAMP,
        )

        resource = resource_manager.texture_loader.get_resource(
            self.texture_name)
        if resource is None:
            resource = resource_manager.texture_loader.create_resource(
                self.texture_name, texture)
        else:
            old_texture = resource.get_data()
            if old_texture is not None:
                old_texture.delete()
            resource.set_data(texture)

        glPolygonMode(GL_FRONT_AND_BACK, renderer.view_mode)
        glDepthFunc(GL_LEQUAL)
        glEnable(GL_CULL_FACE)
        glFrontFace(GL_CCW)
        glEnable(GL_DEPTH_TEST)
        glDepthMask(True)
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClearDepth(1.0)

        renderer.set_blend_state(False)

        renderer.framebuffer_manager.bind_framebuffer(texture)
        glClear(GL_COLOR_BUFFER_BIT)

        material_instance = resource_manager.get_material_instance(
            'procedural.vector_field_3d')
        material_instance.use_program()

        for i in range(texture.depth):
            material_instance.bind_uniform_data('depth', i / texture.depth)
            renderer.framebuffer_manager.bind_framebuffer(texture,
                                                          target_layer=i)
            renderer.postprocess.draw_elements()

        renderer.restore_blend_state_prev()

        # save
        resource_manager.texture_loader.save_resource(resource.name)

    def get_save_data(self):
        save_data = dict(
            texture_type=self.__class__.__name__,
            texture_name=self.texture_name,
            texture_width=self.texture_width,
            texture_height=self.texture_height,
            texture_depth=self.texture_depth,
        )
        return save_data

    def get_attribute(self):
        self.attribute.set_attribute("texture_name", self.texture_name)
        self.attribute.set_attribute("texture_width", self.texture_width)
        self.attribute.set_attribute("texture_height", self.texture_height)
        self.attribute.set_attribute("texture_depth", self.texture_depth)
        return self.attribute

    def set_attribute(self, attribute_name, attribute_value, parent_info,
                      attribute_index):
        if hasattr(self, attribute_name):
            setattr(self, attribute_name, attribute_value)
        return self.attribute
コード例 #4
0
class MaterialInstance:
    def __init__(self, material_instance_name, **data):
        self.valid = False
        self.isNeedToSave = False
        logger.info("Load Material Instance : " + material_instance_name)
        self.name = material_instance_name
        self.shader_name = data.get('shader_name', 'default')
        self.material = None
        self.material_name = data.get('material_name', 'default')
        self.macros = copy.copy(data.get('macros', OrderedDict()))
        self.linked_uniform_map = dict()
        self.linked_material_component_map = dict()
        self.show_message = {}
        self.Attributes = Attributes()

        material = data.get('material')

        # link uniform_buffers and uniform_data
        self.set_material(material)

        if self.material:
            # and set the loaded uniform data.
            uniform_datas = data.get('uniform_datas', {})
            for data_name, data_value in uniform_datas.items():
                self.set_uniform_data_from_string(data_name, data_value)
        else:
            logger.error("%s material instance has no material." % self.name)
            return

        self.valid = True

    def clear(self):
        self.linked_uniform_map = OrderedDict({})
        self.Attributes.clear()

    def is_translucent(self):
        return self.material.is_translucent

    def get_save_data(self):
        uniform_datas = {}
        for uniform_name in self.linked_uniform_map:
            uniform_buffer, uniform_data = self.linked_uniform_map[
                uniform_name]
            if hasattr(uniform_data, 'name'):
                uniform_datas[uniform_name] = uniform_data.name
            else:
                uniform_datas[uniform_name] = uniform_data

        save_data = dict(
            shader_name=self.material.shader_name
            if self.material else 'default',
            material_name=self.material.name if self.material else 'default',
            macros=self.macros,
            uniform_datas=uniform_datas,
        )
        return save_data

    def set_material(self, material):
        if material and self.material != material:
            self.isNeedToSave = self.material_name != material.name

            self.material = material
            self.material_name = material.name
            self.macros = copy.copy(material.macros)

            # link_uniform_buffers
            old_uniform_names = list(self.linked_uniform_map.keys())
            self.linked_material_component_map = dict()
            material_uniform_names = material.uniform_buffers.keys()
            material_component_names = material.material_component_names
            for uniform_name in material_uniform_names:
                if uniform_name in old_uniform_names:
                    old_uniform_names.remove(uniform_name)
                uniform_buffer = material.uniform_buffers[uniform_name]
                if uniform_name not in self.linked_uniform_map:
                    # cannot found uniform data. just set default uniform data.
                    uniform_data = CreateUniformDataFromString(
                        uniform_buffer.uniform_type)
                    if uniform_data is not None:
                        # link between uniform buffer and data.
                        self.linked_uniform_map[uniform_name] = [
                            uniform_buffer, uniform_data
                        ]
                    else:
                        logger.error(
                            "%s material instance failed to create %s uniform data %s."
                            % (self.name, uniform_name, uniform_data))
                        continue

                if uniform_name in material_component_names:
                    self.linked_material_component_map[
                        uniform_name] = self.linked_uniform_map[uniform_name]
            # Remove the uniform data that is not in Material and Shader.
            for uniform_name in old_uniform_names:
                self.linked_uniform_map.pop(uniform_name)

    def bind_material_instance(self):
        for uniform_buffer, uniform_data in self.linked_material_component_map.values(
        ):
            uniform_buffer.bind_uniform(uniform_data)

    def bind_uniform_data(self, uniform_name, uniform_data, **kwargs):
        uniform = self.linked_uniform_map.get(uniform_name)
        if uniform:
            uniform[0].bind_uniform(uniform_data, **kwargs)
        elif uniform_name not in self.show_message or self.show_message[
                uniform_name]:
            self.show_message[uniform_name] = False
            logger.warn('%s material instance has no %s uniform variable.' %
                        (self.name, uniform_name))

    def get_uniform_data(self, uniform_name):
        uniform = self.linked_uniform_map.get(uniform_name)
        return uniform[1] if uniform else None

    def set_uniform_data(self, uniform_name, uniform_data):
        uniform = self.linked_uniform_map.get(uniform_name)
        if uniform:
            uniform[1] = uniform_data

    def set_uniform_data_from_string(self, uniform_name, str_uniform_data):
        uniform = self.linked_uniform_map.get(uniform_name)
        if uniform:
            uniform_buffer = uniform[0]
            if uniform_buffer:
                uniform_data = CreateUniformDataFromString(
                    uniform_buffer.uniform_type, str_uniform_data)
                if uniform_data is not None:
                    uniform[1] = uniform_data
                    return True
        logger.warn(
            "%s material instance has no %s uniform variable. It may have been optimized by the compiler..)"
            % (self.name, uniform_name))

    def get_program(self):
        return self.material.program

    def use_program(self):
        self.material.use_program()

    def get_attribute(self):
        self.Attributes.set_attribute('name', self.name)
        self.Attributes.set_attribute('shader_name', self.shader_name)
        self.Attributes.set_attribute('material_name', self.material_name)
        for uniform_buffer, uniform_data in self.linked_material_component_map.values(
        ):
            self.Attributes.set_attribute(uniform_buffer.name, uniform_data)
        for key in self.macros:
            self.Attributes.set_attribute(key, self.macros[key])
        return self.Attributes

    def set_attribute(self, attribute_name, attribute_value, parent_info,
                      attribute_index):
        if attribute_name == 'shader_name':
            if attribute_value != self.shader_name:
                material = CoreManager.instance(
                ).resource_manager.get_material(attribute_value, self.macros)
                self.set_material(material)
        elif attribute_name in 'material_name':
            if self.material:
                material = CoreManager.instance(
                ).resource_manager.get_material(self.material.shader_name)
                self.set_material(material)
        elif attribute_name in self.linked_material_component_map:
            self.set_uniform_data_from_string(attribute_name, attribute_value)
        elif attribute_name in self.macros:
            if self.macros[attribute_name] != attribute_value:
                self.macros[attribute_name] = attribute_value
                material = CoreManager.instance(
                ).resource_manager.get_material(self.material.shader_name,
                                                self.macros)
                self.set_material(material)
        return self.Attributes
コード例 #5
0
class Material:
    def __init__(self, material_name, material_datas={}):
        self.valid = False
        logger.info("Load %s material." % material_name)

        shader_codes = material_datas.get('shader_codes')
        binary_format = material_datas.get('binary_format')
        binary_data = material_datas.get('binary_data')
        uniforms = material_datas.get('uniforms', [])
        self.material_component_names = [
            x[1] for x in material_datas.get('material_components', [])
        ]
        self.macros = material_datas.get('macros', OrderedDict())

        self.is_translucent = True if 0 < self.macros.get(
            'TRANSPARENT_MATERIAL', 0) else False

        self.name = material_name
        self.shader_name = material_datas.get('shader_name', '')
        self.program = -1
        self.uniform_buffers = dict(
        )  # OrderedDict()  # Declaration order is important.
        self.Attributes = Attributes()

        if binary_format is not None and binary_data is not None:
            self.compile_from_binary(binary_format, binary_data)
            self.valid = self.check_validate() and self.check_linked()
            if not self.valid:
                logger.error(
                    "%s material has been failed to compile from binary" %
                    self.name)

        self.compile_message = ""

        if not self.valid:
            self.compile_from_source(shader_codes)
            self.valid = self.check_validate() and self.check_linked()
            if not self.valid:
                logger.error(
                    "%s material has been failed to compile from source" %
                    self.name)

        if self.valid:
            self.create_uniform_buffers(uniforms)

    def get_attribute(self):
        self.Attributes.set_attribute('name', self.name)
        self.Attributes.set_attribute('shader_name', self.shader_name)
        for key in self.macros:
            self.Attributes.set_attribute(key, self.macros[key])
        return self.Attributes

    def set_attribute(self, attribute_name, attribute_value, parent_info,
                      attribute_index):
        if attribute_name in self.macros and self.macros[
                attribute_name] != attribute_value:
            new_macros = copy.deepcopy(self.macros)
            new_macros[attribute_name] = attribute_value
            # if macro was changed then create a new material.
            CoreManager.instance().resource_manager.get_material(
                self.shader_name, new_macros)

    def delete(self):
        OpenGLContext.use_program(0)
        glDeleteProgram(self.program)
        logger.info("Deleted %s material." % self.name)

    def use_program(self):
        OpenGLContext.use_program(self.program)

    def save_to_binary(self):
        size = GLint()
        glGetProgramiv(self.program, GL_PROGRAM_BINARY_LENGTH, size)
        # very important - check data dtype np.ubyte
        binary_data = np.zeros(size.value, dtype=np.ubyte)
        binary_size = GLint()
        binary_format = GLenum()
        glGetProgramBinary(self.program, size.value, binary_size,
                           binary_format, binary_data)
        binary_data = pickle.dumps(binary_data)
        return binary_format, binary_data

    def compile_from_binary(self, binary_format, binary_data):
        binary_data = pickle.loads(binary_data)
        self.program = glCreateProgram()
        glProgramParameteri(self.program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT,
                            GL_TRUE)
        glProgramBinary(self.program, binary_format.value, binary_data,
                        len(binary_data))

    def compile_from_source(self, shader_codes: dict):
        shaders = []
        for shader_type in shader_codes:
            shader = self.compile(shader_type, shader_codes[shader_type])
            if shader is not None:
                logger.info("Compile %s %s." % (self.name, shader_type))
                shaders.append(shader)

        self.program = glCreateProgram()

        # glProgramParameteri(self.program, GL_PROGRAM_SEPARABLE, GL_TRUE)
        glProgramParameteri(self.program, GL_PROGRAM_BINARY_RETRIEVABLE_HINT,
                            GL_TRUE)

        for shader in shaders:
            glAttachShader(self.program, shader)

        glLinkProgram(self.program)

        for shader in shaders:
            glDetachShader(self.program, shader)
            glDeleteShader(shader)

    def create_uniform_buffers(self, uniforms):
        # create uniform buffers from source code
        active_texture_index = 0
        for uniform_type, uniform_name in uniforms:
            uniform_buffer = CreateUniformBuffer(self.program, uniform_type,
                                                 uniform_name)
            if uniform_buffer is not None:
                # Important : set texture binding index
                if issubclass(uniform_buffer.__class__, UniformTextureBase):
                    uniform_buffer.set_texture_index(active_texture_index)
                    active_texture_index += 1
                self.uniform_buffers[uniform_name] = uniform_buffer
            else:
                logger.warn(
                    "%s material has no %s uniform variable. It may have been optimized by the compiler..)"
                    % (self.name, uniform_name))
        return True

    def compile(self, shaderType, shader_code):
        """
        :param shaderType: GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
        :param shader_code: string
        """
        if shader_code == "" or shader_code is None:
            return None

        try:
            # Compile shaders
            shader = glCreateShader(shaderType)
            glShaderSource(shader, shader_code)
            glCompileShader(shader)
            compile_status = glGetShaderiv(shader, GL_COMPILE_STATUS)
            if compile_status != 1:
                infoLogs = glGetShaderInfoLog(shader)
                if infoLogs:
                    if type(infoLogs) == bytes:
                        infoLogs = infoLogs.decode("utf-8")

                    infoLogs = ("GL_COMPILE_STATUS : %d\n" %
                                compile_status) + infoLogs
                    shader_code_lines = shader_code.split('\n')

                    infoLogs = infoLogs.split('\n')
                    for i, infoLog in enumerate(infoLogs):
                        error_line = re.match('\d\((\d+)\) : error', infoLog)
                        if error_line is not None:
                            # show prev 3 lines
                            error_line = int(error_line.groups()[0]) - 1
                            for num in range(max(0, error_line - 3),
                                             error_line):
                                infoLogs[i] += "\n\t    %s" % (
                                    shader_code_lines[num])
                            # show last line
                            infoLogs[i] += "\n\t--> %s" % (
                                shader_code_lines[error_line])

                    infoLogs = "\n".join(infoLogs)

                    self.compile_message = "\n".join(
                        [self.compile_message, infoLogs])

                    logger.error("%s %s shader compile error.\n%s" %
                                 (self.name, shaderType.name, infoLogs))
            else:
                # complete
                logger.log(
                    Logger.MINOR_INFO,
                    "Complete %s %s compile." % (self.name, shaderType.name))
                return shader
        except BaseException:
            logger.error(traceback.format_exc())
        return None

    def check_validate(self):
        if self.program >= 0:
            glValidateProgram(self.program)
            validation = glGetProgramiv(self.program, GL_VALIDATE_STATUS)
            if validation == GL_TRUE:
                return True
            else:
                logger.warn("Validation failure (%s): %s" %
                            (validation, glGetProgramInfoLog(self.program)))
        else:
            logger.warn("Validation failure : %s" % self.name)
        # always return True
        return True

    def check_linked(self):
        if self.program >= 0:
            link_status = glGetProgramiv(self.program, GL_LINK_STATUS)
            if link_status == GL_TRUE:
                return True
            else:
                logger.error("Link failure (%s): %s" %
                             (link_status, glGetProgramInfoLog(self.program)))
        else:
            logger.error("Link failure : %s" % self.name)
        return False
コード例 #6
0
class CloudTexture3D:
    def __init__(self, **data):
        self.name = self.__class__.__name__
        self.texture_name = 'cloud_3d'
        self.width = data.get('width', 128)
        self.height = data.get('height', 128)
        self.depth = data.get('depth', 128)
        self.sphere_scale = data.get('sphere_scale', 0.15)
        self.sphere_count = data.get('sphere_count', 4096)
        self.noise_persistance = data.get('noise_persistance', 0.7)
        self.noise_scale = data.get('noise_scale', 6)
        self.attribute = Attributes()

    def generate_texture(self):
        logger.info("Generate CloudTexture3D.")

        core_manager = CoreManager.getInstance()
        resource_manager = core_manager.resource_manager
        renderer = core_manager.renderer

        texture = CreateTexture(
            name=self.texture_name,
            texture_type=Texture3D,
            width=self.width,
            height=self.height,
            depth=self.depth,
            internal_format=GL_R16F,
            texture_format=GL_RED,
            min_filter=GL_LINEAR,
            mag_filter=GL_LINEAR,
            data_type=GL_FLOAT,
            wrap=GL_REPEAT,
        )

        resource = resource_manager.texture_loader.get_resource(
            self.texture_name)
        if resource is None:
            resource = resource_manager.texture_loader.create_resource(
                self.texture_name, texture)
            resource_manager.texture_loader.save_resource(resource.name)
        else:
            old_texture = resource.get_data()
            old_texture.delete()
            resource.set_data(texture)

        glPolygonMode(GL_FRONT_AND_BACK, renderer.view_mode)
        glDepthFunc(GL_LEQUAL)
        glEnable(GL_CULL_FACE)
        glFrontFace(GL_CCW)
        glEnable(GL_DEPTH_TEST)
        glDepthMask(True)
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClearDepth(1.0)

        renderer.set_blend_state(False)

        renderer.framebuffer_manager.bind_framebuffer(texture)
        glClear(GL_COLOR_BUFFER_BIT)

        mat = resource_manager.get_material_instance(
            'procedural.cloud_noise_3d')
        mat.use_program()
        mat.bind_uniform_data('texture_random',
                              resource_manager.get_texture("common.random"))
        mat.bind_uniform_data('random_seed', random.random())
        mat.bind_uniform_data('sphere_count', self.sphere_count)
        mat.bind_uniform_data('sphere_scale', self.sphere_scale)
        mat.bind_uniform_data('noise_persistance', self.noise_persistance)
        mat.bind_uniform_data('noise_scale', self.noise_scale)

        for i in range(texture.depth):
            mat.bind_uniform_data('depth', i / texture.depth)
            renderer.framebuffer_manager.bind_framebuffer(texture,
                                                          target_layer=i)
            renderer.postprocess.draw_elements()

        renderer.restore_blend_state_prev()

    def get_save_data(self):
        save_data = dict(
            texture_type=self.__class__.__name__,
            texture_name=self.texture_name,
            width=self.width,
            height=self.height,
            depth=self.depth,
            sphere_scale=self.sphere_scale,
            sphere_count=self.sphere_count,
            noise_persistance=self.noise_persistance,
            noise_scale=self.noise_scale,
        )
        return save_data

    def get_attribute(self):
        self.attribute.set_attribute("texture_name", self.texture_name)
        self.attribute.set_attribute("width", self.width)
        self.attribute.set_attribute("height", self.height)
        self.attribute.set_attribute("depth", self.depth)
        self.attribute.set_attribute("sphere_scale", self.sphere_scale)
        self.attribute.set_attribute("sphere_count", self.sphere_count)
        self.attribute.set_attribute("noise_persistance",
                                     self.noise_persistance)
        self.attribute.set_attribute("noise_scale", self.noise_scale)
        return self.attribute

    def set_attribute(self, attribute_name, attribute_value, item_info_history,
                      attribute_index):
        if hasattr(self, attribute_name):
            setattr(self, attribute_name, attribute_value)
        return self.attribute
コード例 #7
0
class Shader:
    default_macros = dict(MATERIAL_COMPONENTS=1)

    def __init__(self, shader_name, shader_code):
        logger.info("Load " + GetClassName(self) + " : " + shader_name)
        self.name = shader_name
        self.shader_code = shader_code
        self.include_files = []
        self.attribute = Attributes()

    def get_save_data(self):
        return self.shader_code

    def get_attribute(self):
        self.attribute.set_attribute("name", self.name)
        return self.attribute

    def generate_shader_codes(self,
                              is_engine_resource,
                              engine_shader_directory,
                              project_shader_directory,
                              shader_version,
                              compile_option,
                              external_macros={}):
        shader_codes = {}
        for shader_type_name in shader_types:
            shader_type = shader_types[shader_type_name]
            shader_code = self.__parsing_final_code__(
                is_engine_resource, engine_shader_directory,
                project_shader_directory, shader_type_name, shader_version,
                compile_option, external_macros)
            # check void main
            if re.search(reVoidMain, shader_code) is not None:
                shader_codes[shader_type] = shader_code
        return shader_codes

    def __parsing_final_code__(self,
                               is_engine_resource,
                               engine_shader_directory,
                               project_shader_directory,
                               shader_type_name,
                               shader_version,
                               compile_option,
                               external_macros={}):
        if self.shader_code == "" or self.shader_code is None:
            return ""

        # remove comment block
        shader_code = re.sub(reComment, "", self.shader_code)
        code_lines = shader_code.splitlines()

        # combine macro
        combined_macros = OrderedDict()
        # default macro
        for macro in self.default_macros:
            combined_macros[macro] = self.default_macros[macro]
        # shader type macro
        combined_macros[shader_type_name] = "1"

        # external macro
        if external_macros is None:
            external_macros = {}

        for macro in external_macros:
            if external_macros[macro] is None or external_macros[macro] is '':
                combined_macros[macro] = 0
            else:
                combined_macros[macro] = external_macros[macro]

        # insert shader version - ex) #version 430 core
        final_code_lines = [
            shader_version, "# extension GL_EXT_texture_array : enable"
        ]

        # insert defines to final code
        for macro in combined_macros:
            final_code_lines.append("#define %s %s" %
                                    (macro, str(combined_macros[macro])))

        # global texture function
        if ShaderCompileOption.USE_GLOBAL_TEXTURE_FUNCTION in compile_option:
            final_code_lines.append("#if __VERSION__ >= 130")
            # ex) replace texture2D -> texutre, textureCubeLod -> textureLod
            for texture_target in texture_targets:
                if "Lod" in texture_target:
                    final_code_lines.append("#define %s textureLod" %
                                            texture_target)
                elif "Grad" in texture_target:
                    final_code_lines.append("#define %s textureGrad" %
                                            texture_target)
                else:
                    final_code_lines.append("#define %s texture" %
                                            texture_target)
            final_code_lines.append("#endif")

        # insert version as comment
        include_files = dict()  # { 'filename': uuid }

        # do parsing
        line_num = 0
        macro_depth = 0
        macro_result = [
            True,
        ]
        macro_code_remove = True
        while line_num < len(code_lines):
            code = code_lines[line_num]
            line_num += 1

            # remove comment
            if "//" in code:
                code = code.split("//")[0]

            # macro parsing
            m = re.search(reMacroStart, code)
            if m is not None:
                macro, expression = m.groups()
                expression = expression.strip()
                if macro == 'define' or macro == 'undef':
                    define_expression = expression.split('(')[0].strip()
                    if ' ' in define_expression:
                        define_name, define_value = define_expression.split(
                            ' ', 1)
                    else:
                        define_name, define_value = define_expression, None

                    # check external macro
                    if macro == 'define' and define_name in external_macros:
                        continue  # ignore legacy macro

                    if macro == 'define' and define_name not in combined_macros:
                        combined_macros[define_name] = define_value
                    elif macro == 'undef' and define_name in combined_macros:
                        combined_macros.pop(define_name)
                elif macro == 'ifdef':
                    macro_depth += 1
                    if expression in combined_macros:
                        macro_result.append(True)
                    else:
                        macro_result.append(False)
                elif macro == 'ifndef':
                    macro_depth += 1
                    if expression not in combined_macros:
                        macro_result.append(True)
                    else:
                        macro_result.append(False)
                elif macro == 'if' or macro == 'elif' and not macro_result[
                        macro_depth]:
                    variables = re.findall(reVariable, expression)
                    variables.sort(key=lambda x: len(x), reverse=True)
                    for variable in variables:
                        if variable in combined_macros:
                            while True:
                                final_value = combined_macros[variable]
                                if final_value not in combined_macros:
                                    break
                                variable = final_value
                            expression = re.sub(reVariable, str(final_value),
                                                expression, 1)
                    expression = expression.replace('&&', ' and ')
                    expression = expression.replace('||', ' or ')
                    # expression = re.sub('\!?!\=', 'not ', expression)
                    # Important : To avoid errors, convert the undecalred variables to zero.
                    expression = re.sub(reVariable, '0', expression)
                    result = True if eval(expression) else False
                    if macro == 'if':
                        macro_depth += 1
                        macro_result.append(result)
                    elif macro == 'elif':
                        macro_result[macro_depth] = result
                elif macro == 'else':
                    macro_result[macro_depth] = not macro_result[macro_depth]
                elif macro == 'endif':
                    macro_depth -= 1
                    macro_result.pop()
            # be in failed macro block. continue
            elif not macro_result[macro_depth]:
                if not macro_code_remove:
                    # make comment
                    final_code_lines.append("// " + code)
                continue

            # is version code?
            m = re.search(reVersion, code)
            if m is not None:
                version_code = m.groups()[0].strip()
                if final_code_lines[
                        0] == "" or version_code > final_code_lines[0]:
                    final_code_lines[0] = version_code
                continue

            # find include block
            m = re.search(reInclude, code)
            if m is not None:
                is_include_file_exists = False
                include_file_in_engine = os.path.join(engine_shader_directory,
                                                      m.groups()[0])
                include_file_in_project = os.path.join(
                    project_shader_directory,
                    m.groups()[0])
                if is_engine_resource:
                    if os.path.exists(include_file_in_engine):
                        include_file = include_file_in_engine
                        is_include_file_exists = True
                    else:
                        include_file = include_file_in_project
                else:
                    if os.path.exists(include_file_in_project):
                        include_file = include_file_in_project
                        is_include_file_exists = True
                    else:
                        include_file = include_file_in_engine

                # insert include code
                valid = False
                if is_include_file_exists or os.path.exists(include_file):
                    try:
                        f = codecs.open(include_file,
                                        mode='r',
                                        encoding='utf-8')
                        include_source = f.read()
                        # remove comment block
                        include_source = re.sub(reComment, "", include_source)
                        include_code_lines = include_source.splitlines()
                        f.close()
                        valid = True
                    except BaseException:
                        logger.error(traceback.format_exc())

                    if valid:
                        if include_file in include_files:
                            unique_id = include_files[include_file]
                        else:
                            unique_id = "UUID_" + str(
                                uuid.uuid3(uuid.NAMESPACE_DNS,
                                           include_file)).replace("-", "_")
                            include_files[include_file] = unique_id

                            if include_file not in self.include_files:
                                self.include_files.append(include_file)
                        # insert included code
                        final_code_lines.append(
                            "//------------ INCLUDE -------------//")
                        final_code_lines.append("// " +
                                                code)  # include comment
                        include_code_lines.insert(0, "#ifndef %s" % unique_id)
                        include_code_lines.insert(1, "#define %s" % unique_id)
                        include_code_lines.append("#endif /* %s */" %
                                                  unique_id)
                        code_lines = include_code_lines + code_lines[line_num:]
                        line_num = 0

                if not valid:
                    logger.error(
                        "Shader parsing error.\n\t--> Cannot open %s file." %
                        include_file)
                continue
            # append code block
            final_code_lines.append(code)
        return '\n'.join(final_code_lines)
コード例 #8
0
ファイル: Atmosphere.py プロジェクト: idkwim/PyEngine3D
class Atmosphere:
    def __init__(self, **object_data):
        self.name = object_data.get('name', 'atmosphere')
        self.attributes = Attributes()
        self.is_render_atmosphere = object_data.get('is_render_atmosphere', True)
        self.use_constant_solar_spectrum = False
        self.use_ozone = True
        self.use_combined_textures = True
        self.luminance_type = Luminance.NONE
        self.num_precomputed_wavelengths = 15 if Luminance.PRECOMPUTED == self.luminance_type else 3
        self.do_white_balance = False
        self.show_help = True
        self.view_distance_meters = 9000.0
        self.view_zenith_angle_radians = 1.47
        self.view_azimuth_angle_radians = -0.1
        self.sun_zenith_angle_radians = 1.3
        self.sun_azimuth_angle_radians = 2.9
        self.sun_direction = Float3()

        self.white_point = Float3()
        self.earth_center = Float3(0.0, -kBottomRadius / kLengthUnitInMeters, 0.0)
        self.sun_size = Float2(math.tan(kSunAngularRadius), math.cos(kSunAngularRadius))

        self.kSky = Float3(1.0, 1.0, 1.0)
        self.kSun = Float3(1.0, 1.0, 1.0)

        self.atmosphere_exposure = 0.0001

        # cloud constants
        self.cloud_altitude = 100.0
        self.cloud_height = 500.0
        self.cloud_speed = 0.01
        self.cloud_absorption = 0.15

        self.cloud_contrast = 2.0
        self.cloud_coverage = 0.8
        self.cloud_tiling = 0.0004

        self.noise_contrast = 1.0
        self.noise_coverage = 1.0
        self.noise_tiling = 0.0003

        self.model = None
        self.atmosphere_material_instance = None

        self.transmittance_texture = None
        self.scattering_texture = None
        self.irradiance_texture = None
        self.optional_single_mie_scattering_texture = None

        self.cloud_texture = None
        self.noise_texture = None

        self.quad = ScreenQuad.get_vertex_array_buffer()

        self.load_data(object_data)

        self.initialize()

    def get_attribute(self):
        save_data = self.get_save_data()
        attribute_names = list(save_data.keys())
        attribute_names.sort()

        for attribute_name in attribute_names:
            self.attributes.set_attribute(attribute_name, save_data[attribute_name])

        return self.attributes

    def set_attribute(self, attribute_name, attribute_value, parent_info, attribute_index):
        if hasattr(self, attribute_name):
            setattr(self, attribute_name, attribute_value)

    def load_data(self, object_data):
        for key, value in object_data.items():
            if hasattr(self, key):
                setattr(self, key, value)

    def get_save_data(self):
        save_data = dict(
            is_render_atmosphere=self.is_render_atmosphere,
            atmosphere_exposure=self.atmosphere_exposure,
            cloud_altitude=self.cloud_altitude,
            cloud_height=self.cloud_height,
            cloud_tiling=self.cloud_tiling,
            cloud_speed=self.cloud_speed,
            cloud_contrast=self.cloud_contrast,
            cloud_coverage=self.cloud_coverage,
            cloud_absorption=self.cloud_absorption,
            noise_tiling=self.noise_tiling,
            noise_contrast=self.noise_contrast,
            noise_coverage=self.noise_coverage,
            sun_size=self.sun_size,
        )
        return save_data

    def initialize(self):
        resource_manager = CoreManager.instance().resource_manager

        self.atmosphere_material_instance = resource_manager.get_material_instance(
            'precomputed_atmosphere.atmosphere',
            macros={
                'USE_LUMINANCE': 1 if self.luminance_type else 0,
                'COMBINED_SCATTERING_TEXTURES': 1 if self.use_combined_textures else 0
            }
        )

        # precompute constants
        max_sun_zenith_angle = 120.0 / 180.0 * kPi

        rayleigh_layer = DensityProfileLayer(0.0, 1.0, -1.0 / kRayleighScaleHeight, 0.0, 0.0)
        mie_layer = DensityProfileLayer(0.0, 1.0, -1.0 / kMieScaleHeight, 0.0, 0.0)

        ozone_density = [DensityProfileLayer(25000.0, 0.0, 0.0, 1.0 / 15000.0, -2.0 / 3.0),
                         DensityProfileLayer(0.0, 0.0, 0.0, -1.0 / 15000.0, 8.0 / 3.0)]

        wavelengths = []
        solar_irradiance = []
        rayleigh_scattering = []
        mie_scattering = []
        mie_extinction = []
        absorption_extinction = []
        ground_albedo = []

        for i in range(kLambdaMin, kLambdaMax + 1, 10):
            L = float(i) * 1e-3  # micro-meters
            mie = kMieAngstromBeta / kMieScaleHeight * math.pow(L, -kMieAngstromAlpha)
            wavelengths.append(i)
            if self.use_constant_solar_spectrum:
                solar_irradiance.append(kConstantSolarIrradiance)
            else:
                solar_irradiance.append(kSolarIrradiance[int((i - kLambdaMin) / 10)])
            rayleigh_scattering.append(kRayleigh * math.pow(L, -4))
            mie_scattering.append(mie * kMieSingleScatteringAlbedo)
            mie_extinction.append(mie)
            if self.use_ozone:
                absorption_extinction.append(kMaxOzoneNumberDensity * kOzoneCrossSection[int((i - kLambdaMin) / 10)])
            else:
                absorption_extinction.append(0.0)
            ground_albedo.append(kGroundAlbedo)

        rayleigh_density = [rayleigh_layer, ]
        mie_density = [mie_layer, ]

        if Luminance.PRECOMPUTED == self.luminance_type:
            self.kSky[...] = [MAX_LUMINOUS_EFFICACY, MAX_LUMINOUS_EFFICACY, MAX_LUMINOUS_EFFICACY]
        else:
            self.kSky[...] = ComputeSpectralRadianceToLuminanceFactors(wavelengths, solar_irradiance, -3)
        self.kSun[...] = ComputeSpectralRadianceToLuminanceFactors(wavelengths, solar_irradiance, 0)

        # generate precomputed textures
        if not resource_manager.texture_loader.hasResource('precomputed_atmosphere.transmittance') or \
                not resource_manager.texture_loader.hasResource('precomputed_atmosphere.scattering') or \
                not resource_manager.texture_loader.hasResource('precomputed_atmosphere.irradiance') or \
                not resource_manager.texture_loader.hasResource(
                    'precomputed_atmosphere.optional_single_mie_scattering') and not self.use_combined_textures:
            model = Model(wavelengths,
                          solar_irradiance,
                          kSunAngularRadius,
                          kBottomRadius,
                          kTopRadius,
                          rayleigh_density,
                          rayleigh_scattering,
                          mie_density,
                          mie_scattering,
                          mie_extinction,
                          kMiePhaseFunctionG,
                          ozone_density,
                          absorption_extinction,
                          ground_albedo,
                          max_sun_zenith_angle,
                          kLengthUnitInMeters,
                          self.num_precomputed_wavelengths,
                          Luminance.PRECOMPUTED == self.luminance_type,
                          self.use_combined_textures)
            model.generate()

        self.transmittance_texture = resource_manager.get_texture('precomputed_atmosphere.transmittance')
        self.scattering_texture = resource_manager.get_texture('precomputed_atmosphere.scattering')
        self.irradiance_texture = resource_manager.get_texture('precomputed_atmosphere.irradiance')

        if not self.use_combined_textures:
            self.optional_single_mie_scattering_texture = resource_manager.get_texture(
                'precomputed_atmosphere.optional_single_mie_scattering')

        self.cloud_texture = resource_manager.get_texture('precomputed_atmosphere.cloud_3d')
        self.noise_texture = resource_manager.get_texture('precomputed_atmosphere.noise_3d')

    def update(self, main_light):
        if not self.is_render_atmosphere:
            return

        self.sun_direction[...] = main_light.transform.front

    def bind_precomputed_atmosphere(self, material_instance):
        material_instance.bind_uniform_data("transmittance_texture", self.transmittance_texture)
        material_instance.bind_uniform_data("scattering_texture", self.scattering_texture)
        material_instance.bind_uniform_data("irradiance_texture", self.irradiance_texture)

        if not self.use_combined_textures:
            material_instance.bind_uniform_data(
                "single_mie_scattering_texture", self.optional_single_mie_scattering_texture)

        material_instance.bind_uniform_data("SKY_RADIANCE_TO_LUMINANCE", self.kSky * self.atmosphere_exposure)
        material_instance.bind_uniform_data("SUN_RADIANCE_TO_LUMINANCE", self.kSun * self.atmosphere_exposure)

        material_instance.bind_uniform_data("atmosphere_exposure", self.atmosphere_exposure)
        material_instance.bind_uniform_data("earth_center", self.earth_center)

    def bind_cloud(self, material_instance):
        material_instance.bind_uniform_data("texture_cloud", self.cloud_texture)
        material_instance.bind_uniform_data("texture_noise", self.noise_texture)

        material_instance.bind_uniform_data('cloud_altitude', self.cloud_altitude)
        material_instance.bind_uniform_data('cloud_height', self.cloud_height)
        material_instance.bind_uniform_data('cloud_speed', self.cloud_speed)
        material_instance.bind_uniform_data('cloud_absorption', self.cloud_absorption)

        material_instance.bind_uniform_data('cloud_tiling', self.cloud_tiling)
        material_instance.bind_uniform_data('cloud_contrast', self.cloud_contrast)
        material_instance.bind_uniform_data('cloud_coverage', self.cloud_coverage)

        material_instance.bind_uniform_data('noise_tiling', self.noise_tiling)
        material_instance.bind_uniform_data('noise_contrast', self.noise_contrast)
        material_instance.bind_uniform_data('noise_coverage', self.noise_coverage)

    def render_precomputed_atmosphere(self, texture_linear_depth, texture_shadow, render_light_probe_mode):
        if not self.is_render_atmosphere:
            return

        self.atmosphere_material_instance.use_program()
        self.atmosphere_material_instance.bind_material_instance()
        self.atmosphere_material_instance.bind_uniform_data("texture_linear_depth", texture_linear_depth)
        self.atmosphere_material_instance.bind_uniform_data("texture_shadow", texture_shadow)
        self.atmosphere_material_instance.bind_uniform_data("sun_size", self.sun_size)
        self.atmosphere_material_instance.bind_uniform_data("render_light_probe_mode", render_light_probe_mode)
        self.bind_precomputed_atmosphere(self.atmosphere_material_instance)
        self.bind_cloud(self.atmosphere_material_instance)
        self.quad.draw_elements()