[Blender Python] Afficher les images sur la vue 3D en utilisant OpenGL (bgl)

en premier

Lorsque vous écrivez un module complémentaire pour Blender, vous souhaitez souvent utiliser des images ici. Je pense que oui. Si vous utilisez OpenGL (bgl) fourni par Blender, vous pouvez l'afficher, alors essayons-le.

Il y a deux façons de lire l'image, donc j'écrirai chacune.

Lors de l'utilisation de bpy.data.images.load ()

échantillon

gl_texture_test.py


# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# Blender2.77a

import bpy
import bgl


image_file_path = "C:/Works/blender_rogo.png "

class GL_Texture():
    def __init__(self, file_path):
        self.image = None
        self.width = 0
        self.height = 0
        
        self.load_image(file_path)
    
    def load_image(self, file_path):
        try:
            self.image = bpy.data.images.load(file_path)
        except Exception as e:
            print(e)
        
        if self.image:
            self.width, self.height = self.image.size
            self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
    
    def remove(self):
        if self.image:
            try:
                self.image.user_clear()
                self.image.gl_free()
                #self.image.buffers_free()
                bpy.data.images.remove(self.image)
            except Exception as e:
                print(e)
    
    def bind(self):
        if self.image.bindcode[0]:
            bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.image.bindcode[0])
        else:
            self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
            print("reload gl texture")


class GL_Texture_test_Operator(bpy.types.Operator):
    bl_idname = "view3d.gl_texture_test_operator"
    bl_label = "View3D GL_Texture draw test"
    
    _handle_draw = None
    is_enabled = False
    _my_texture = None
    
    @staticmethod
    def draw_callback_px(self, context):
        bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glEnable(bgl.GL_TEXTURE_2D)
        
        tex = GL_Texture_test_Operator._my_texture
        w = tex.width
        h =tex.height
        tex.bind()
        bgl.glBegin(bgl.GL_QUADS)
        bgl.glTexCoord2f(0.0, 0.0)
        bgl.glVertex2f(0.0, 0.0)
        bgl.glTexCoord2f(1.0, 0.0)
        bgl.glVertex2f(0.0+w, 0.0)
        bgl.glTexCoord2f(1.0, 1.0)
        bgl.glVertex2f(0.0+w, 0.0+h)
        bgl.glTexCoord2f(0.0, 1.0)
        bgl.glVertex2f(0.0, 0.0+h)
        bgl.glEnd()
        
        bgl.glDisable(bgl.GL_TEXTURE_2D)
        bgl.glDisable(bgl.GL_BLEND)
    
    @staticmethod
    def handle_add(self, context):
        GL_Texture_test_Operator._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
                self.draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
    
    @staticmethod
    def handle_remove():
        if GL_Texture_test_Operator._handle_draw is not None:
            bpy.types.SpaceView3D.draw_handler_remove(GL_Texture_test_Operator._handle_draw, 'WINDOW')
            GL_Texture_test_Operator._handle_draw = None
            GL_Texture_test_Operator.is_enabled = False
    
    @classmethod
    def poll(cls, context):
        return context.area.type == 'VIEW_3D'
    
    def modal(self, context, event):
        if context.area:
            context.area.tag_redraw()
        return {'PASS_THROUGH'}
    
    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            if GL_Texture_test_Operator.is_enabled:
                self.cancel(context)
                return {'FINISHED'}
            else:
                GL_Texture_test_Operator._my_texture = GL_Texture(image_file_path)
                GL_Texture_test_Operator.handle_add(self, context)
                GL_Texture_test_Operator.is_enabled = True
                
                context.area.tag_redraw()
                context.window_manager.modal_handler_add(self)
                return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}
    
    def cancel(self, context):
        GL_Texture_test_Operator.handle_remove()
        
        if GL_Texture_test_Operator._my_texture is not None:
            GL_Texture_test_Operator._my_texture.remove()
            GL_Texture_test_Operator._my_texture = None


