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.