Team Ai
Apppublic

OpenMotionLab/MotionGPT

sourceHugging Facemitupdated 1y agoView on Hugging Face
118likes
renderer.py1340 linesDownload Raw Back to pyrender
1"""PBR renderer for Python.2 3Author: Matthew Matl4"""5import sys6 7import numpy as np8import PIL9 10from .constants import (RenderFlags, TextAlign, GLTF, BufFlags, TexFlags,11                        ProgramFlags, DEFAULT_Z_FAR, DEFAULT_Z_NEAR,12                        SHADOW_TEX_SZ, MAX_N_LIGHTS)13from .shader_program import ShaderProgramCache14from .material import MetallicRoughnessMaterial, SpecularGlossinessMaterial15from .light import PointLight, SpotLight, DirectionalLight16from .font import FontCache17from .utils import format_color_vector18 19from OpenGL.GL import *20 21 22class Renderer(object):23    """Class for handling all rendering operations on a scene.24 25    Note26    ----27    This renderer relies on the existence of an OpenGL context and28    does not create one on its own.29 30    Parameters31    ----------32    viewport_width : int33        Width of the viewport in pixels.34    viewport_height : int35        Width of the viewport height in pixels.36    point_size : float, optional37        Size of points in pixels. Defaults to 1.0.38    """39 40    def __init__(self, viewport_width, viewport_height, point_size=1.0):41        self.dpscale = 142        # Scaling needed on retina displays43        if sys.platform == 'darwin':44            self.dpscale = 245 46        self.viewport_width = viewport_width47        self.viewport_height = viewport_height48        self.point_size = point_size49 50        # Optional framebuffer for offscreen renders51        self._main_fb = None52        self._main_cb = None53        self._main_db = None54        self._main_fb_ms = None55        self._main_cb_ms = None56        self._main_db_ms = None57        self._main_fb_dims = (None, None)58        self._shadow_fb = None59        self._latest_znear = DEFAULT_Z_NEAR60        self._latest_zfar = DEFAULT_Z_FAR61 62        # Shader Program Cache63        self._program_cache = ShaderProgramCache()64        self._font_cache = FontCache()65        self._meshes = set()66        self._mesh_textures = set()67        self._shadow_textures = set()68        self._texture_alloc_idx = 069 70    @property71    def viewport_width(self):72        """int : The width of the main viewport, in pixels.73        """74        return self._viewport_width75 76    @viewport_width.setter77    def viewport_width(self, value):78        self._viewport_width = self.dpscale * value79 80    @property81    def viewport_height(self):82        """int : The height of the main viewport, in pixels.83        """84        return self._viewport_height85 86    @viewport_height.setter87    def viewport_height(self, value):88        self._viewport_height = self.dpscale * value89 90    @property91    def point_size(self):92        """float : The size of screen-space points, in pixels.93        """94        return self._point_size95 96    @point_size.setter97    def point_size(self, value):98        self._point_size = float(value)99 100    def render(self, scene, flags, seg_node_map=None):101        """Render a scene with the given set of flags.102 103        Parameters104        ----------105        scene : :class:`Scene`106            A scene to render.107        flags : int108            A specification from :class:`.RenderFlags`.109        seg_node_map : dict110            A map from :class:`.Node` objects to (3,) colors for each.111            If specified along with flags set to :attr:`.RenderFlags.SEG`,112            the color image will be a segmentation image.113 114        Returns115        -------116        color_im : (h, w, 3) uint8 or (h, w, 4) uint8117            If :attr:`RenderFlags.OFFSCREEN` is set, the color buffer. This is118            normally an RGB buffer, but if :attr:`.RenderFlags.RGBA` is set,119            the buffer will be a full RGBA buffer.120        depth_im : (h, w) float32121            If :attr:`RenderFlags.OFFSCREEN` is set, the depth buffer122            in linear units.123        """124        # Update context with meshes and textures125        self._update_context(scene, flags)126 127        # Render necessary shadow maps128        if not bool(flags & RenderFlags.DEPTH_ONLY or flags & RenderFlags.SEG):129            for ln in scene.light_nodes:130                take_pass = False131                if (isinstance(ln.light, DirectionalLight) and132                        bool(flags & RenderFlags.SHADOWS_DIRECTIONAL)):133                    take_pass = True134                elif (isinstance(ln.light, SpotLight) and135                        bool(flags & RenderFlags.SHADOWS_SPOT)):136                    take_pass = True137                elif (isinstance(ln.light, PointLight) and138                        bool(flags & RenderFlags.SHADOWS_POINT)):139                    take_pass = True140                if take_pass:141                    self._shadow_mapping_pass(scene, ln, flags)142 143        # Make forward pass144        retval = self._forward_pass(scene, flags, seg_node_map=seg_node_map)145 146        # If necessary, make normals pass147        if flags & (RenderFlags.VERTEX_NORMALS | RenderFlags.FACE_NORMALS):148            self._normals_pass(scene, flags)149 150        # Update camera settings for retrieving depth buffers151        self._latest_znear = scene.main_camera_node.camera.znear152        self._latest_zfar = scene.main_camera_node.camera.zfar153 154        return retval155 156    def render_text(self, text, x, y, font_name='OpenSans-Regular',157                    font_pt=40, color=None, scale=1.0,158                    align=TextAlign.BOTTOM_LEFT):159        """Render text into the current viewport.160 161        Note162        ----163        This cannot be done into an offscreen buffer.164 165        Parameters166        ----------167        text : str168            The text to render.169        x : int170            Horizontal pixel location of text.171        y : int172            Vertical pixel location of text.173        font_name : str174            Name of font, from the ``pyrender/fonts`` folder, or175            a path to a ``.ttf`` file.176        font_pt : int177            Height of the text, in font points.178        color : (4,) float179            The color of the text. Default is black.180        scale : int181            Scaling factor for text.182        align : int183            One of the :class:`TextAlign` options which specifies where the184            ``x`` and ``y`` parameters lie on the text. For example,185            :attr:`TextAlign.BOTTOM_LEFT` means that ``x`` and ``y`` indicate186            the position of the bottom-left corner of the textbox.187        """188        x *= self.dpscale189        y *= self.dpscale190        font_pt *= self.dpscale191 192        if color is None:193            color = np.array([0.0, 0.0, 0.0, 1.0])194        else:195            color = format_color_vector(color, 4)196 197        # Set up viewport for render198        self._configure_forward_pass_viewport(0)199 200        # Load font201        font = self._font_cache.get_font(font_name, font_pt)202        if not font._in_context():203            font._add_to_context()204 205        # Load program206        program = self._get_text_program()207        program._bind()208 209        # Set uniforms210        p = np.eye(4)211        p[0,0] = 2.0 / self.viewport_width212        p[0,3] = -1.0213        p[1,1] = 2.0 / self.viewport_height214        p[1,3] = -1.0215        program.set_uniform('projection', p)216        program.set_uniform('text_color', color)217 218        # Draw text219        font.render_string(text, x, y, scale, align)220 221    def read_color_buf(self):222        """Read and return the current viewport's color buffer.223 224        Alpha cannot be computed for an on-screen buffer.225 226        Returns227        -------228        color_im : (h, w, 3) uint8229            The color buffer in RGB byte format.230        """231        # Extract color image from frame buffer232        width, height = self.viewport_width, self.viewport_height233        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)234        glReadBuffer(GL_FRONT)235        color_buf = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)236 237        # Re-format them into numpy arrays238        color_im = np.frombuffer(color_buf, dtype=np.uint8)239        color_im = color_im.reshape((height, width, 3))240        color_im = np.flip(color_im, axis=0)241 242        # Resize for macos if needed243        if sys.platform == 'darwin':244            color_im = self._resize_image(color_im, True)245 246        return color_im247 248    def read_depth_buf(self):249        """Read and return the current viewport's color buffer.250 251        Returns252        -------253        depth_im : (h, w) float32254            The depth buffer in linear units.255        """256        width, height = self.viewport_width, self.viewport_height257        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)258        glReadBuffer(GL_FRONT)259        depth_buf = glReadPixels(260            0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT261        )262 263        depth_im = np.frombuffer(depth_buf, dtype=np.float32)264        depth_im = depth_im.reshape((height, width))265        depth_im = np.flip(depth_im, axis=0)266 267        inf_inds = (depth_im == 1.0)268        depth_im = 2.0 * depth_im - 1.0269        z_near, z_far = self._latest_znear, self._latest_zfar270        noninf = np.logical_not(inf_inds)271        if z_far is None:272            depth_im[noninf] = 2 * z_near / (1.0 - depth_im[noninf])273        else:274            depth_im[noninf] = ((2.0 * z_near * z_far) /275                                (z_far + z_near - depth_im[noninf] *276                                (z_far - z_near)))277        depth_im[inf_inds] = 0.0278 279        # Resize for macos if needed280        if sys.platform == 'darwin':281            depth_im = self._resize_image(depth_im)282 283        return depth_im284 285    def delete(self):286        """Free all allocated OpenGL resources.287        """288        # Free shaders289        self._program_cache.clear()290 291        # Free fonts292        self._font_cache.clear()293 294        # Free meshes295        for mesh in self._meshes:296            for p in mesh.primitives:297                p.delete()298 299        # Free textures300        for mesh_texture in self._mesh_textures:301            mesh_texture.delete()302 303        for shadow_texture in self._shadow_textures:304            shadow_texture.delete()305 306        self._meshes = set()307        self._mesh_textures = set()308        self._shadow_textures = set()309        self._texture_alloc_idx = 0310 311        self._delete_main_framebuffer()312        self._delete_shadow_framebuffer()313 314    def __del__(self):315        try:316            self.delete()317        except Exception:318            pass319 320    ###########################################################################321    # Rendering passes322    ###########################################################################323 324    def _forward_pass(self, scene, flags, seg_node_map=None):325        # Set up viewport for render326        self._configure_forward_pass_viewport(flags)327 328        # Clear it329        if bool(flags & RenderFlags.SEG):330            glClearColor(0.0, 0.0, 0.0, 1.0)331            if seg_node_map is None:332                seg_node_map = {}333        else:334            glClearColor(*scene.bg_color)335 336        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)337 338        if not bool(flags & RenderFlags.SEG):339            glEnable(GL_MULTISAMPLE)340        else:341            glDisable(GL_MULTISAMPLE)342 343        # Set up camera matrices344        V, P = self._get_camera_matrices(scene)345 346        program = None347        # Now, render each object in sorted order348        for node in self._sorted_mesh_nodes(scene):349            mesh = node.mesh350 351            # Skip the mesh if it's not visible352            if not mesh.is_visible:353                continue354 355            # If SEG, set color356            if bool(flags & RenderFlags.SEG):357                if node not in seg_node_map:358                    continue359                color = seg_node_map[node]360                if not isinstance(color, (list, tuple, np.ndarray)):361                    color = np.repeat(color, 3)362                else:363                    color = np.asanyarray(color)364                color = color / 255.0365 366            for primitive in mesh.primitives:367 368                # First, get and bind the appropriate program369                program = self._get_primitive_program(370                    primitive, flags, ProgramFlags.USE_MATERIAL371                )372                program._bind()373 374                # Set the camera uniforms375                program.set_uniform('V', V)376                program.set_uniform('P', P)377                program.set_uniform(378                    'cam_pos', scene.get_pose(scene.main_camera_node)[:3,3]379                )380                if bool(flags & RenderFlags.SEG):381                    program.set_uniform('color', color)382 383                # Next, bind the lighting384                if not (flags & RenderFlags.DEPTH_ONLY or flags & RenderFlags.FLAT or385                        flags & RenderFlags.SEG):386                    self._bind_lighting(scene, program, node, flags)387 388                # Finally, bind and draw the primitive389                self._bind_and_draw_primitive(390                    primitive=primitive,391                    pose=scene.get_pose(node),392                    program=program,393                    flags=flags394                )395                self._reset_active_textures()396 397        # Unbind the shader and flush the output398        if program is not None:399            program._unbind()400        glFlush()401 402        # If doing offscreen render, copy result from framebuffer and return403        if flags & RenderFlags.OFFSCREEN:404            return self._read_main_framebuffer(scene, flags)405        else:406            return407 408    def _shadow_mapping_pass(self, scene, light_node, flags):409        light = light_node.light410 411        # Set up viewport for render412        self._configure_shadow_mapping_viewport(light, flags)413 414        # Set up camera matrices415        V, P = self._get_light_cam_matrices(scene, light_node, flags)416 417        # Now, render each object in sorted order418        for node in self._sorted_mesh_nodes(scene):419            mesh = node.mesh420 421            # Skip the mesh if it's not visible422            if not mesh.is_visible:423                continue424 425            for primitive in mesh.primitives:426 427                # First, get and bind the appropriate program428                program = self._get_primitive_program(429                    primitive, flags, ProgramFlags.NONE430                )431                program._bind()432 433                # Set the camera uniforms434                program.set_uniform('V', V)435                program.set_uniform('P', P)436                program.set_uniform(437                    'cam_pos', scene.get_pose(scene.main_camera_node)[:3,3]438                )439 440                # Finally, bind and draw the primitive441                self._bind_and_draw_primitive(442                    primitive=primitive,443                    pose=scene.get_pose(node),444                    program=program,445                    flags=RenderFlags.DEPTH_ONLY446                )447                self._reset_active_textures()448 449        # Unbind the shader and flush the output450        if program is not None:451            program._unbind()452        glFlush()453 454    def _normals_pass(self, scene, flags):455        # Set up viewport for render456        self._configure_forward_pass_viewport(flags)457        program = None458 459        # Set up camera matrices460        V, P = self._get_camera_matrices(scene)461 462        # Now, render each object in sorted order463        for node in self._sorted_mesh_nodes(scene):464            mesh = node.mesh465 466            # Skip the mesh if it's not visible467            if not mesh.is_visible:468                continue469 470            for primitive in mesh.primitives:471 472                # Skip objects that don't have normals473                if not primitive.buf_flags & BufFlags.NORMAL:474                    continue475 476                # First, get and bind the appropriate program477                pf = ProgramFlags.NONE478                if flags & RenderFlags.VERTEX_NORMALS:479                    pf = pf | ProgramFlags.VERTEX_NORMALS480                if flags & RenderFlags.FACE_NORMALS:481                    pf = pf | ProgramFlags.FACE_NORMALS482                program = self._get_primitive_program(primitive, flags, pf)483                program._bind()484 485                # Set the camera uniforms486                program.set_uniform('V', V)487                program.set_uniform('P', P)488                program.set_uniform('normal_magnitude', 0.05 * primitive.scale)489                program.set_uniform(490                    'normal_color', np.array([0.1, 0.1, 1.0, 1.0])491                )492 493                # Finally, bind and draw the primitive494                self._bind_and_draw_primitive(495                    primitive=primitive,496                    pose=scene.get_pose(node),497                    program=program,498                    flags=RenderFlags.DEPTH_ONLY499                )500                self._reset_active_textures()501 502        # Unbind the shader and flush the output503        if program is not None:504            program._unbind()505        glFlush()506 507    ###########################################################################508    # Handlers for binding uniforms and drawing primitives509    ###########################################################################510 511    def _bind_and_draw_primitive(self, primitive, pose, program, flags):512        # Set model pose matrix513        program.set_uniform('M', pose)514 515        # Bind mesh buffers516        primitive._bind()517 518        # Bind mesh material519        if not (flags & RenderFlags.DEPTH_ONLY or flags & RenderFlags.SEG):520            material = primitive.material521 522            # Bind textures523            tf = material.tex_flags524            if tf & TexFlags.NORMAL:525                self._bind_texture(material.normalTexture,526                                   'material.normal_texture', program)527            if tf & TexFlags.OCCLUSION:528                self._bind_texture(material.occlusionTexture,529                                   'material.occlusion_texture', program)530            if tf & TexFlags.EMISSIVE:531                self._bind_texture(material.emissiveTexture,532                                   'material.emissive_texture', program)533            if tf & TexFlags.BASE_COLOR:534                self._bind_texture(material.baseColorTexture,535                                   'material.base_color_texture', program)536            if tf & TexFlags.METALLIC_ROUGHNESS:537                self._bind_texture(material.metallicRoughnessTexture,538                                   'material.metallic_roughness_texture',539                                   program)540            if tf & TexFlags.DIFFUSE:541                self._bind_texture(material.diffuseTexture,542                                   'material.diffuse_texture', program)543            if tf & TexFlags.SPECULAR_GLOSSINESS:544                self._bind_texture(material.specularGlossinessTexture,545                                   'material.specular_glossiness_texture',546                                   program)547 548            # Bind other uniforms549            b = 'material.{}'550            program.set_uniform(b.format('emissive_factor'),551                                material.emissiveFactor)552            if isinstance(material, MetallicRoughnessMaterial):553                program.set_uniform(b.format('base_color_factor'),554                                    material.baseColorFactor)555                program.set_uniform(b.format('metallic_factor'),556                                    material.metallicFactor)557                program.set_uniform(b.format('roughness_factor'),558                                    material.roughnessFactor)559            elif isinstance(material, SpecularGlossinessMaterial):560                program.set_uniform(b.format('diffuse_factor'),561                                    material.diffuseFactor)562                program.set_uniform(b.format('specular_factor'),563                                    material.specularFactor)564                program.set_uniform(b.format('glossiness_factor'),565                                    material.glossinessFactor)566 567            # Set blending options568            if material.alphaMode == 'BLEND':569                glEnable(GL_BLEND)570                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)571            else:572                glEnable(GL_BLEND)573                glBlendFunc(GL_ONE, GL_ZERO)574 575            # Set wireframe mode576            wf = material.wireframe577            if flags & RenderFlags.FLIP_WIREFRAME:578                wf = not wf579            if (flags & RenderFlags.ALL_WIREFRAME) or wf:580                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)581            else:582                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)583 584            # Set culling mode585            if material.doubleSided or flags & RenderFlags.SKIP_CULL_FACES:586                glDisable(GL_CULL_FACE)587            else:588                glEnable(GL_CULL_FACE)589                glCullFace(GL_BACK)590        else:591            glEnable(GL_CULL_FACE)592            glEnable(GL_BLEND)593            glCullFace(GL_BACK)594            glBlendFunc(GL_ONE, GL_ZERO)595            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)596 597        # Set point size if needed598        glDisable(GL_PROGRAM_POINT_SIZE)599        if primitive.mode == GLTF.POINTS:600            glEnable(GL_PROGRAM_POINT_SIZE)601            glPointSize(self.point_size)602 603        # Render mesh604        n_instances = 1605        if primitive.poses is not None:606            n_instances = len(primitive.poses)607 608        if primitive.indices is not None:609            glDrawElementsInstanced(610                primitive.mode, primitive.indices.size, GL_UNSIGNED_INT,611                ctypes.c_void_p(0), n_instances612            )613        else:614            glDrawArraysInstanced(615                primitive.mode, 0, len(primitive.positions), n_instances616            )617 618        # Unbind mesh buffers619        primitive._unbind()620 621    def _bind_lighting(self, scene, program, node, flags):622        """Bind all lighting uniform values for a scene.623        """624        max_n_lights = self._compute_max_n_lights(flags)625 626        n_d = min(len(scene.directional_light_nodes), max_n_lights[0])627        n_s = min(len(scene.spot_light_nodes), max_n_lights[1])628        n_p = min(len(scene.point_light_nodes), max_n_lights[2])629        program.set_uniform('ambient_light', scene.ambient_light)630        program.set_uniform('n_directional_lights', n_d)631        program.set_uniform('n_spot_lights', n_s)632        program.set_uniform('n_point_lights', n_p)633        plc = 0634        slc = 0635        dlc = 0636 637        light_nodes = scene.light_nodes638        if (len(scene.directional_light_nodes) > max_n_lights[0] or639                len(scene.spot_light_nodes) > max_n_lights[1] or640                len(scene.point_light_nodes) > max_n_lights[2]):641            light_nodes = self._sorted_nodes_by_distance(642                scene, scene.light_nodes, node643            )644 645        for n in light_nodes:646            light = n.light647            pose = scene.get_pose(n)648            position = pose[:3,3]649            direction = -pose[:3,2]650 651            if isinstance(light, PointLight):652                if plc == max_n_lights[2]:653                    continue654                b = 'point_lights[{}].'.format(plc)655                plc += 1656                shadow = bool(flags & RenderFlags.SHADOWS_POINT)657                program.set_uniform(b + 'position', position)658            elif isinstance(light, SpotLight):659                if slc == max_n_lights[1]:660                    continue661                b = 'spot_lights[{}].'.format(slc)662                slc += 1663                shadow = bool(flags & RenderFlags.SHADOWS_SPOT)664                las = 1.0 / max(0.001, np.cos(light.innerConeAngle) -665                                np.cos(light.outerConeAngle))666                lao = -np.cos(light.outerConeAngle) * las667                program.set_uniform(b + 'direction', direction)668                program.set_uniform(b + 'position', position)669                program.set_uniform(b + 'light_angle_scale', las)670                program.set_uniform(b + 'light_angle_offset', lao)671            else:672                if dlc == max_n_lights[0]:673                    continue674                b = 'directional_lights[{}].'.format(dlc)675                dlc += 1676                shadow = bool(flags & RenderFlags.SHADOWS_DIRECTIONAL)677                program.set_uniform(b + 'direction', direction)678 679            program.set_uniform(b + 'color', light.color)680            program.set_uniform(b + 'intensity', light.intensity)681            # if light.range is not None:682            #     program.set_uniform(b + 'range', light.range)683            # else:684            #     program.set_uniform(b + 'range', 0)685 686            if shadow:687                self._bind_texture(light.shadow_texture,688                                   b + 'shadow_map', program)689                if not isinstance(light, PointLight):690                    V, P = self._get_light_cam_matrices(scene, n, flags)691                    program.set_uniform(b + 'light_matrix', P.dot(V))692                else:693                    raise NotImplementedError(694                        'Point light shadows not implemented'695                    )696 697    def _sorted_mesh_nodes(self, scene):698        cam_loc = scene.get_pose(scene.main_camera_node)[:3,3]699        solid_nodes = []700        trans_nodes = []701        for node in scene.mesh_nodes:702            mesh = node.mesh703            if mesh.is_transparent:704                trans_nodes.append(node)705            else:706                solid_nodes.append(node)707 708        # TODO BETTER SORTING METHOD709        trans_nodes.sort(710            key=lambda n: -np.linalg.norm(scene.get_pose(n)[:3,3] - cam_loc)711        )712        solid_nodes.sort(713            key=lambda n: -np.linalg.norm(scene.get_pose(n)[:3,3] - cam_loc)714        )715 716        return solid_nodes + trans_nodes717 718    def _sorted_nodes_by_distance(self, scene, nodes, compare_node):719        nodes = list(nodes)720        compare_posn = scene.get_pose(compare_node)[:3,3]721        nodes.sort(key=lambda n: np.linalg.norm(722            scene.get_pose(n)[:3,3] - compare_posn)723        )724        return nodes725 726    ###########################################################################727    # Context Management728    ###########################################################################729 730    def _update_context(self, scene, flags):731 732        # Update meshes733        scene_meshes = scene.meshes734 735        # Add new meshes to context736        for mesh in scene_meshes - self._meshes:737            for p in mesh.primitives:738                p._add_to_context()739 740        # Remove old meshes from context741        for mesh in self._meshes - scene_meshes:742            for p in mesh.primitives:743                p.delete()744 745        self._meshes = scene_meshes.copy()746 747        # Update mesh textures748        mesh_textures = set()749        for m in scene_meshes:750            for p in m.primitives:751                mesh_textures |= p.material.textures752 753        # Add new textures to context754        for texture in mesh_textures - self._mesh_textures:755            texture._add_to_context()756 757        # Remove old textures from context758        for texture in self._mesh_textures - mesh_textures:759            texture.delete()760 761        self._mesh_textures = mesh_textures.copy()762 763        shadow_textures = set()764        for l in scene.lights:765            # Create if needed766            active = False767            if (isinstance(l, DirectionalLight) and768                    flags & RenderFlags.SHADOWS_DIRECTIONAL):769                active = True770            elif (isinstance(l, PointLight) and771                    flags & RenderFlags.SHADOWS_POINT):772                active = True773            elif isinstance(l, SpotLight) and flags & RenderFlags.SHADOWS_SPOT:774                active = True775 776            if active and l.shadow_texture is None:777                l._generate_shadow_texture()778            if l.shadow_texture is not None:779                shadow_textures.add(l.shadow_texture)780 781        # Add new textures to context782        for texture in shadow_textures - self._shadow_textures:783            texture._add_to_context()784 785        # Remove old textures from context786        for texture in self._shadow_textures - shadow_textures:787            texture.delete()788 789        self._shadow_textures = shadow_textures.copy()790 791    ###########################################################################792    # Texture Management793    ###########################################################################794 795    def _bind_texture(self, texture, uniform_name, program):796        """Bind a texture to an active texture unit and return797        the texture unit index that was used.798        """799        tex_id = self._get_next_active_texture()800        glActiveTexture(GL_TEXTURE0 + tex_id)801        texture._bind()802        program.set_uniform(uniform_name, tex_id)803 804    def _get_next_active_texture(self):805        val = self._texture_alloc_idx806        self._texture_alloc_idx += 1807        return val808 809    def _reset_active_textures(self):810        self._texture_alloc_idx = 0811 812    ###########################################################################813    # Camera Matrix Management814    ###########################################################################815 816    def _get_camera_matrices(self, scene):817        main_camera_node = scene.main_camera_node818        if main_camera_node is None:819            raise ValueError('Cannot render scene without a camera')820        P = main_camera_node.camera.get_projection_matrix(821            width=self.viewport_width, height=self.viewport_height822        )823        pose = scene.get_pose(main_camera_node)824        V = np.linalg.inv(pose)  # V maps from world to camera825        return V, P826 827    def _get_light_cam_matrices(self, scene, light_node, flags):828        light = light_node.light829        pose = scene.get_pose(light_node).copy()830        s = scene.scale831        camera = light._get_shadow_camera(s)832        P = camera.get_projection_matrix()833        if isinstance(light, DirectionalLight):834            direction = -pose[:3,2]835            c = scene.centroid836            loc = c - direction * s837            pose[:3,3] = loc838        V = np.linalg.inv(pose)  # V maps from world to camera839        return V, P840 841    ###########################################################################842    # Shader Program Management843    ###########################################################################844 845    def _get_text_program(self):846        program = self._program_cache.get_program(847            vertex_shader='text.vert',848            fragment_shader='text.frag'849        )850 851        if not program._in_context():852            program._add_to_context()853 854        return program855 856    def _compute_max_n_lights(self, flags):857        max_n_lights = [MAX_N_LIGHTS, MAX_N_LIGHTS, MAX_N_LIGHTS]858        n_tex_units = glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS)859 860        # Reserved texture units: 6861        #   Normal Map862        #   Occlusion Map863        #   Emissive Map864        #   Base Color or Diffuse Map865        #   MR or SG Map866        #   Environment cubemap867 868        n_reserved_textures = 6869        n_available_textures = n_tex_units - n_reserved_textures870 871        # Distribute textures evenly among lights with shadows, with872        # a preference for directional lights873        n_shadow_types = 0874        if flags & RenderFlags.SHADOWS_DIRECTIONAL:875            n_shadow_types += 1876        if flags & RenderFlags.SHADOWS_SPOT:877            n_shadow_types += 1878        if flags & RenderFlags.SHADOWS_POINT:879            n_shadow_types += 1880 881        if n_shadow_types > 0:882            tex_per_light = n_available_textures // n_shadow_types883 884            if flags & RenderFlags.SHADOWS_DIRECTIONAL:885                max_n_lights[0] = (886                    tex_per_light +887                    (n_available_textures - tex_per_light * n_shadow_types)888                )889            if flags & RenderFlags.SHADOWS_SPOT:890                max_n_lights[1] = tex_per_light891            if flags & RenderFlags.SHADOWS_POINT:892                max_n_lights[2] = tex_per_light893 894        return max_n_lights895 896    def _get_primitive_program(self, primitive, flags, program_flags):897        vertex_shader = None898        fragment_shader = None899        geometry_shader = None900        defines = {}901 902        if (bool(program_flags & ProgramFlags.USE_MATERIAL) and903                not flags & RenderFlags.DEPTH_ONLY and904                not flags & RenderFlags.FLAT and905                not flags & RenderFlags.SEG):906            vertex_shader = 'mesh.vert'907            fragment_shader = 'mesh.frag'908        elif bool(program_flags & (ProgramFlags.VERTEX_NORMALS |909                                   ProgramFlags.FACE_NORMALS)):910            vertex_shader = 'vertex_normals.vert'911            if primitive.mode == GLTF.POINTS:912                geometry_shader = 'vertex_normals_pc.geom'913            else:914                geometry_shader = 'vertex_normals.geom'915            fragment_shader = 'vertex_normals.frag'916        elif flags & RenderFlags.FLAT:917            vertex_shader = 'flat.vert'918            fragment_shader = 'flat.frag'919        elif flags & RenderFlags.SEG:920            vertex_shader = 'segmentation.vert'921            fragment_shader = 'segmentation.frag'922        else:923            vertex_shader = 'mesh_depth.vert'924            fragment_shader = 'mesh_depth.frag'925 926        # Set up vertex buffer DEFINES927        bf = primitive.buf_flags928        buf_idx = 1929        if bf & BufFlags.NORMAL:930            defines['NORMAL_LOC'] = buf_idx931            buf_idx += 1932        if bf & BufFlags.TANGENT:933            defines['TANGENT_LOC'] = buf_idx934            buf_idx += 1935        if bf & BufFlags.TEXCOORD_0:936            defines['TEXCOORD_0_LOC'] = buf_idx937            buf_idx += 1938        if bf & BufFlags.TEXCOORD_1:939            defines['TEXCOORD_1_LOC'] = buf_idx940            buf_idx += 1941        if bf & BufFlags.COLOR_0:942            defines['COLOR_0_LOC'] = buf_idx943            buf_idx += 1944        if bf & BufFlags.JOINTS_0:945            defines['JOINTS_0_LOC'] = buf_idx946            buf_idx += 1947        if bf & BufFlags.WEIGHTS_0:948            defines['WEIGHTS_0_LOC'] = buf_idx949            buf_idx += 1950        defines['INST_M_LOC'] = buf_idx951 952        # Set up shadow mapping defines953        if flags & RenderFlags.SHADOWS_DIRECTIONAL:954            defines['DIRECTIONAL_LIGHT_SHADOWS'] = 1955        if flags & RenderFlags.SHADOWS_SPOT:956            defines['SPOT_LIGHT_SHADOWS'] = 1957        if flags & RenderFlags.SHADOWS_POINT:958            defines['POINT_LIGHT_SHADOWS'] = 1959        max_n_lights = self._compute_max_n_lights(flags)960        defines['MAX_DIRECTIONAL_LIGHTS'] = max_n_lights[0]961        defines['MAX_SPOT_LIGHTS'] = max_n_lights[1]962        defines['MAX_POINT_LIGHTS'] = max_n_lights[2]963 964        # Set up vertex normal defines965        if program_flags & ProgramFlags.VERTEX_NORMALS:966            defines['VERTEX_NORMALS'] = 1967        if program_flags & ProgramFlags.FACE_NORMALS:968            defines['FACE_NORMALS'] = 1969 970        # Set up material texture defines971        if bool(program_flags & ProgramFlags.USE_MATERIAL):972            tf = primitive.material.tex_flags973            if tf & TexFlags.NORMAL:974                defines['HAS_NORMAL_TEX'] = 1975            if tf & TexFlags.OCCLUSION:976                defines['HAS_OCCLUSION_TEX'] = 1977            if tf & TexFlags.EMISSIVE:978                defines['HAS_EMISSIVE_TEX'] = 1979            if tf & TexFlags.BASE_COLOR:980                defines['HAS_BASE_COLOR_TEX'] = 1981            if tf & TexFlags.METALLIC_ROUGHNESS:982                defines['HAS_METALLIC_ROUGHNESS_TEX'] = 1983            if tf & TexFlags.DIFFUSE:984                defines['HAS_DIFFUSE_TEX'] = 1985            if tf & TexFlags.SPECULAR_GLOSSINESS:986                defines['HAS_SPECULAR_GLOSSINESS_TEX'] = 1987            if isinstance(primitive.material, MetallicRoughnessMaterial):988                defines['USE_METALLIC_MATERIAL'] = 1989            elif isinstance(primitive.material, SpecularGlossinessMaterial):990                defines['USE_GLOSSY_MATERIAL'] = 1991 992        program = self._program_cache.get_program(993            vertex_shader=vertex_shader,994            fragment_shader=fragment_shader,995            geometry_shader=geometry_shader,996            defines=defines997        )998 999        if not program._in_context():1000            program._add_to_context()1001 1002        return program1003 1004    ###########################################################################1005    # Viewport Management1006    ###########################################################################1007 1008    def _configure_forward_pass_viewport(self, flags):1009 1010        # If using offscreen render, bind main framebuffer1011        if flags & RenderFlags.OFFSCREEN:1012            self._configure_main_framebuffer()1013            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self._main_fb_ms)1014        else:1015            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)1016 1017        glViewport(0, 0, self.viewport_width, self.viewport_height)1018        glEnable(GL_DEPTH_TEST)1019        glDepthMask(GL_TRUE)1020        glDepthFunc(GL_LESS)1021        glDepthRange(0.0, 1.0)1022 1023    def _configure_shadow_mapping_viewport(self, light, flags):1024        self._configure_shadow_framebuffer()1025        glBindFramebuffer(GL_FRAMEBUFFER, self._shadow_fb)1026        light.shadow_texture._bind()1027        light.shadow_texture._bind_as_depth_attachment()1028        glActiveTexture(GL_TEXTURE0)1029        light.shadow_texture._bind()1030        glDrawBuffer(GL_NONE)1031        glReadBuffer(GL_NONE)1032 1033        glClear(GL_DEPTH_BUFFER_BIT)1034        glViewport(0, 0, SHADOW_TEX_SZ, SHADOW_TEX_SZ)1035        glEnable(GL_DEPTH_TEST)1036        glDepthMask(GL_TRUE)1037        glDepthFunc(GL_LESS)1038        glDepthRange(0.0, 1.0)1039        glDisable(GL_CULL_FACE)1040        glDisable(GL_BLEND)1041 1042    ###########################################################################1043    # Framebuffer Management1044    ###########################################################################1045 1046    def _configure_shadow_framebuffer(self):1047        if self._shadow_fb is None:1048            self._shadow_fb = glGenFramebuffers(1)1049 1050    def _delete_shadow_framebuffer(self):1051        if self._shadow_fb is not None:1052            glDeleteFramebuffers(1, [self._shadow_fb])1053 1054    def _configure_main_framebuffer(self):1055        # If mismatch with prior framebuffer, delete it1056        if (self._main_fb is not None and1057                self.viewport_width != self._main_fb_dims[0] or1058                self.viewport_height != self._main_fb_dims[1]):1059            self._delete_main_framebuffer()1060 1061        # If framebuffer doesn't exist, create it1062        if self._main_fb is None:1063            # Generate standard buffer1064            self._main_cb, self._main_db = glGenRenderbuffers(2)1065 1066            glBindRenderbuffer(GL_RENDERBUFFER, self._main_cb)1067            glRenderbufferStorage(1068                GL_RENDERBUFFER, GL_RGBA,1069                self.viewport_width, self.viewport_height1070            )1071 1072            glBindRenderbuffer(GL_RENDERBUFFER, self._main_db)1073            glRenderbufferStorage(1074                GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,1075                self.viewport_width, self.viewport_height1076            )1077 1078            self._main_fb = glGenFramebuffers(1)1079            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self._main_fb)1080            glFramebufferRenderbuffer(1081                GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,1082                GL_RENDERBUFFER, self._main_cb1083            )1084            glFramebufferRenderbuffer(1085                GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,1086                GL_RENDERBUFFER, self._main_db1087            )1088 1089            # Generate multisample buffer1090            self._main_cb_ms, self._main_db_ms = glGenRenderbuffers(2)1091            glBindRenderbuffer(GL_RENDERBUFFER, self._main_cb_ms)1092            # glRenderbufferStorageMultisample(1093            #     GL_RENDERBUFFER, 4, GL_RGBA,1094            #     self.viewport_width, self.viewport_height1095            # )1096            # glBindRenderbuffer(GL_RENDERBUFFER, self._main_db_ms)1097            # glRenderbufferStorageMultisample(1098            #     GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT24,1099            #     self.viewport_width, self.viewport_height1100            # )1101            # 增加这一行1102            num_samples = min(glGetIntegerv(GL_MAX_SAMPLES), 4) # No more than GL_MAX_SAMPLES1103 1104            # 其实就是把 4 替换成 num_samples,其余不变1105            glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_RGBA, self.viewport_width, self.viewport_height)1106 1107            glBindRenderbuffer(GL_RENDERBUFFER, self._main_db_ms) # 这行不变1108 1109            # 这一行也是将 4 替换成 num_samples1110            glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_DEPTH_COMPONENT24, self.viewport_width, self.viewport_height)1111            1112            self._main_fb_ms = glGenFramebuffers(1)1113            glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self._main_fb_ms)1114            glFramebufferRenderbuffer(1115                GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,1116                GL_RENDERBUFFER, self._main_cb_ms1117            )1118            glFramebufferRenderbuffer(1119                GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,1120                GL_RENDERBUFFER, self._main_db_ms1121            )1122 1123            self._main_fb_dims = (self.viewport_width, self.viewport_height)1124 1125    def _delete_main_framebuffer(self):1126        if self._main_fb is not None:1127            glDeleteFramebuffers(2, [self._main_fb, self._main_fb_ms])1128        if self._main_cb is not None:1129            glDeleteRenderbuffers(2, [self._main_cb, self._main_cb_ms])1130        if self._main_db is not None:1131            glDeleteRenderbuffers(2, [self._main_db, self._main_db_ms])1132 1133        self._main_fb = None1134        self._main_cb = None1135        self._main_db = None1136        self._main_fb_ms = None1137        self._main_cb_ms = None1138        self._main_db_ms = None1139        self._main_fb_dims = (None, None)1140 1141    def _read_main_framebuffer(self, scene, flags):1142        width, height = self._main_fb_dims[0], self._main_fb_dims[1]1143 1144        # Bind framebuffer and blit buffers1145        glBindFramebuffer(GL_READ_FRAMEBUFFER, self._main_fb_ms)1146        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, self._main_fb)1147        glBlitFramebuffer(1148            0, 0, width, height, 0, 0, width, height,1149            GL_COLOR_BUFFER_BIT, GL_LINEAR1150        )1151        glBlitFramebuffer(1152            0, 0, width, height, 0, 0, width, height,1153            GL_DEPTH_BUFFER_BIT, GL_NEAREST1154        )1155        glBindFramebuffer(GL_READ_FRAMEBUFFER, self._main_fb)1156 1157        # Read depth1158        depth_buf = glReadPixels(1159            0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT1160        )1161        depth_im = np.frombuffer(depth_buf, dtype=np.float32)1162        depth_im = depth_im.reshape((height, width))1163        depth_im = np.flip(depth_im, axis=0)1164        inf_inds = (depth_im == 1.0)1165        depth_im = 2.0 * depth_im - 1.01166        z_near = scene.main_camera_node.camera.znear1167        z_far = scene.main_camera_node.camera.zfar1168        noninf = np.logical_not(inf_inds)1169        if z_far is None:1170            depth_im[noninf] = 2 * z_near / (1.0 - depth_im[noninf])1171        else:1172            depth_im[noninf] = ((2.0 * z_near * z_far) /1173                                (z_far + z_near - depth_im[noninf] *1174                                (z_far - z_near)))1175        depth_im[inf_inds] = 0.01176 1177        # Resize for macos if needed1178        if sys.platform == 'darwin':1179            depth_im = self._resize_image(depth_im)1180 1181        if flags & RenderFlags.DEPTH_ONLY:1182            return depth_im1183 1184        # Read color1185        if flags & RenderFlags.RGBA:1186            color_buf = glReadPixels(1187                0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE1188            )1189            color_im = np.frombuffer(color_buf, dtype=np.uint8)1190            color_im = color_im.reshape((height, width, 4))1191        else:1192            color_buf = glReadPixels(1193                0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE1194            )1195            color_im = np.frombuffer(color_buf, dtype=np.uint8)1196            color_im = color_im.reshape((height, width, 3))1197        color_im = np.flip(color_im, axis=0)1198 1199        # Resize for macos if needed1200        if sys.platform == 'darwin':

Showing the first 1,200 of 1340 lines. Download the file for the rest.