class GL_Texture_test_panel(bpy.types.Panel):
    bl_label = "GL Texture test"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    
    def draw(self, context):
        layout = self.layout
        if GL_Texture_test_Operator.is_enabled:
            layout.operator("view3d.gl_texture_test_operator", "Stop", icon="PAUSE")
        else:
            layout.operator("view3d.gl_texture_test_operator", "Start", icon="PLAY")


def register():
    bpy.utils.register_class(GL_Texture_test_Operator)
    bpy.utils.register_class(GL_Texture_test_panel)

def unregister():
    bpy.utils.unregister_class(GL_Texture_test_panel)
    bpy.utils.unregister_class(GL_Texture_test_Operator)

if __name__ == "__main__":
    register()

Copiez-le et collez-le dans l'éditeur de texte de Blender et exécutez-le. Pour image_file_path en haut, entrez le chemin de l'image que vous utilisez réellement.

blender_rogo.png J'ai dessiné le logo Blender de manière appropriée. Utilisez ceci pour que l'image soit affichée. Si vous n'avez pas d'image appropriée, veuillez l'utiliser.

Résultat d'exécution

Lorsque vous l'exécutez, un bouton sera d'abord affiché dans le panneau des propriétés, alors appuyez dessus. 2016-05-02_20h19_42.png

Puis 2016-05-02_20h24_01.png

La description

La vérification du chemin du fichier image à utiliser est omise. Vous devriez le vérifier lorsque vous l'utilisez réellement.

self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST) Je pense que les données d'image sont envoyées à la mémoire d'OpenGL. Je pense que les deux arguments de droite sont la méthode de stockage lors de la mise à l'échelle.

self.image.bindcode[0] L'ID de texture est entré ici après gl_load (). Ce n'était pas une liste avant, mais pour une raison quelconque, elle est devenue une liste. Je ne sais pas quelles données sont contenues sauf 0.

def bind(self):
    if self.image.bindcode[0]:
        bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.image.bindcode[0])
    else:
        self.image.gl_load(0, bgl.GL_NEAREST, bgl.GL_NEAREST)
        print("reload gl texture")

Quant à cette partie, la mémoire de texture est automatiquement libérée quelques minutes après son exécution, donc la texture est à nouveau chargée. Blender semble libérer de la mémoire tout seul.

La partie de dessin est la même que OpenGL.

La bonne chose à propos de cette méthode

«C'est facile de toute façon.

Où je pense que ce n'est pas si bon

Lors de la lecture des données d'image dans un tampon et de l'enregistrement

Exemple de classe GL_Texture

Réécrivez la classe GL_Texture de l'exemple ci-dessus et exécutez-la.

GL_Classe de texture


class GL_Texture():
    def __init__(self, file_path):
        self.texture_id = 0
        self.width = 0
        self.height = 0
        
        self.load_8bit_bitmap(file_path)
    
    def load_8bit_bitmap(self, file_path):
        f = None
        bitmap_data = None
        gl_buffer = None
        
        try:
            f = open(file_path, 'rb')
            file_header = f.read(14)
            info_header = f.read(40)
            self.width = struct.unpack("<i", info_header[4:8])[0]
            self.height = struct.unpack("<i", info_header[8:12])[0]
            
            palette = []
            for i in range(256):
                rgbr = f.read(4)
                palette.append(struct.unpack("BBBB", rgbr)[0:3])
            
            gl_buffer = bgl.Buffer(bgl.GL_BYTE, self.width*self.height*4)
            for i in range(self.width*self.height):
                data = f.read(1)
                index = struct.unpack("B", data)[0]
                bgr = palette[index]
                gl_buffer[i*4] = bgr[2]
                gl_buffer[i*4+1] = bgr[1]
                gl_buffer[i*4+2] = bgr[0]
                
                if index == 0:
                    gl_buffer[i*4+3] = 0
                else:
                    gl_buffer[i*4+3] = 255
            
            f.close()
        except Exception as e:
            if f is not None:
                f.close()
            print(e)
            return False
            
        # set opengl
        textures = bgl.Buffer(bgl.GL_INT, 1)
        bgl.glGenTextures(1, textures)
        self.texture_id = textures[0]
        
        bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture_id)
        
        bgl.glPixelStorei(bgl.GL_UNPACK_ALIGNMENT, 4)
        bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, bgl.GL_RGBA,
                self.width, self.height, 0, bgl.GL_RGBA,
                bgl.GL_UNSIGNED_BYTE, gl_buffer)
        
        bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MAG_FILTER, bgl.GL_NEAREST)
        bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_TEXTURE_MIN_FILTER, bgl.GL_NEAREST)
        
    def remove(self):
        if self.texture_id:
            textures = bgl.Buffer(bgl.GL_INT, 1)
            textures[0] = self.texture_id
            bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
            bgl.glDeleteTextures(1, textures)
            self.texture_id = 0
    
    def bind(self):
        bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture_id)

