Un petit "tuto" sur ce que je fais en ce moment et qui pourrait servir à d'autres, ou même à moi quand je serais sénile ou pas réveillée...
Dans une application, je devais faire une UI 2D avec un rendu de webcam et par dessus de la 3D :
- Sur le côté gauche j'ai donc une partie d'UI qui est toujours au DESSUS de tout
- En blanc j'ai de la 3D
- Derrière ma 3D, j'ai l'image de ma webcam qui est toujours au DESSOUS de tout
Comment faire? A l'aide de Dragonic, j'ai percé le mystère.
On peut avoir plusieurs Canvas d'UI! Et ça je l'ignorais jusque là. Donc c'est une super nouvelle. Il suffit de savoir manipuler les caméras dans leur depth et le tour est presque joué!
Donc prenons la partie UI de gauche, toujours au dessus :
Une fois votre UI créer, il faut faire quelques modifications de settings :
Rappel : de base, votre UI à pour Layer UI
Pour que votre UI ne soit pas rendue toujours au dessus de tout, il va d'abord falloir changer les settings de base de votre Canvas
Le Render Mode est le plus important des settings : il ne faut surtout PAS être en Screen Space - Overlay. Vous pouvez choisir Screen Space - Camera ou World Space. Pour des raisons particulières je n'ai pas le choix, je dois être en World Space.
Avec ce changement de render mode, il faut préciser la caméra qui regarde cette UI. Vous devez donc créer un Caméra qui ne regardera QUE cette ui.
Avec ces settings là, on dit que la Caméra ne voit que le Layer UI, elle a comme Depth 10, ce qui signifie qu'elle est rendu APRES les caméras de Depth entre 0 et 9. De plus elle est en Clear Flags Depth only.
Donc votre première Caméra est bien préparée, elle ne voit que votre UI (ma partie gauche en bleu vert donc).
Ensuite, on s'occupe de la Webcam :
Pour rendre une webcam il y a plusieurs solutions. La plus simple à mon goût pour s'éviter les complications de calculs bourrins et c'est ce qui est proposé par pas mal de gens quand on pose la question à google : utiliser une RawImage, soit un élément d'UI particulier.
L'idée est de récupérer la texture de la webcam et la fixée sur une Raw Image. Pour cela, il y a pleins de scripts sur le net. Voici le mien :
Code : Tout sélectionner
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class WebCamView : MonoBehaviour
{
//la raw image sur laquel je vais rendre ma texture de webcam
[SerializeField] private RawImage planeToRenderTexture;
//la webcam que je choisi
private WebCamDevice device;
//la texture de ma webcam activée
private WebCamTexture mCameraTexture = null;
public void Start()
{
#if UNITY_EDITOR
//pour récupérer la texture de ma webcam, Unity à une fonction toute faite.
// Les deux gros chiffres c'est la witdth et la height que je souhaite
//Pourquoi si gros? Ca se met tout seul au max à priori
mCameraTexture = new WebCamTexture (10000,10000);
#else
//je cherche la caméra de derrière. Vous n'y êtes pas obligés!
//la caméra 0 est la caméra arrière de votre device
device = WebCamTexture.devices[0];
int width = (int)Screen.width/2;
int height = (int)Screen.height/2;
//si je ne suis pas sur l'éditeur de Unity, je prends ma caméra de derrière que j'ai trouvé
//j'ai pris une résolution moindre pour éviter la latence.
mCameraTexture = new WebCamTexture (device.name,width,height);
#endif
OnCameraDisplay ();
}
public void OnCameraDisplay ()
{
//je connais la texture qui contiendra l'image de ma webcam
// il est tant que je l'applique à ma Raw Image
planeToRenderTexture.texture = mCameraTexture;
//on lance la webcam
mCameraTexture.Play ();
}
}
Oui mais t'avais parlé de 3D à un moment nan?
En effet, et ça normalement, vous savez le faire depuis toujours. Il vous faut un objet 3D (une cube, une sphère, un bonhomme, whatever) à rendre par une super Caméra. Évidemment cette caméra ne dois voir QUE la 3D, il faut donc faire une nouvelle Caméra avec ces paramètres ci :
On a donc les paramètres suivants : Clear Flags à Depth Only, la Depth à 1 et le Culling Mask est Mixed car on voit TOUT sauf l'UI.
Il ne reste plus qu'à faire "Play" et trilalilalou.... Ben, et ma 3D? Je la vois pas! C'est un scandale!
Et oui, on ne voit rien de plus qu'avant soit notre menu à gauche avec notre webcam.
Récapitulons l'ordre de rendu de nos Caméras :
- PanoramicCamera : depth à 1
- UICamera : depth à 10 (est rendu après les caméras 0 à 9)
C'est bien sûr! La caméra PanoramicCamera est dessinée d'abord PUIS plus tard la caméra UICamera! Hors notre RawImage prend quasi tout l'écran sauf le bandeau gauche. Donc rien n'est vide dans notre UI. Donc ce qui se passe derrière nous est invisible!
Et si on fait l'inverse au niveau des depths? Et bien on ne corrige rien, on verra bien la 3D au dessus. Mais elle sera au dessus de TOUT! Donc c'est hyper moche.
La solution c'est d'utiliser 3 Caméras pour arriver à nos fins. Il nous faut une caméra qui rend la webcam uniquement et qu'on dessinera en tout premier, puis on dessinera la 3D, puis enfin on dessinera notre bandeau gauche.
On va donc modifier un peu notre UI en créant un nouveau Canvas. Ce Canvas est très similaire à celui de l'UI. Il aura simplement une Caméra spéciale pour lui tout seul et on le place dans un Layer particulier appeler Webcam.
Une fois le Canvas créé, on y déplace notre RawImage d'avant et on à plus qu'à créer la Caméra spéciale webcam comme suit :
La différence avec la caméra UI? La depth est à 0 et le culling mask ne voit que le layer Webcam.
Du coup par rapport à avant on à ceci :
- WebcamRenderCam : depth à 0, ne voit que le layer Webcam
- PanoramicCamera : depth à 1, voit tout SAUT le layer Webcam et le layer UI
- UICamera : depth à 10, ne voit que le layer UI
On dessine donc bien dans l'ordre : la webcam, la 3D, le menu de gauche!
Super! Trop cool!!! Regarde Gérard ce que j'ai réussi à faire (Gérard c'est votre voisin de bureau)!!! Allez un p'tit café, une pause! J'ai trop bien bossé! ....
Hep, hep, hep! T'as pas l'impression que Gérard a grandit et minci d'un coup?
Han! Mais... Tu nous as menti! Comment tu nous a embobiné, ton truc il marche pas c'est nul! En plus Bruce Willis il meurt à la fin c'est ça ?!
Je vous rassure! La cause de l’amincissement soudain de Gérard est toute simple : on prend notre rendu de webcam et on le plaque bêtement sur notre RawImage. C'est pas bien malin en y réfléchissant! On passe à côté du ratio de l'image.
Première chose c'est d'ajouter à notre RawImage un component AspectRatioFitter! Ce component permet de conserver un ratio sur notre image et de lui offrir des capacités intéressantes pour étirer l'image pour remplir le parent ou autre. En l'occurrence, notre rendu de webcam prend les 3/4 de l'écran et on veut que ces 3/4 d'écran soit REMPLI (pas de bords noirs). On créé un parent à RawImage à la bonne taille (soit 3/4 d'écran) et on met la propriété Aspect Mode de l'aspactRatioFitter de RawImage à Envelope Parent.
Okay, mais ça suffit pas, on vient juste d'ajouter un outil, maintenant il faut s'en servir.
Donc pour corriger le problème google est notre ami et voici le lien qui m'a aidé et que j'ai repris en grande partie : http://answers.unity3d.com/questions/77 ... er-1148424
Un gentil monsieur nous offre donc la chance de modifier le code légèrement, pour que ce soit bien plus beau, moins tordu! Et voici l'adaptation que j'en ai faite :
Code : Tout sélectionner
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class WebCamView : MonoBehaviour
{
[SerializeField] private RawImage planeToRenderTexture;
private WebCamDevice device;
private WebCamTexture mCameraTexture = null;
//UPDATE : le component qu'on vient d'ajouter
private AspectRatioFitter rawImageARF;
// Image rotation
Vector3 rotationVector = new Vector3(0f, 0f, 0f);
// Image uvRect
Rect defaultRect = new Rect(0f, 0f, 1f, 1f);
Rect fixedRect = new Rect(0f, 1f, 1f, -1f);
// Image Parent's scale
Vector3 defaultScale = new Vector3(1f, 1f, 1f);
Vector3 fixedScale = new Vector3(-1f, 1f, 1f);
internal bool Visible { set { planeToRenderTexture.gameObject.SetActive(value);} }
public void Start()
{
#if UNITY_EDITOR
//pour récupérer la texture de ma webcam, Unity à une fonction toute faite.
// Les deux gros chiffres c'est la witdth et la height que je souhaite
//Pourquoi si gros? Ca se met tout seul au max à priori
mCameraTexture = new WebCamTexture (10000,10000);
#else
//je cherche la caméra de derrière. Vous n'y êtes pas obligés!
//la caméra 0 est la caméra arrière de votre device
device = WebCamTexture.devices[0];
int width = (int)Screen.width/2;
int height = (int)Screen.height/2;
//si je ne suis pas sur l'éditeur de Unity, je prends ma caméra de derrière que j'ai trouvé
//j'ai pris une résolution moindre pour éviter la latence.
mCameraTexture = new WebCamTexture (device.name,width,height);
#endif
//On rempli nos paramètres fraichement créés
rawImageARF = planeToRenderTexture.GetComponent<AspectRatioFitter>();
OnCameraDisplay ();
}
public void OnCameraDisplay ()
{
planeToRenderTexture.texture = mCameraTexture;
mCameraTexture.Play ();
//On change le ratio de notre RawImage pour collé au ratio de la webcam
SetWebCamRatio();
}
//UPDATE : à chaque update on vérifie si on doit ou nous mettre à jour le ratio!
private void Update()
{
if (mCameraTexture!=null && mCameraTexture.width < 100 )
{
//Debug.Log("Still waiting another frame for correct info...");
return;
}
if(mCameraTexture!=null && mCameraTexture.isPlaying && planeToRenderTexture!=null && planeToRenderTexture.gameObject.activeInHierarchy )
{
SetWebCamRatio();
}
}
//UPDATE : cette partie est commentée en anglais, ça se comprend bien.
// Le principe est de récupérer le ratio de l'image et l'appliquer au ratiofitter et à la raw image
private void SetWebCamRatio()
{
// from http://answers.unity3d.com/questions/773464/webcamtexture-correct-resolution-and-ratio.html#answer-1148424
// Rotate image to show correct orientation
rotationVector.z = -mCameraTexture.videoRotationAngle;
planeToRenderTexture.rectTransform.localEulerAngles = rotationVector;
// Set AspectRatioFitter's ratio
float videoRatio =
(float)mCameraTexture.width / (float)mCameraTexture.height;
rawImageARF.aspectRatio = videoRatio;
// Unflip if vertically flipped
planeToRenderTexture.uvRect =
mCameraTexture.videoVerticallyMirrored ? fixedRect : defaultRect;
// Mirror front-facing camera's image horizontally to look more natural
planeToRenderTexture.transform.parent.localScale =
device.isFrontFacing ? fixedScale : defaultScale;
}
}
Voilà, j'espère que ça vous aura aidé. C'est un peu long mais le but était de découpé en parties. Si vous avez des questions n'hésitez pas non plus . (Et non, Bruce Willis n'est même pas mort, vous avez vu!!)
Bonne aprèm à vous.
EDIT : ATTENTION : SI VOUS AVEZ DES LUMIÈRES DANS VOTRE SCÈNE VEILLEZ A BIEN RETIRER LES LAYERS CONCERNANT NOTRE UI!