AutoStereo3DCAMRenderer Script for 3DSmax2010
Transcription
AutoStereo3DCAMRenderer Script for 3DSmax2010
AutoStereo3DCAMRenderer Script for 3DSmax2010 Auteur : Clément Follet Date : Avril 2010 Licence : GNU/GPL 1. Explications détaillées sur le développement du script. Note sur les versions de 3DSmax : Le script a été conçu pour la version 2010 de 3DSmax, elle fonctionne donc sans problème sur cette version. Par contre des souçis peuvent survenir sur les versions antérieures de 3DSmax, notament sur les versions antérieures à la version 3DSmax 8 puisque le langage Maxscript a subit de nombreuses modifications pour le passage à 3DSmax2009. Le script a été développé avec la version de 3DSmax2010, avec l'appui du fichier d'aide maxscript2010.chm qui peut être téléchargé directement sur le site Autodesk. Installation et Tests: Pour charger le script il faut démarrer 3DSmax avec le script dans le dossier C:\Program Files\Autodesk\3ds Max 2010\Scripts\Startup, puis lancer le script dans la barre d'outils de droite menu Utilities puis MAXScript puis sélectionner le script correspodant dans la liste déroulante. Pour tester le script la manière la plus simple est de relancer 3DSmax, il y a aussi le Debugger et le Listener qui servent à tester le code. Lorsqu'un lance 3DSmax les erreurs du script sont affichées soit lors de l'ouverture du logiciel, soit lors de l'exécution du script. Quelques explications sur le langage MAXScript. Le widget principal qui sera ouvert lors du clic sur le menu déroulant se définit par le nom 'utility' dans le script, on y ajoute ensuite les groupes de widgets nécessaires, on retrouve dans MAXScript les nombreux widgets courants des langages d'interface graphique : – label <name> [ <string> ] [ style_sunkenedge:<bool> ] – button <name> [ <caption> ] [ images:<image_spec_array> ] \ [ toolTip:<string> ] [ border:<boolean> ] – dropdownlist <name> [ <caption> ] [ items:<array_of_strings> ] \ [ selection:<number> ] [ height:<number> ] [ tooltip:<string> ] – spinner <name> [ <caption> ] [ range:[min,max,val] ] [type:<name>] [scale:<float>] [ fieldWidth:<integer>] [toolTip:<string>] [indeterminate:<boolean>] [controller:(<controller>)] [setKeyBrackets:<boolean>] – checkbox <name> [ <caption> ] [ checked:<boolean> ] [ triState:<integer> ] [ tooltip:<string> ] – radiobuttons <name> [ <caption> ] labels:<array_of_strings> [default:<number>] [columns:<number>] – slider <name> [ <caption> ] [range:[min,max,val] ] [type:<#float| #integer>] [ticks:10] [orient:<#horizontal|#vertical>] [controller: (<controller>)] [toolTip:<string>] – colorpicker <name> [ <caption> ] [color:<color>] [alpha:<boolean>] [fieldWidth:<number>] [height:<number>] [modal:<boolean>] [title:<string>] – etc... Ces widgets permettent d'enregistrer des informations sur les paramètres à prendre en compte lors de l'éxécution du script, soit on y accède directement en lisant les paramètres de la manière widget.paramètre, soit on enregistre ce widget.paramètre dans une variable globale définie donc au début du script hors de toute fonction. L'avantage est que cette variable globale pourra être lue dans toutes les fonctions du script, de plus ça peut entraîner un gain de temps pour l'accès à la valeur, par contre le code en devient plus complexe. Dans le code ici, on évite d'utiliser des variables globales et on appelle les paramètres des widgets directement. Les widgets peuvent également envoyer des évènements de type: on <button> pressed do <expr> Ce qui permet d'appeler les fonctions nécessaires pour une procédure particulière. Les fonctions implémentées doivent alors être conçues de telles sorte à pouvoir être appelées de tel ou tel endroit du script (pour éviter les redondances dans le code) et correspondre à un ordre bien défini. Elles doivent de plus se trouver dans le bon ordre, c'est à dire définir la fonction initialisation() avant de l'appeler dans on AutoStereo3DCAMRendererUtility open do initialisation() . On rappelle donc un ordre élémentaire lors de l'écriture du script : 1. Ecriture des variables globales 2. Ecriture des fonctions 3. Ecriture des évènements appelant les fonctions (+ timers) De même faire attention à l'ordre des définitions des fonctions, vu qu'elles ne sont pas initialisées au début du script. Lorsque l'on veut ouvrir un ensemble de widget dans une nouvelle fenêtre, on définit un rollout de la manière : rollout generalSettingsRollout "General Settings" width:400 et ensuite on ajoute ses widgets de la même manière qu'une utility. Explications sur le développement du code. 1. Analogie entre variables globales et widgets. - global camera My3DCAM Cette variable permet de contenir la caméra 3D sélectionnée dans la liste, donc utilisée, pour lui appliquer les changements de paramètres dans tous le code telles que changement de la target distance, de la fov et des autres paramètres ajoutés à la caméra standard pour créer la caméra 3D. La valeur (la camera) change lorsque l'utilisateur change de caméra dans la liste des caméras 3D. - Paramètres pour l'initialisation de la caméra 3D : global integer initCamType=1 --mode Translation par défaut Il existe deux mode pour une caméra virtuelle 3D, soit le mode Rotation, soit le mode Translation. Tout d'abord une caméra 3D est un ensemble de caméras standards filmant différents points de vue, le nombre de caméras étant modulable. Ces caméras sont disposés suivant deux modes, le mode Rotation où toutes les caméras vise un point précis (on peut faire l'analogie avec l'oeil humain), et le mode Translation où les caméras sont orientées parallèlement et visent l'infini, un traitement postérieur de l'image étant nécessaire pour replacer l'objet théoriquement visé au centre de toutes les images. Le mode Translation sera plutôt utilisé pour les objets lointains.Voir plus de détails plus loin sur les mode Rotation et Translation. global float initTargetDistance=160.0 Distance à laquelle se trouve le point de visé devant la caméra. global float initShift=6.5 Espacement entre les caméras définissant la profondeur de l'image 3D, équivalent aux disparités entre les différentes vues (écartement des yeux : 65mm par défaut) --global float initRendApertureWidth = 5.0 –Non utilisé Pourrait servir à ouvrir le diaph de la caméra donc la luminosité et à moduler la pupille du spectateur, ce qui permettrait des gains en profondeur dans l'image 3D. global integer initDisparities=30 Valeur de référence pour évaluer un pourcentage de disparités dans l'image. global float initFov=45 Angle d'ouverture de la caméra virtuelle, correspondant à l'épaisseur de la lentille de la caméra. global integer initScreenPreset=1 On peut choisir son type d'écran pour le preview ou pour le rendu, soit un écran lenticulaire de dimension WxH avec n vues, soit un écran plat standard dans le cas de l'anaglyphe ou de la split3D. La valeur par défaut est Custom, on choisit les réglages que l'on souhaite. global integer initNumberOfViews=9 Le nombre de vues (ce paramètre est choisi suivant le type d'écran et modifie la caméra 3D virtuelle également) global integer initViewWidth=640 global integer initViewHeight=360 global float initPixelAspectRatio=1.0 La résolution et le ratio des pixels de l'image de sortie (preview ou rendu) global integer customNumberOfViews=initNumberOfViews global integer customViewWidth=initViewWidth global integer customViewHeight=initViewHeight global float customPixelAspectRatio=initPixelAspectRatio Variables enregistrant les paramètre de l'écran « Custom » global boolean initShowScreenPlane=true global boolean initShowLimitPlanes=true global boolean initShowPlanesMaterial=true global boolean initShowStereoCameras=true global boolean initShowAll=true Variables pour les paramètres d'affichage global array focusObjectArray global Point3 targetPosition Variables pour que la caméra3D fixe un objet particulier. global array previewImagesArray global bitmap previewImage Variable globales pour la mémorisation des vues et leur affichage global boolean initInvertViewsOrder = false Variable de configuration du preview et du rendu, affichage de vue1 à vueN ou inversement. -- Variables globales pour l'échange de données entre les rollout et utility --Output Files Settings global string lastDirectory = "" --enregistre le nom de dossier pour l'export global string lastFilename = "" --enregistre le nom de fichier pour l'export global string lastExtensionString=".tif" --enregistre l'extension pour l'export --General Settings global string lastAnaglypheTypeString="Red-Cyan" global string lastSplit3DTypeString="Side-by-Side" global color lastScreenPlaneColor=[255,255,255] global color lastLimitPlanesColor=[46,46,46] global boolean colorChanged=false global integer lastPlanesOpacity=100 global integer lastAnimationFrameDelay=0 --enregistre le type d'anaglyphe --enregistre le type de split3D --couleur du plan écran modifiable --couleur des plans de référence --ordre au timer pour changer la couleur --transparence des plans --durée entre chaque image lors du --preview lorsque mode animation Initialisation Lors de l'initialisation, seuls les 6 premiers widgets sont autorisés pour choisir une caméra standard et la transformer en caméra 3D, la recherche se fait automatiquement au lancement du script. Automatiquement, si des caméras 3D sont trouvées (c'est à dire si la caméra a le paramètre ID et qu'elle n'est pas une caméra fille, caméra filmant une vue particulière), elles sont ajouté dans la liste des caméras 3D. La première caméra 3D de liste est chargée et les autres widgets de l'interface sont autorisés. Création d'une caméra 3D et enregistrement de ses paramètres Lors de la création de la caméra 3D, des paramètres sont ajoutés à la caméra standard : --Création d'une My3DCAM avec ses paramètres My3DCAMParameters = attributes "My3DCAM Parameters" ( parameters main ( ID type:#integer camType type:#integer default:initCamType --1:rotation 2:translation -- = paramètre targetdistance de la camera (side cams en instance) shift type:#float default:initShift min:0 --6,5mm entre chaque oeil disparities type:#float default:initDisparities --FOV de toutes les cameras = paramètre fov de la camera (side cams en instance) screenPreset type:#integer default:initScreenPreset numberOfViews type:#integer default:initNumberOfViews viewWidth type:#integer default:initViewWidth min:0 viewHeight type:#integer default:initViewHeight min:0 pixelAspectRatio type:#float default:initPixelAspectRatio showScreenPlane type:#boolean default:initShowScreenPlane showLimitPlanes type:#boolean default:initShowLimitPlanes showPlanesMaterial type:#boolean default:initShowPlanesMaterial showStereoCameras type:#boolean default:initShowStereoCameras showAll type:#boolean default:initShowAll ) ) custAttributes.add My3DCAM My3DCAMParameters #unique Ces paramètres permettent lors d'un changement de caméra3D de recharger les paramètres de la nouvelle caméra 3D sélectionnée (fonction LoadMy3DCAM(caméra)). Ces paramètres sont enregistrés à chaque changement par les widgets, ce qui permet aussi de changer et d'enregistrer automatiquement les paramètres lorsque l'on met des clés dans une animation. Attention : Si l'on ferme le script avec le bouton close, ou que l'on delete la caméra 3D tous ces paramètres seront effacés, il faudra donc rerégler ceux-ci lors d'une nouvelle transformation d'une caméra standard en caméra 3D. Mode Translation, mode Rotation et scripts internes Suite à l'ajout des paramètres sur la caméra, on crée les caméras des différentes vues puis les plans de référence. On les crée à l'aide de scripts interne qui permettent que lors d'un changement de paramètre le script retrace automatiquement les caméras filles et les plans suivant ce nouveau paramètre. Pour créer ce script interne, on applique un controller du type voulu (ici position_Script) : cam.position.controller = position_Script() puis on définit le script : xcam=...stringx ycam=...stringy zcam=...stringz cam.position.controller.script = "dependsOn $"+My3DCAM.name+"\nin coordsys local ["+xcam+","+ycam+","+zcam+"]" Dans le cas du placement des caméra en mode rotation, les caméras doivent être placées sur un cercle avec comme point de visé le centre du cercle, on utilise donc l'équation d'un cercle : x = a + r.cos @ y = b + r.sin @ a,b: centre du cercle, r: rayon du cercle = targetdistance et où @: angle pour la position de la caméra Il reste donc à calculer cet angle en fonction du paramètre shift de la caméra 3D (espacement entre caméras) Cela se fait grâce à l'équation d'une corde c : c = shift = 2*r*sin (@/2) On a donc @ = 2*asin(shift/(2*r)) Remarque : en coordsys local de la caméra, l'axe de visé est toujours suivant z, dans la direction de visé. Dans le cas du placement en Translation, c'est plus simple, il suffit de décaler les caméras de shift. Un problème rencontré était que lorsqu'on voulait faire une rotation de la caméra 3D, les caméras filles de tournaient pas à la même vitesse, on a donc créé un nouveau script qui divise par 60 la rotation des caméras filles par rapport à la caméra mère : cam.transform.controller.roll_angle.controller = float_Script() str="($"+My3DCAM.name+".transform.controller.roll_angle/60)" cam.transform.controller.roll_angle.controller.script = "dependsOn $"+AI3DCAM.name+"\n "+str En ce qui concerne la création des plans de référence, dans le cas du plan symbolisant l'écran, on doit faire un script pour contrôler la position du plan, un script pour contrôler la hauteur du plan dépendant du paramètre .fov (Field Of View) de la caméra, et un script pour contrôler la largeur du plan dépendant de même du .fov. En ce qui concerne les plans de référence représentant un seuil limite de disparités, il a fallu définir une équation empirique pour donner une valeur à ces disparités. Définition du pourcentage de disparité : %Disp = (Distance Limite de Disparités) / (Distance caméra-écran) pour Fov de la caméra de 45° et un écartement de caméra shift égal à 6,5cm. Ce qui nous donne : Position Plan de disparité = targetDistance-((disparities/100.0)*(initShift/shift)*targetDistance*(tan(fov/2.0)/tan(initFov/2.0))) Cette formule est alors ajouté grâce à un script interne et les plans se redessineront automatiquement lorsque qu'un des paramètres de l'équation changera. Différents modes de preview et de rendu Lorsque l'on demande un preview ou un render de la scène, c'est la même fonction qui est utilisée sauf que quelques changements sont appliqués dans le cas d'un render, les fichiers sont alors enregistrés à un endroit précis déterminé par le rollout Output File Setting. Pour résumer, la stratégie du preview/render est d'exploiter la fenêtre de dialogue classique de 3DSmax nommée Render Scene Dialog. Cela permet d'utiliser toutes les fonctions existantes du dialogue comme par exemple rendre avec les effets atmosphériques. Le but du script est donc d'enregistrer dans le Render Scene Dialog les données de résolution et de pixel aspect ratio choisies sur l'interface du script, à chaque changement dans les widgets du script les données dans le render scene dialog sont actualisées. De même si un changement est fait dans le Render Scene Dialog, les widgets du script s'actualisent (le widget screenPreset se met en mode Custom) à l'aide d'un timer. Une fois les données de résolution (dépendant du type d'écran) choisies, l'utilisateur doit choisir dans le Render Scene Dialog, comme il le fait habituellement, entre rendu en mode Single, ou en mode Animation (plusieurs frames). Une fois ceci fait, l'utilisateur ne doit pas utiliser le bouton Render du Render Scene Dialog mais le bouton Render Preview ou Render de l'interface du script. Fonctionnement global de la fonction preview/render : 1. les paramètres du Render Scene Dialog sont enregistrés pour être remis ultérieurement car des changements vont être opérés. 2. Dans le cas d'un render, on doit enregistrer les chemins des fichiers de sortie 3. Dans le cas d'un preview, mettre en mode Single 4. Dans le cas d'un preview, on peut choisir entre utiliser le renderer par default ou l'actuel pour optimiser le temps de visualisation 5. Si mode Anaglyphe ou 3DSplit on doit utiliser seulement deux caméras, changement automatique 6. Dans le cas d'un preview, définir la taille de l'image rendue en fonction du widget Preview Size Ratio qui permet un gain de temps pour la visualisation 7. Si mode Translation on doit changer la fov de la caméra car les images doivent être croppée par la suite 8. Rendu des vues (et des éventuels Render Elements) dans un dossier temporaire avec l'outil max render 9. Enregistrement des chemins des fichiers pour ouverture ultérieure 10. Ouverture à chaque instant t de l'animation (si animation) des fichiers des n vues et stockage dans un tableau 11. Croppage si mode Translation 12. Inversion ou non des vues 13. Assemblage des images finale suivant plusieurs mode 14. Dans le cas d'un render, enregistrement des fichiers finaux. Croppage pour le mode Translation Dans le cas du mode Translation, la fov de la caméra doit être changée pour prendre des images des vues plus larges et ensuite recadrer la partie nécessaire dans les différentes vues (voir schéma dans le cas de 9 vues) : Width=2*tan(fov/2.0)*targetDistance newWidth=Width+(numberOfViewsSpinner.value-1)*shift newFov=2*atan((newWidth/2.0)/targetDistance) Une fois le nouveau paramètre .fov établit, on peut rendre les vues. Ces vues vont être croppées pour revenir à la taille normale de rendu et centrer l'objet visé par la caméra 3D dans chaque image. On le fait de cette manière, en travaillant ligne par ligne : for n=1 to numberOfViewsSpinner.value do( for j=0 to Height-1 do --suivant la verticale ( TempLine = getPixels openImagesArray[n] [0,j] newWidth for i=1 to ImageLine.count do ( ImageLine[i] = TempLine[i+(numberOfViewsSpinner.value-ViewNum)*shift] ) setPixels previewImagesArray[n] [0,j] ImageLine ) ) Différents modes d'affichage et d'export du preview/render : On peut choisir entre différents modes d'affichage, avec une certaine résolution d'image finale, et un certain nombre de vues (excepté pour les modes anaglyphe et Split3D). 1. Animation (preview seulement) Dans ce mode, les n vues sont affichés à la suite et l'on peut régler la vitesse de défilement dans les options générales. 2. Views Folders (render seulement) Dans ce mode, chaque vue pour un rendu d'une séquence est enregistré dans un dossier séparé. 3. Layers (preview et render) Dans ce mode, les n vues sont superposées pour se rendre compte de l'effet relief grâce au flou créé. 4. Mosaïc (preview et render) Dans ce mode, les n vues sont collées entre elles formant une mosaïque pouvant être compressée et lue par un player vidéo spécialement conçu pour cet effet par exemple. 5. Interlaced (preview et render) Dans ce mode, les n vues sont directement entrelacées pour former une image pouvant être lue directement sans player, en plein écran, sur l'écran autostéréoscopique lenticulaire correspondant. Description de l'entrelacement dans un pixel 3D équivalent à 3X3 pixels standard pour 9 vues (numérotées de 1 à 9): RV B R V B R V B R V B R V B R V B R V B R V B R V B 12345 67 89123456789123456789 23456 78 91234567891234567891 34567 89 12345678912345678912 45678 91 23456789123456789123 56789 12 34567891234567891234 67891 23 45678912345678912345 78912 34 56789123456789123456 89123 45 67891234567891234567 91234 56 78912345678912345678 6. Anaglyphe (preview et render) Dans ce mode, 2 points de vue d'écartement 'shift' sont exportés pour former une image anaglyphe de type Red-Cyan, Magenta-Vert ou Jaune-Bleu. 7. Split3D Dans ce mode, 2 points de vue d'écartement 'shift' sont exportés pour former une image split3D de type Side-by-Side, Full Side-by-Side, Top-and-Bottom ou Full Top-and-Bottom. Ce format sera utilisé par les prochaines chaînes de télévision 3D comme la chaîne de Canal+ attendue d'ici peu.