Table des matières:
- Étape 1: Démarrez un nouveau projet Unity
- Étape 2: Configurez la scène
- Étape 3: Faisons des particules
- Étape 4: Ralentir les particules
- Étape 5: Création du portail
- Étape 6: Nuanceurs de particules
- Étape 7: Créez la Skybox
- Étape 8: Logique du portail
- Étape 9: Presque terminé
- Étape 10: Mettez l'application sur votre téléphone
Vidéo: Portail AR à l'envers de Stranger Things : 10 étapes (avec photos)
2025 Auteur: John Day | [email protected]. Dernière modifié: 2025-01-13 06:57
Ce Instructable passera par la création d'une application mobile de réalité augmentée pour l'iPhone avec un portail qui mène à l'envers de Stranger Things. Vous pouvez entrer dans le portail, vous promener et revenir. Tout ce qui se trouve à l'intérieur du portail ne peut être vu qu'à travers le portail jusqu'à ce que vous y pénétriez. Une fois à l'intérieur, tout sera rendu partout, jusqu'à ce que vous reveniez dans le monde réel. Nous utiliserons le moteur de jeu vidéo Unity 3D avec le plugin Apple ARKit. Tous les logiciels que nous utiliserons peuvent être téléchargés et utilisés gratuitement. Vous n'avez pas besoin d'être un expert pour suivre, nous passerons par chaque étape !
Étape 1: Démarrez un nouveau projet Unity
Tout d'abord, téléchargez Unity3D et assurez-vous d'installer les fichiers de construction pour la plate-forme IOS. Vous devrez également télécharger Xcode et créer un compte développeur Apple gratuit. Votre iPhone devra également exécuter IOS 11 ou supérieur. Depuis aujourd'hui le 5 février 2018, IOS 11.3 est sorti mais xCode 9.2 n'a pas encore de fichiers de support pour cela. Donc, si vous utilisez la toute dernière version d'IOS, assurez-vous de télécharger la dernière version bêta de Xcode sur Apple. Developer.com.
Une fois que vous avez tous les programmes nécessaires, ouvrez Unity et démarrez un nouveau projet, appelez-le comme vous voulez. Nous allons avoir besoin du plugin Apple ARKit pour pouvoir utiliser la caméra de notre téléphone pour détecter le sol et placer des objets sur le sol. Importons cela maintenant en allant dans l'onglet Asset Store et en recherchant "ARKit". Vous devrez créer un compte Unity gratuit si vous n'en avez pas déjà un, puis cliquez sur importer pour obtenir le plugin.
Accédez au dossier des exemples dans le dossier ARKit et recherchez « UnityARKitScene ». Double-cliquez dessus pour l'ouvrir. Nous allons utiliser cette scène comme point de départ et construire à partir d'ici. Cette scène par défaut vous permettra de détecter le sol et lorsque vous appuyez sur l'écran, un cube sera placé dans cette position.
Commençons par mettre nos paramètres de construction au carré afin de ne pas oublier de le faire plus tard. Cliquez sur le fichier, créez les paramètres et supprimez toutes les scènes de cette liste. Cliquez sur ajouter des scènes ouvertes pour ajouter notre scène actuelle. La dernière chose que nous devons configurer ici est dans les paramètres du lecteur, descendez jusqu'à l'identifiant du paquet et le format de cette chaîne est com. YourCompanyName. YourAppName, donc dans mon cas, je fais quelque chose comme com. MatthewHallberg. PortalTest.
Étape 2: Configurez la scène
Regardez d'abord à gauche et trouvez l'objet du jeu appelé "GeneratePlanes". Avec cela en surbrillance, regardez vers la droite maintenant et cliquez sur la case à cocher pour le désactiver. De cette façon, nous n'avons pas les carrés bleus laids générés lorsque ARKit détecte un plan de masse. Supprimez ensuite l'objet de jeu "RandomCube" car nous ne voulons pas le voir dans notre scène.
Maintenant, nous devons d'abord créer notre porte de portail. Supprimez le cube qui est un enfant du "HitCubeParent". Faites un clic droit et choisissez créer un objet de jeu vide. Renommez-le "Portail". Maintenant, faites un clic droit sur cet objet et créez un cube, cela en fera un enfant du portail. Renommez-le "PostLeft" et ce sera le poste de gauche de notre portail. Mettez-le à l'échelle de sorte que le x soit 1, le y soit 28 et le z soit un. Faites la même chose pour le bon post. Créez maintenant le poteau supérieur et redimensionnez le y à 14. Tournez-le sur le côté et déplacez-le de manière à ce qu'il relie les autres poteaux. Rendre l'ensemble du portail à l'échelle 1,3 x 1,4 x 1.
Allez sur google et tapez la texture du bois ou de l'écorce. Téléchargez l'une de ces images et faites-la glisser dans votre dossier d'actifs dans Unity. Maintenant, faites glisser cette image sur tous vos messages de portail.
Cliquez à nouveau sur l'objet "Portail" et cliquez sur ajouter un composant à droite. Ajoutez-y le script "UnityARHitTestExample". Il y a un emplacement vide pour "Hit Transform", faites glisser l'objet "HitCubeParent" dans cet emplacement.
Étape 3: Faisons des particules
Nous allons maintenant utiliser le système Unity Particle pour créer un effet de fumée et de particules flottantes à l'intérieur de notre portail. Accédez à Actifs dans la barre de menus supérieure, aux actifs standard et aux systèmes de particules d'importation.
Créez deux objets de jeu vides dans votre portail et appelez l'un "SmokeParticles" et l'autre "FloatingParticles".
Ajoutez un composant de système de particules aux particules de fumée.
Ce composant a un tas d'options, mais nous n'avons besoin d'en changer que quelques-unes.
Changez la couleur de départ en quelque chose de bleu foncé avec environ 50% de transparence. Faire le taux d'émission 100. À l'intérieur de la forme, faire le rayon.01. Dans la partie du moteur de rendu en bas, changez la taille minimale à 0,8 et la taille maximale à 5. Sur le composant matériel, choisissez simplement le matériau de fumée dans la liste, mais nous allons changer cela plus tard.
Ajoutez maintenant un système de particules à l'objet de jeu de particules flottantes et définissez l'émission sur 500. Définissez la durée de vie de départ sur 2, le rayon sur 10, la taille de particule minimale sur 0,01 et la taille de particule maximale sur 0,015. Définissez le matériau sur la particule par défaut pour le moment.
Enfin, prenez les deux objets du jeu et faites-les pivoter de 90 degrés sur le x et soulevez-les dans les airs afin qu'ils émettent vers le bas sur la porte du portail.
Étape 4: Ralentir les particules
Puisque nous voulons que ces particules couvrent une grande surface mais se déplacent également lentement, nous devons créer notre propre fonction d'échantillonnage. Alors faites un clic droit dans le dossier des actifs et créez un nouveau script C# et appelez-le "ParticleSample". Copiez et collez ce code:
en utilisant System. Collections;
en utilisant System. Collections. Generic; en utilisant UnityEngine; public class ParticleSample: MonoBehaviour { private ParticleSystem ps; // Utilisez ceci pour l'initialisation void Start () { ps = GetComponent (); StartCoroutine (SampleParticleRoutine ()); } IEnumerator SampleParticleRoutine(){ var main = ps.main; main.simulationSpeed = 1000f; ps. Jouer (); return return new WaitForSeconds (.1f); main.simulationSpeed =.05f; } }
Faites maintenant glisser ce script sur chacun de vos objets de jeu de système de particules.
Étape 5: Création du portail
Maintenant, nous devons créer le portail, donc faites un clic droit sur l'objet de jeu du portail et créez un quad. Redimensionnez le quad pour qu'il couvre tout le portail, cela va devenir notre fenêtre de portail. La première chose que nous devons y ajouter est le shader de portail, cela ne rendra que les objets avec un autre shader spécifique dessus. Faites un clic droit dans le dossier des actifs et créez un nouveau shader non éclairé. Supprimez tout et collez ce code:
Shader "Portail/portailFenêtre"
{ SubShader { Zwrite off Colormask 0 cull off Stencil{ Ref 1 Pass replace } Pass { } } }
Cliquez avec le bouton droit dans la hiérarchie et créez un nouveau matériau, appelez-le PortalWindowMat, dans la liste déroulante de ce matériau, recherchez la section du portail et choisissez la fenêtre du portail. Faites glisser ce matériel sur votre portail portail.
Étape 6: Nuanceurs de particules
Cliquez à nouveau avec le bouton droit dans le dossier des actifs et créez un nouveau shader. Nous devons créer les shaders pour les particules qui entrent dans le portail. Remplacez tout le code par ceci:
Shader "Portail/Particules" {
Propriétés { _TintColor ("Tint Color", Color) = (0.5, 0.5, 0.5, 0.5) _MainTex ("Particle Texture", 2D) = "white" {} _InvFade ("Soft Particles Factor", Range(0.01, 3.0)) = 1.0 _Stencil("stencil", int) = 6 } Catégorie { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } Blend SrcAlpha OneMinusSrcAlpha ColorMask RGB Cull Off Lighting Off ZWrite Off SubShader { Stencil{ Ref 1 Comp[_Stencil] } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma multi_compile_particles #pragma multi_compile_fog #include "UnityCG.cginc_Main sampler2D; fixed4 _TintColor; struct appdata_t { float4 vertex: POSITION; couleur fixe4: COULEUR; float2 texcoord: TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex: SV_POSITION; couleur fixe4: COULEUR; float2 texcoord: TEXCOORD0; UNITY_FOG_COORDS(1) #ifdef SOFTPARTICLES_ON float4 projPos: TEXCOORD2; #endif UNITY_VERTEX_OUTPUT_STEREO }; float4 _MainTex_ST; v2f vert (appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.vertex = UnityObjectToClipPos(v.vertex); #ifdef SOFTPARTICLES_ON o.projPos = ComputeScreenPos (o.vertex); COMPUTE_EYEDEPTH(o.projPos.z); #endif o.color = v.color * _TintColor; o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); UNITY_TRANSFER_FOG(o, o.sommet); retour o; } UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture); float _InvFade; fixed4 frag (v2f i): SV_Target { #ifdef SOFTPARTICLES_ON float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))); float partZ = i.projPos.z; float fade = saturer (_InvFade * (sceneZ-partZ)); i.color.a *= fondu; #endif fixed4 col = 2.0f * i.color * tex2D(_MainTex, i.texcoord); UNITY_APPLY_FOG(i.fogCoord, col); retour col; } ENDCG } } } }
Créez deux nouveaux matériaux, l'un appelé portalSmoke et l'autre appelé portalParticles.
Pour chacun choisissez ce shader, dans la liste déroulante, dans les portails, les particules. Pour les particules de fumée, choisissez une texture de fumée et pour les particules, choisissez la texture de particules. Changez la couleur de la fumée en un bleu plus foncé avec environ 50 % de transparence. Accédez au composant de rendu de chaque système de particules dans votre portail et choisissez leurs matériaux respectifs que nous venons de créer.
Étape 7: Créez la Skybox
Maintenant, pour vraiment créer le type de look à l'envers, nous devons tout teinter en bleu foncé. Pour cela nous allons utiliser une skybox transparente donc faire un nouveau shader et coller dans ce code:
Shader "Portail/portailSkybox" {
Propriétés { _Tint ("Couleur de la teinte", Couleur) = (.5,.5,.5,.5) [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0 _Rotation ("Rotation", Range (0, 360)) = 0 [NoScaleOffset] _Tex ("Cubemap (HDR)", Cube) = "grey" {} _Stencil("StencilNum", int) = 6 } SubShader { Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" } Cull Off ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Stencil{ Ref 1 Comp[_Stencil] } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" échantillonneurCUBE _Tex; half4 _Tex_HDR; half4 _Teinte; la moitié _Exposition; float _Rotation; float3 RotateAroundYInDegrees (float3 vertex, float degrés) { float alpha = degrés * UNITY_PI / 180.0; flotteur sina, cosa; sincos(alpha, sina, cosa); float2x2 m = float2x2(cosa, -sina, sina, cosa); return float3(mul(m, vertex.xz), vertex.y).xzy; } struct appdata_t { float4 vertex: POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex: SV_POSITION; float3 texcoord: TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; v2f vert (appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); float3 rotated = RotateAroundYInDegrees(v.vertex, _Rotation); o.vertex = UnityObjectToClipPos(rotation); o.texcoord = v.vertex.xyz; retour o; } fixed4 frag (v2f i): SV_Target { half4 tex = texCUBE (_Tex, i.texcoord); half3 c = DecodeHDR (tex, _Tex_HDR); c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb; c *= _Exposition; retourner half4(c,.5); } ENDCG } } Repli désactivé }
Créez maintenant un nouveau matériau skybox, appelez-le "PortalSkybox" et choisissez ce shader portalSkybox dans le menu du portail. Allez dans Fenêtre, Éclairage, en haut et choisissez cette skybox que nous venons de créer. Accédez à la caméra principale et définissez des indicateurs clairs sur skybox. Pendant que nous sommes ici, ajoutons quelques composants sur notre caméra afin que nous puissions détecter les collisions. Ajoutez un composant de corps rigide à la caméra et décochez utiliser la gravité. Ajoutez un collisionneur de boîte et vérifiez son déclencheur. Faites en sorte que les collisionneurs de boîte taillent 0,5 x 1 x 4. Réglez le plan de découpage de la caméra sur 0,01.
Étape 8: Logique du portail
La dernière chose que nous devons faire est de créer la logique qui contrôle notre portail. Créez un nouveau script C# et appelez-le PortalController.
en utilisant System. Collections;
en utilisant System. Collections. Generic; en utilisant UnityEngine; espace de noms UnityEngine. XR.iOS{ public class PortalController: MonoBehaviour { public Material matériaux; public MeshRenderer meshRenderer; public UnityARVideo UnityARVideo; private bool isInside = false; private bool isOutside = true; // Utilisez ceci pour l'initialisation void Start () { OutsidePortal (); } void OnTriggerStay(Collider col){ Vector3 playerPos = Camera.main.transform.position + Camera.main.transform.forward * (Camera.main.nearClipPlane * 4); if (transform. InverseTransformPoint(playerPos).z <= 0){ if (isOutside) { isOutside = false; isInside = vrai; InsidePortal (); } } else { if (isInside) { isInside = false; isOutside = vrai; ExtérieurPortail (); } } } void OutsidePortal(){ StartCoroutine (DelayChangeMat (3)); } void InsidePortal(){ StartCoroutine (DelayChangeMat (6)); } IEnumerator DelayChangeMat(int stencilNum){ UnityARVideo.shouldRender = false; rendement return new WaitForEndOfFrame (); meshRenderer.enabled = false; foreach (Material mat dans les matériaux) { mat. SetInt ("_Stencil", stencilNum); } return return new WaitForEndOfFrame (); meshRenderer.enabled = true; UnityARVideo.shouldRender = true; } } }
Faites glisser ce nouveau script sur la fenêtre de votre portail. Cela nous fera entrer et sortir du portail chaque fois que le collisionneur de notre caméra entre en collision avec la fenêtre du portail. Maintenant, dans la fonction qui modifie tous les matériaux, nous disons au plugin ARkit de ne pas rendre le cadre, alors allez à la caméra principale et ouvrez le script UnityARVideo. Créez un bool public shouldRender en haut et définissez-le sur true. Dans la fonction OnPreRender(), enveloppez tout dans une instruction if où tout à l'intérieur ne s'exécutera que si shouldRender est vrai. L'ensemble du script devrait ressembler à ceci:
en utilisant le système;
en utilisant System. Runtime. InteropServices; en utilisant UnityEngine; en utilisant UnityEngine. Rendering; espace de noms UnityEngine. XR.iOS { public class UnityARVideo: MonoBehaviour { public Material m_ClearMaterial; [HideInInspector] public bool shouldRender = true; privé CommandBuffer m_VideoCommandBuffer; privé Texture2D _videoTextureY; privé Texture2D _videoTextureCbCr; privé Matrix4x4 _displayTransform; private bool bCommandBufferInitialized; public void Start() { UnityARSessionNativeInterface. ARFrameUpdatedEvent += UpdateFrame; bCommandBufferInitialized = false; } void UpdateFrame(UnityARCamera cam) { _displayTransform = new Matrix4x4(); _displayTransform. SetColumn(0, cam.displayTransform.column0); _displayTransform. SetColumn(1, cam.displayTransform.column1); _displayTransform. SetColumn(2, cam.displayTransform.column2); _displayTransform. SetColumn(3, cam.displayTransform.column3); } void InitializeCommandBuffer() { m_VideoCommandBuffer = new CommandBuffer(); m_VideoCommandBuffer. Blit(null, BuiltinRenderTextureType. CurrentActive, m_ClearMaterial); GetComponent(). AddCommandBuffer(CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); bCommandBufferInitialized = true; } void OnDestroy() { GetComponent(). RemoveCommandBuffer(CameraEvent. BeforeForwardOpaque, m_VideoCommandBuffer); UnityARSessionNativeInterface. ARFrameUpdatedEvent -= UpdateFrame; bCommandBufferInitialized = false; } #if !UNITY_EDITOR public void OnPreRender() { if (shouldRender){ ARTextureHandles handles = UnityARSessionNativeInterface. GetARSessionNativeInterface (). GetARVideoTextureHandles(); if (handles.textureY == System. IntPtr. Zero || handles.textureCbCr == System. IntPtr. Zero) { return; } if (!bCommandBufferInitialized) { InitializeCommandBuffer (); } Résolution currentResolution = Screen.currentResolution; // Texture Y if (_videoTextureY == null) { _videoTextureY = Texture2D. CreateExternalTexture(currentResolution.width, currentResolution.height, TextureFormat. R8, false, false, (System. IntPtr)handles.textureY); _videoTextureY.filterMode = FilterMode. Bilinear; _videoTextureY.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture("_textureY", _videoTextureY); } // Texture CbCr if (_videoTextureCbCr == null) { _videoTextureCbCr = Texture2D. CreateExternalTexture(currentResolution.width, currentResolution.height, TextureFormat. RG16, false, false, (System. IntPtr)handles.textureCbCr); _videoTextureCbCr.filterMode = FilterMode. Bilinear; _videoTextureCbCr.wrapMode = TextureWrapMode. Repeat; m_ClearMaterial. SetTexture("_textureCbCr", _videoTextureCbCr); } _videoTextureY. UpdateExternalTexture(handles.textureY); _videoTextureCbCr. UpdateExternalTexture(handles.textureCbCr); m_ClearMaterial. SetMatrix("_DisplayTransform", _displayTransform); } } #else public void SetYTexure(Texture2D YTex) { _videoTextureY = YTex; } public void SetUVTexure (Texture2D UVTex) { _videoTextureCbCr = UVTex; } public void OnPreRender() { if (!bCommandBufferInitialized) { InitializeCommandBuffer (); } m_ClearMaterial. SetTexture("_textureY", _videoTextureY); m_ClearMaterial. SetTexture("_textureCbCr", _videoTextureCbCr); m_ClearMaterial. SetMatrix("_DisplayTransform", _displayTransform); } #fin si } }
Étape 9: Presque terminé
Enfin, lorsque nous cliquons sur l'écran et plaçons le portail, nous voulons qu'il soit toujours face à nous. Pour ce faire, accédez au script "UnityARHitTestExample" sur le portail. Remplacez tout à l'intérieur par ceci:
en utilisant le système;
en utilisant System. Collections. Generic; espace de noms UnityEngine. XR.iOS { public class UnityARHitTestExample: MonoBehaviour { public Transform m_HitTransform; public float maxRayDistance = 30.0f; public LayerMask collisionLayer = 1 < 0) { foreach (var hitResult in hitResults) { Debug. Log ("J'ai été touché !"); m_HitTransform.position = UnityARMatrixOps. GetPosition (hitResult.worldTransform); m_HitTransform.rotation = UnityARMatrixOps. GetRotation (hitResult.worldTransform); Debug. Log (string. Format ("x:{0:0.######} y:{1:0.######} z:{2:0.###### }", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); Vector3 currAngle = transform.eulerAngles; transform. LookAt (Camera.main.transform); transform.eulerAngles = new Vector3 (currAngle.x, transform.eulerAngles.y, currAngle.z); renvoie vrai; } } renvoie faux; } // Update est appelé une fois par image void Update () { #if UNITY_EDITOR // nous n'utiliserons ce script que du côté de l'éditeur, bien que rien ne l'empêche de fonctionner sur l'appareil si (Input. GetMouseButtonDown (0)) { Ray ray = Camera.main. ScreenPointToRay (Input.mousePosition); RaycastHit a été touché; // nous allons essayer de toucher l'un des objets de jeu du collisionneur d'avions générés par le plugin // de manière similaire à l'appel de HitTest avec ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent if (Physics. Raycast (ray, out hit, maxRayDistance, collisionLayer)) { // nous allons obtenir la position du point de contact m_HitTransform.position = hit.point; Debug. Log (string. Format ("x:{0:0.######} y:{1:0.######} z:{2:0.###### }", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); //et la rotation de la transformation du collisionneur plan m_HitTransform.rotation = hit.transform.rotation; } } #else if (Input.touchCount > 0 && m_HitTransform != null) { var touch = Input. GetTouch(0); if (touch.phase == TouchPhase. Began || touch.phase == TouchPhase. Moved) { var screenPosition = Camera.main. ScreenToViewportPoint(touch.position); ARPoint point = nouveau ARPoint { x = screenPosition.x, y = screenPosition.y }; // hiérarchiser les types de résultats ARHitTestResultType resultTypes = { ARHitTestResultType. ARHitTestResultTypeExistingPlaneUsingExtent, // si vous voulez utiliser des plans infinis, utilisez ceci: //ARHitTestResultType. ARHitTestResultTypeExistingPlane, ARHitori. foreach (ARHitTestResultType resultType dans resultTypes) { if (HitTestWithResultType (point, resultType)) { return; } } } } #fin si } } }
Étape 10: Mettez l'application sur votre téléphone
Enfin, nous avons terminé. Allez dans le fichier, créez les paramètres et cliquez sur construire. Ouvrez Xcode et choisissez le dossier qui a été créé à partir du build. Choisissez votre équipe de développement et installez l'application sur votre téléphone ! Vous pouvez modifier les couleurs des particules et de la skybox en fonction de vos besoins. Faites-moi savoir dans les commentaires si vous avez des questions et merci d'avoir regardé!