Mise en garde. Seuls les fichiers bitmap au format 8 bits peuvent être chargés. L'index 0 est transparent. Il semble que vous ne puissiez pas télécharger le fichier bitmap, veuillez donc le préparer vous-même.

La description

Il s'agit d'un flux d'ouverture d'un fichier, de son expansion dans un tampon et de son enregistrement dans OpenGL.

Dans bgl, si vous voulez passer un tampon comme argument, vous devez utiliser la classe Buffer. Je pense que c'est la différence avec OpenGL ordinaire.

Le processus de vérification des fichiers étant omis, veuillez vérifier diverses choses lorsque vous l'utilisez réellement.

La bonne chose à propos de cette méthode

Où je pense que ce n'est pas si bon

finalement

Personnellement, je recommande d'utiliser bpy.data.images.load () car c'est plus facile et plus facile. Il se charge vite. Le fichier image est également chargé dans Blender pendant l'exécution, mais même si vous l'enregistrez tel quel, il ne sera pas enregistré si le nombre d'utilisateurs est égal à 0, vous n'avez donc pas à vous inquiéter trop.

Recommended Posts

[Blender Python] Afficher les images sur la vue 3D en utilisant OpenGL (bgl)
Afficher des images dans OpenCV à partir de Python à l'aide d'une caméra USB externe sur votre MacBook
Enregistrer des images à l'aide de requêtes python3
Afficher les images PIL sur Jupyter
Utilisez python sur Raspberry Pi 3 pour détecter la "température (en utilisant un convertisseur A / N)"!
Diffusion sur LINE en utilisant python
Remarques sur l'utilisation de MeCab depuis Python
Installez Pytorch sur Blender 2.90 python sous Windows
Générer des images de texte multilingues à l'aide de Python
Étude sur Tokyo Rent en utilisant Python (3-2)
Remarques sur l'installation de Python à l'aide de PyEnv
Afficher les traces de pile en utilisant [Python] inspect
Notes sur l'utilisation de rstrip avec python.
Installer Python sur CentOS à l'aide de Pyenv
Étude sur Tokyo Rent en utilisant Python (3-3)
Installez Python sur CentOS en utilisant pyenv
Détectez les signaux analogiques avec un convertisseur A / N en utilisant python sur Raspberry Pi 3!
Faisons une animation 3D uniquement avec Python sans utiliser Blender! [FBX SDK Python]
Méthode de division de la crête d'eau dans une image 3D à l'aide de ImageJ
Remarques sur l'utilisation d'OpenCV avec Windows10 Python 3.8.3.
Exécuter du code Python sur C ++ (en utilisant Boost.Python)
Détectez la "luminosité" en utilisant python sur Raspberry Pi 3!
Installez la bibliothèque python sur Lambda à l'aide de [/ tmp]
Exécutez un servomoteur en utilisant python sur Raspberry Pi 3
Étude sur Tokyo Rent en utilisant Python (3-1 sur 3)
[Python] Affichage de la progression par barre de progression en utilisant tqdm
Détectez la température à l'aide de python sur Raspberry Pi 3!
GUI affiche les informations de retard de train à l'aide de python