Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
blender [2017/10/23 16:01] jcombier |
blender [2017/12/15 15:50] (Version actuelle) jcombier |
||
---|---|---|---|
Ligne 121: | Ligne 121: | ||
=====Rendu depuis un script python==== | =====Rendu depuis un script python==== | ||
+ | Les commandes pythons correspondantes aux actions en cours dans la GUI sont affichées dans la console python intégrée: https://www.youtube.com/watch?v=K0yb4sZ7B4g | ||
+ | |||
+ | |||
+ | https://wiki.blender.org/index.php/Doc:FR/2.4/Manual/Extensions/Python/Example | ||
+ | |||
+ | Exemple de rendu avec caméra fisheye: https://blender.stackexchange.com/questions/32848/script-for-rendering-a-simple-scene | ||
+ | ====Execution d'un script==== | ||
+ | |||
Utiliser l’interpréteur python intégré à blender depuis la ligne de commande | Utiliser l’interpréteur python intégré à blender depuis la ligne de commande | ||
Ligne 556: | Ligne 564: | ||
- | ==== Ajout de modules python externe ==== | + | ==== Ajout de modules python externes ==== |
- | Blender est livré avec son propre python. On le trouve à l'emplacement suivant : "chemin_vers_le_dossier_blender/blender-2.79-linux-glibc219-x86_64/2.79/python/lib/python3.5/". Votre version de blender (ici 2.79) et de python (ici 3.5) est à adapter à votre configuration. On retrouve à l'emplacement "python3.5/site-packages" les modules "numpy" et "requests". | + | Blender est livré avec son propre python. On le trouve à l'emplacement suivant : "chemin_vers_le_dossier_blender/blender-2.79-linux-glibc219-x86_64/2.79/python/lib/python3.5/". Votre version de blender (ici 2.79) et de python (ici 3.5) sera à adapter à votre configuration. On retrouve à l'emplacement "python3.5/site-packages" les modules "numpy" et "requests" déjà présent dans blender. |
Pour importer de nouveaux modules dans mes scripts, j'ai appliqué la solution suivante : | Pour importer de nouveaux modules dans mes scripts, j'ai appliqué la solution suivante : | ||
- | * faire en sorte que la version de python installé et celle de blender coïncide. J'ai téléchargé la dernière version de blender dont le python intégré avait la bonne version. | + | * faire en sorte que la version de python installé et celle de blender coïncide. Télécharger la dernière version de blender dont le python intégré a la bonne version. |
- | * télécharger les modules voulus sur mon python3 installé dans /usr/local/ | + | * télécharger les modules voulus sur le python3 installé dans /usr/local/ (l'adresse dépend de votre installation) |
- | * dans le script python ajouter les chemins vers les modules externes recherchés : sys.path.append('/usr/local/lib/python3.5/site-packages/') et sys.path.append('/usr/local/lib/python3.5/lib-dynload/') pour tkinter utilisé par matplotlib. | + | * ajouter dans le script python les chemins vers les modules externes recherchés : sys.path.append('/usr/local/lib/python3.5/site-packages/') et sys.path.append('/usr/local/lib/python3.5/lib-dynload/') pour tkinter utilisé par matplotlib. Les chemins doivent être ajouté avant d'inclure les modules avec "import". |
- | * Si votre script n'est pas à emplacement de l'exécutable python, ajoutez sys.path.append('chemin_vers_le_dossier_contenant_votre_script') et lancez votre commande ./chemin_vers_blender/blender -P mon_script.py | + | * ajouter sys.path.append('chemin_vers_le_dossier_contenant_le_script') et lancer la commande ./chemin_vers_blender/blender -P mon_script.py, si le script n'est pas à emplacement de l'exécutable python |
+ | |||
+ | Si la version de numpy de blender ne coïncide pas avec celle utilisée par les modules externes, renommez le numpy de blender (par exemple en 'numpy_1.10.1'). Le script utilisera alors le numpy installé dans /usr/local/. | ||
+ | |||
+ | Pour voir la version de numpy, entrer dans la console python : | ||
+ | >>> import numpy | ||
+ | >>> numpy.version.version | ||
+ | |||
+ | |||
+ | =====Animation modèle de visage===== | ||
+ | |||
+ | charger la scène Swirski-EyeModel.blend | ||
+ | |||
+ | Mode d'affichage default | ||
+ | |||
+ | Développer l'arbre Armature Head->the rig->Pose_head.002->eyetargetparent | ||
+ | |||
+ | à droite, choisir mode Bone (OS) | ||
+ | |||
+ | en bas, chosir Pose Mode | ||
+ | |||
+ | à droite dans transform->Location, changer x,y,z pour spécifier la cible visée par l'oeil | ||
+ | |||
+ | passer en mode scripting | ||
+ | |||
+ | recliquer sur Bone et changer transform->Location, la commande python correspondante apparait dans la console: | ||
+ | bpy.context.object.pose.bones["eyetargetparent"].location[0] = -0.113456 | ||
+ | |||
+ | bpy.context dépend de l'objet actuellement selectionné dans la GUI, pour modifier dans le script, il faut retrouver dans l'arborescence de la scene (s'aider de l'auto complétion avec CTRL+SPACE) | ||
+ | scene = bpy.context.scene | ||
+ | armature = bpy.data.objects['Armature Head'] | ||
+ | armature.pose.bones['eyetargetparent'].location[0] = -0.113456 | ||
+ | |||
+ | |||
+ | ./blender-2.74-linux-glibc211-x86_64/blender --python ./Swirski-EyeModel.py -b | ||
+ | |||
+ | <file python Swirski-EyeModel.py> | ||
+ | import bpy | ||
+ | import math | ||
+ | import mathutils | ||
+ | import os | ||
+ | import sys | ||
+ | import numpy as np | ||
+ | #lancer avec ./blender-2.74-linux-glibc211-x86_64/blender --python ./Swirski-EyeModel.py -b | ||
+ | |||
+ | |||
+ | |||
+ | #****** CREATE DIRECTORY WHERE TO SAVE IMAGES *********** | ||
+ | img_dir = '/tmp/images/' | ||
+ | if not os.path.exists(img_dir): | ||
+ | os.makedirs(img_dir) | ||
+ | |||
+ | |||
+ | |||
+ | bpy.ops.wm.open_mainfile(filepath="/media/HD500GO/blender/Swirski-EyeModel.blend") | ||
+ | |||
+ | scene = bpy.context.scene | ||
+ | armature = bpy.data.objects['Armature Head'] | ||
+ | camera_obj = bpy.data.objects['Camera'] | ||
+ | camera = bpy.data.cameras['Camera'] | ||
+ | |||
+ | eyeL = bpy.data.objects['eye.L'] | ||
+ | pupilGroup = eyeL.vertex_groups['eyepulpex.L'] | ||
+ | pupilVertices = [v for v in eyeL.data.vertices if pupilGroup.index in [g.group for g in v.groups]] | ||
+ | |||
+ | pupil_base_radius = max((v1.co - v2.co).length for v1 in pupilVertices for v2 in pupilVertices) * eyeL.scale[1] | ||
+ | |||
+ | eyeLbone = armature.pose.bones['def_eye.L'] | ||
+ | pupilLbone = armature.pose.bones['eyepulpex.L'] | ||
+ | |||
+ | def strVec(vec): | ||
+ | return "({},{},{})".format(vec[0], vec[1], vec[2]) | ||
+ | |||
+ | datafilepath = os.path.join(os.path.dirname(bpy.data.filepath), "render_eye_data.txt") | ||
+ | |||
+ | |||
+ | |||
+ | # switch on nodes | ||
+ | scene.use_nodes = True | ||
+ | tree = scene.node_tree | ||
+ | links = tree.links | ||
+ | |||
+ | # clear default nodes | ||
+ | for n in tree.nodes: | ||
+ | tree.nodes.remove(n) | ||
+ | |||
+ | # create input render layer node | ||
+ | rl = tree.nodes.new('CompositorNodeRLayers') | ||
+ | rl.location = 185,285 | ||
+ | |||
+ | # create output node | ||
+ | v = tree.nodes.new('CompositorNodeViewer') | ||
+ | v.location = 750,210 | ||
+ | v.use_alpha = False | ||
+ | |||
+ | # create output node | ||
+ | of_c_node = tree.nodes.new('CompositorNodeOutputFile') | ||
+ | of_c_node.location = 600, 200 | ||
+ | #of_node.base_path = | ||
+ | of_c_node.format.file_format = 'PNG' | ||
+ | |||
+ | # Links | ||
+ | links.new(rl.outputs[0], v.inputs[0]) # link Image output to Viewer input | ||
+ | links.new(rl.outputs[0], of_c_node.inputs[0]) | ||
+ | |||
+ | # Define path where to save image | ||
+ | base_path = "/tmp/images" #"./images_test_png" | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | with open(datafilepath, "w") as datafile: | ||
+ | #for frame in range(scene.frame_end+1): | ||
+ | for frame in range(2): | ||
+ | scene.frame_set(frame) | ||
+ | #modification position de visée de l'oeil | ||
+ | print('Jessica et Bertrand sont passés par la'); | ||
+ | armature.pose.bones['eyetargetparent'].location[0] = -0.113456 | ||
+ | |||
+ | camera_mat = camera_obj.matrix_world * mathutils.Matrix([[1,0,0,0],[0,-1,0,0],[0,0,-1,0],[0,0,0,1]]) | ||
+ | |||
+ | camera_invmat = camera_mat.inverted() | ||
+ | armature_mat = armature.matrix_world | ||
+ | |||
+ | head_world = armature_mat*eyeLbone.head | ||
+ | tail_world = armature_mat*eyeLbone.tail | ||
+ | |||
+ | head_cam = camera_invmat*head_world | ||
+ | tail_cam = camera_invmat*tail_world | ||
+ | |||
+ | eye_centre = head_cam | ||
+ | eye_radius = eyeLbone.bone.length | ||
+ | pupil_gaze = (tail_cam - head_cam).normalized() | ||
+ | pupil_radius = pupilLbone.scale[0] * pupil_base_radius | ||
+ | pupil_centre = eye_centre + eye_radius*pupil_gaze | ||
+ | |||
+ | #framestr = "{} | head_world: {} | tail_world: {} | vec_world: {} | head_cam: {} | tail_cam: {} | vec_cam {}".format( | ||
+ | # frame, *[strVec(x) for x in [head_world,tail_world,vec_world,head_cam,tail_cam,vec_cam]]) | ||
+ | framestr = "{} {} {} {} {} {}".format( | ||
+ | frame, | ||
+ | strVec(eye_centre), | ||
+ | eye_radius, | ||
+ | strVec(pupil_centre), | ||
+ | strVec(pupil_gaze), | ||
+ | pupil_radius | ||
+ | ) | ||
+ | print(framestr) | ||
+ | print(framestr, file=datafile) | ||
+ | #************* RENDER AND SAVE IMAGES *********** | ||
+ | # Define image resolution for rendering | ||
+ | scene.render.resolution_x = 250 | ||
+ | scene.render.resolution_y = 250 | ||
+ | # resolution percentage : have to be 100% to have the whole image resolution defined earlier | ||
+ | scene.render.resolution_percentage = 100 | ||
+ | |||
+ | # Activate which camera will be used for rendering | ||
+ | # (if more than one camera are defined, the rendering have to be repeated for each camera) | ||
+ | scene.camera = bpy.data.objects["Camera"] | ||
+ | |||
+ | |||
+ | # get viewer pixels directly | ||
+ | #pixels = bpy.data.images['Viewer Node'].pixels | ||
+ | #print(len(pixels)) # size is always width * height * 4 (rgba) | ||
+ | |||
+ | # copy buffer to numpy array for faster manipulation | ||
+ | # arr = np.array(pixels[:]) | ||
+ | # print('one pixel \n',arr[100:104]) | ||
+ | |||
+ | |||
+ | # Define path where to save images | ||
+ | str4 = base_path + "/image" | ||
+ | of_c_node.base_path = str4 + "000" | ||
+ | #out_z_node.base_path = str4 +"_Z_"+ "000" | ||
+ | |||
+ | # Render | ||
+ | bpy.ops.render.render()#write_still=True) | ||
+ | |||
+ | #************* EXIT BLENDER *********** | ||
+ | sys.exit(0) | ||
+ | |||
+ | </file> | ||
+ | |||
+ | |||
+ | {{http://homepages.laas.fr/bvandepo/files/oeil.png}} | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Accélérer le rendu ==== | ||
+ | |||
+ | Plusieurs astuces existent pour accelérer le rendu. Ces astuces diffèrent suivant si on est en "Blender render" ou en "Cycle render". | ||
+ | |||
+ | Lien pour le "Cycle render": | ||
+ | * [[https://www.blenderguru.com/articles/4-easy-ways-to-speed-up-cycles]] | ||
+ | * [[http://boundlessblending.com/blender-fast-rendering/]] | ||
+ | |||
+ | Lien pour "Blender render": | ||
+ | * [[https://www.blenderguru.com/articles/13-ways-to-reduce-render-times]] | ||
+ | |||
+ | Pour le calcul de Blender occupe 100% du CPU il faut indiquer le bon nombre de threads et la bonne taille de tuile ("Tile size") : [[https://blenderartists.org/forum/showthread.php?410469-Why-Blender-Cycles-Rendering-Not-Using-100-CPU]] | ||