با WebGL Overlay View تجارب نقشه سه بعدی بسازید

1. قبل از شروع

این کد لبه به شما می آموزد که چگونه از ویژگی های مبتنی بر WebGL Maps JavaScript API برای کنترل و رندر کردن نقشه برداری در سه بعدی استفاده کنید.

پین سه بعدی نهایی

پیش نیازها

این کد لبه فرض می کند که شما دانش متوسطی از جاوا اسکریپت و Maps JavaScript API دارید. برای یادگیری اصول اولیه استفاده از Maps JS API، نوار کد افزودن نقشه به وب سایت خود (جاوا اسکریپت) را امتحان کنید.

چیزی که یاد خواهید گرفت

  • ایجاد شناسه نقشه با فعال بودن نقشه برداری برای جاوا اسکریپت.
  • کنترل نقشه با شیب و چرخش برنامه ای.
  • رندر کردن اشیاء سه بعدی روی نقشه با WebGLOverlayView و Three.js .
  • متحرک سازی حرکات دوربین با moveCamera .

آنچه شما نیاز دارید

  • یک حساب Google Cloud Platform با فعال کردن صورت‌حساب
  • یک کلید API پلتفرم Google Maps با فعال کردن API جاوا اسکریپت Maps
  • دانش متوسط ​​از جاوا اسکریپت، HTML و CSS
  • یک ویرایشگر متن یا IDE به انتخاب شما
  • Node.js

2. راه اندازی شوید

برای مرحله فعال سازی زیر، باید Maps JavaScript API را فعال کنید.

پلتفرم نقشه های گوگل را راه اندازی کنید

اگر قبلاً حساب Google Cloud Platform و پروژه‌ای با صورت‌حساب فعال ندارید، لطفاً راهنمای شروع به کار با Google Maps Platform را برای ایجاد یک حساب صورت‌حساب و یک پروژه ببینید.

  1. در Cloud Console ، روی منوی کشویی پروژه کلیک کنید و پروژه ای را که می خواهید برای این کد لبه استفاده کنید انتخاب کنید.

  1. APIها و SDKهای پلتفرم Google Maps مورد نیاز برای این لبه کد را در Google Cloud Marketplace فعال کنید. برای انجام این کار، مراحل این ویدئو یا این مستند را دنبال کنید.
  2. یک کلید API در صفحه Credentials در Cloud Console ایجاد کنید. می توانید مراحل این ویدئو یا این مستند را دنبال کنید. همه درخواست‌ها به پلتفرم نقشه‌های Google به یک کلید API نیاز دارند.

راه اندازی Node.js

اگر قبلاً آن را ندارید، به https://nodejs.org/ بروید تا زمان اجرا Node.js را دانلود و بر روی رایانه خود نصب کنید.

Node.js با مدیریت بسته npm ارائه می شود که باید وابستگی هایی را برای این کد لبه نصب کنید.

قالب شروع پروژه را دانلود کنید

قبل از شروع این کد لبه، برای دانلود قالب پروژه شروع کننده و همچنین کد راه حل کامل، موارد زیر را انجام دهید:

  1. مخزن GitHub را برای این کد لبه در https://github.com/googlecodelabs/maps-platform-101-webgl/ دانلود یا جدا کنید. پروژه شروع در دایرکتوری /starter قرار دارد و شامل ساختار فایل اصلی است که برای تکمیل کد لبه نیاز دارید. هر چیزی که برای کار با آن نیاز دارید در فهرست /starter/src قرار دارد.
  2. هنگامی که پروژه شروع را دانلود کردید، npm install در پوشه /starter اجرا کنید. این همه وابستگی های مورد نیاز فهرست شده در package.json را نصب می کند.
  3. هنگامی که وابستگی های شما نصب شد، npm start در دایرکتوری اجرا کنید.

پروژه شروع برای شما تنظیم شده است تا از webpack-dev-server استفاده کنید که کدی را که شما به صورت محلی می نویسید را کامپایل و اجرا می کند. webpack-dev-server همچنین هر زمان که کد را تغییر دهید به طور خودکار برنامه شما را در مرورگر بارگیری می کند.

اگر می‌خواهید کد راه‌حل کامل را در حال اجرا ببینید، می‌توانید مراحل راه‌اندازی بالا را در پوشه /solution کامل کنید.

کلید API خود را اضافه کنید

برنامه شروع کننده شامل تمام کدهای مورد نیاز برای بارگیری نقشه با JS API Loader است، به طوری که تنها کاری که باید انجام دهید این است که کلید API و شناسه نقشه خود را ارائه دهید. JS API Loader یک کتابخانه ساده است که روش سنتی بارگیری Maps JS API به صورت درون خطی در قالب HTML را با یک تگ script خلاصه می کند و به شما امکان می دهد همه چیز را در کد جاوا اسکریپت مدیریت کنید.

برای افزودن کلید API خود، در پروژه شروع به صورت زیر عمل کنید:

  1. app.js باز کنید.
  2. در شیء apiOptions ، کلید API خود را به عنوان مقدار apiOptions.apiKey کنید.

3. یک Map ID ایجاد و استفاده کنید

برای استفاده از ویژگی های مبتنی بر WebGL Maps JavaScript API، به شناسه نقشه با فعال بودن نقشه برداری نیاز دارید.

ایجاد شناسه نقشه

نسل شناسه نقشه

  1. در کنسول Google Cloud، به 'Google Maps Platform' > 'Map Management' بروید.
  2. روی «ایجاد شناسه نقشه جدید» کلیک کنید.
  3. در قسمت «نام نقشه»، یک نام برای شناسه نقشه خود وارد کنید.
  4. در منوی کشویی «نوع نقشه»، «جاوا اسکریپت» را انتخاب کنید. 'گزینه های جاوا اسکریپت' ظاهر می شود.
  5. در زیر «گزینه‌های جاوا اسکریپت»، دکمه رادیویی «وکتور»، کادر «تیلت» و کادر «چرخش» را انتخاب کنید.
  6. اختیاری در قسمت «توضیحات»، توضیحاتی را برای کلید API خود وارد کنید.
  7. روی دکمه «بعدی» کلیک کنید. صفحه "جزئیات شناسه نقشه" ظاهر می شود.

    صفحه جزئیات نقشه
  8. شناسه نقشه را کپی کنید. از این در مرحله بعد برای بارگیری نقشه استفاده خواهید کرد.

با استفاده از شناسه نقشه

برای بارگذاری نقشه برداری، باید یک شناسه نقشه را به عنوان ویژگی در گزینه ها هنگام نمونه سازی نقشه ارائه دهید. در صورت تمایل، هنگام بارگیری Maps JavaScript API، می‌توانید همان Map ID را نیز ارائه دهید.

برای بارگذاری نقشه با شناسه نقشه، موارد زیر را انجام دهید:

  1. شناسه نقشه خود را به عنوان مقدار mapOptions.mapId تنظیم کنید.

    ارائه شناسه نقشه هنگام نمونه‌برداری از نقشه، به پلتفرم نقشه‌های Google می‌گوید کدام یک از نقشه‌های شما برای یک نمونه خاص بارگیری شود. می‌توانید از همان Map ID در چندین برنامه یا چندین نما در یک برنامه دوباره استفاده کنید.
    const mapOptions = {
      "tilt": 0,
      "heading": 0,
      "zoom": 18,
      "center": { lat: 35.6594945, lng: 139.6999859 },
      "mapId": "YOUR_MAP_ID"
    };
    

برنامه در حال اجرا در مرورگر خود را بررسی کنید. نقشه برداری با شیب و چرخش فعال باید با موفقیت بارگیری شود. برای بررسی فعال بودن شیب و چرخش، کلید shift را نگه دارید و یا با ماوس بکشید یا از کلیدهای جهت دار روی صفحه کلید خود استفاده کنید.

اگر نقشه بارگیری نمی‌شود، بررسی کنید که یک کلید API معتبر در apiOptions ارائه کرده‌اید. اگر نقشه کج نمی‌شود و نمی‌چرخد، بررسی کنید که شناسه نقشه را با شیب و چرخش در apiOptions و mapOptions فعال کرده‌اید.

نقشه کج شده

فایل app.js شما اکنون باید به شکل زیر باشد:

    import { Loader } from '@googlemaps/js-api-loader';

    const apiOptions = {
      "apiKey": 'YOUR_API_KEY',
    };

    const mapOptions = {
      "tilt": 0,
      "heading": 0,
      "zoom": 18,
      "center": { lat: 35.6594945, lng: 139.6999859 },
      "mapId": "YOUR_MAP_ID"
    }

    async function initMap() {
      const mapDiv = document.getElementById("map");
      const apiLoader = new Loader(apiOptions);
      await apiLoader.load();
      return new google.maps.Map(mapDiv, mapOptions);
    }

    function initWebGLOverlayView (map) {
      let scene, renderer, camera, loader;
      // WebGLOverlayView code goes here
    }

    (async () => {
      const map = await initMap();
    })();

4. WebGLOverlayView را پیاده سازی کنید

WebGLOverlayView به شما امکان دسترسی مستقیم به همان زمینه رندر WebGL را می دهد که برای ارائه نقشه پایه برداری استفاده می شود. این بدان معناست که می‌توانید با استفاده از WebGL و همچنین کتابخانه‌های گرافیکی مبتنی بر WebGL، اشیاء دو بعدی و سه بعدی را مستقیماً روی نقشه رندر کنید.

WebGLOverlayView پنج قلاب را در چرخه حیات بافت رندر WebGL نقشه نشان می دهد که می توانید از آنها استفاده کنید. در اینجا یک توضیح سریع از هر قلاب و اینکه برای چه کاری باید آنها را انجام دهید آورده شده است:

  • onAdd() : زمانی که با فراخوانی setMap در یک نمونه WebGLOverlayView ، همپوشانی به نقشه اضافه می شود، فراخوانی می شود. اینجاست که شما باید هر کار مرتبط با WebGL را که نیازی به دسترسی مستقیم به زمینه WebGL ندارد، انجام دهید.
  • onContextRestored() : زمانی فراخوانی می شود که زمینه WebGL در دسترس باشد اما قبل از اینکه هر چیزی رندر شود. اینجاست که باید اشیاء را مقداردهی اولیه کنید، وضعیت bind را انجام دهید و هر کار دیگری که نیاز به دسترسی به زمینه WebGL دارد اما خارج از onDraw() انجام می شود، انجام دهید. این به شما این امکان را می‌دهد تا هر چیزی را که نیاز دارید بدون اضافه کردن سربار اضافی به رندر واقعی نقشه، که در حال حاضر به GPU فشرده است، تنظیم کنید.
  • onDraw() : هنگامی که WebGL شروع به رندر کردن نقشه و هر چیز دیگری که درخواست کرده اید، یک بار در هر فریم فراخوانی می شود. شما باید تا حد امکان در onDraw() کمتر کار کنید تا از ایجاد مشکل در عملکرد در رندر نقشه جلوگیری کنید.
  • onContextLost() : زمانی فراخوانی می شود که زمینه رندر WebGL به هر دلیلی از بین برود.
  • onRemove() : زمانی فراخوانی می شود که همپوشانی با فراخوانی setMap(null) در یک نمونه WebGLOverlayView از روی نقشه حذف شود.

در این مرحله، یک نمونه از WebGLOverlayView ایجاد می‌کنید و سه مورد از قلاب‌های چرخه حیات آن را پیاده‌سازی می‌کنید: onAdd ، onContextRestored ، و onDraw . برای تمیز نگه داشتن چیزها و پیگیری آسان تر، تمام کدهای همپوشانی در تابع initWebGLOverlayView() ارائه شده در قالب شروع برای این کد لبه استفاده می شود.

  1. یک WebGLOverlayView() ایجاد کنید.

    پوشش توسط Maps JS API در google.maps.WebGLOverlayView ارائه شده است. برای شروع، با الحاق موارد زیر به initWebGLOverlayView() یک نمونه ایجاد کنید:
    const webGLOverlayView = new google.maps.WebGLOverlayView();
    
  2. قلاب های چرخه حیات را پیاده سازی کنید.

    برای پیاده‌سازی قلاب‌های چرخه حیات، موارد زیر را به initWebGLOverlayView() اضافه کنید:
    webGLOverlayView.onAdd = () => {};
    webGLOverlayView.onContextRestored = ({gl}) => {};
    webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {};
    
  3. نمونه همپوشانی را به نقشه اضافه کنید.

    اکنون setMap() را در نمونه overlay فراخوانی کنید و با اضافه کردن موارد زیر به initWebGLOverlayView() در نقشه ارسال کنید:
    webGLOverlayView.setMap(map)
    
  4. با initWebGLOverlayView تماس بگیرید.

    آخرین مرحله اجرای initWebGLOverlayView() با افزودن موارد زیر به تابع بلافاصله فراخوانی شده در پایین app.js :
    initWebGLOverlayView(map);
    

initWebGLOverlayView و تابع بلافاصله فراخوانی شده شما اکنون باید به شکل زیر باشد:

    async function initWebGLOverlayView (map) {
      let scene, renderer, camera, loader;
      const webGLOverlayView = new google.maps.WebGLOverlayView();

      webGLOverlayView.onAdd = () => {}
      webGLOverlayView.onContextRestored = ({gl}) => {}
      webGLOverlayView.onDraw = ({gl, coordinateTransformer}) => {}
      webGLOverlayView.setMap(map);
    }

    (async () => {
      const map = await initMap();
      initWebGLOverlayView(map);
    })();

این تمام چیزی است که برای پیاده سازی WebGLOverlayView نیاز دارید. در مرحله بعد، همه چیزهایی را که برای رندر کردن یک شی سه بعدی روی نقشه نیاز دارید با استفاده از Three.js تنظیم می کنید.

5. یک صحنه three.js تنظیم کنید

استفاده از WebGL می‌تواند بسیار پیچیده باشد، زیرا به شما نیاز دارد که تمام جنبه‌های هر شی را به صورت دستی و سپس برخی از آن‌ها را تعریف کنید. برای آسان‌تر کردن کارها، برای این نرم‌افزار از Three.js، یک کتابخانه گرافیکی محبوب استفاده می‌کنید که یک لایه انتزاعی ساده در بالای WebGL ارائه می‌کند. Three.js با طیف گسترده ای از توابع راحت ارائه می شود که همه کارها را از ایجاد یک رندر WebGL گرفته تا ترسیم اشکال شیء دو بعدی و سه بعدی تا کنترل دوربین ها، تبدیل اشیا و موارد دیگر انجام می دهد.

سه نوع شی اصلی در Three.js وجود دارد که برای نمایش هر چیزی لازم است:

  • صحنه: «کانتینری» که در آن همه اشیا، منابع نور، بافت ها و غیره رندر و نمایش داده می شوند.
  • دوربین: دوربینی که نمای صحنه را نشان می دهد. چندین نوع دوربین موجود است و ممکن است یک یا چند دوربین به یک صحنه اضافه شود.
  • Renderer: یک رندر که پردازش و نمایش تمام اشیاء در صحنه را انجام می دهد. در Three.js، WebGLRenderer متداول‌ترین مورد استفاده است، اما در صورتی که کلاینت از WebGL پشتیبانی نمی‌کند، چند مورد دیگر به عنوان جایگزین در دسترس هستند.

در این مرحله، تمام وابستگی‌های مورد نیاز برای Three.js را بارگیری می‌کنید و یک صحنه اصلی را تنظیم می‌کنید.

  1. سه.js را بارگیری کنید

    برای این کد لبه به دو وابستگی نیاز دارید: کتابخانه Three.js و GLTF Loader، کلاسی که به شما امکان می دهد اشیاء سه بعدی را در قالب GL Trasmission (gLTF) بارگذاری کنید. Three.js بارگذارهای تخصصی را برای بسیاری از فرمت های شی سه بعدی مختلف ارائه می دهد، اما استفاده از gLTF توصیه می شود.

    در کد زیر، کل کتابخانه Three.js وارد شده است. در یک برنامه تولیدی احتمالاً می خواهید فقط کلاس های مورد نیاز خود را وارد کنید، اما برای این کد لبه، کل کتابخانه را وارد کنید تا همه چیز ساده باشد. همچنین توجه داشته باشید که GLTF Loader در کتابخانه پیش‌فرض گنجانده نشده است، و باید از یک مسیر جداگانه در وابستگی وارد شود - این مسیری است که در آن می‌توانید به همه لودرهای ارائه‌شده توسط Three.js دسترسی داشته باشید.

    برای وارد کردن Three.js و GLTF Loader، موارد زیر را به بالای app.js کنید:
    import * as THREE from 'three';
    import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
    
  2. یک صحنه three.js ایجاد کنید.

    برای ایجاد یک صحنه، کلاس Three.js Scene را با اضافه کردن موارد زیر به قلاب onAdd نمونه سازی کنید:
    scene = new THREE.Scene();
    
  3. یک دوربین به صحنه اضافه کنید.

    همانطور که قبلا ذکر شد، دوربین نمایانگر دید صحنه است و تعیین می کند که Three.js چگونه رندر بصری اشیاء را در یک صحنه انجام می دهد. بدون دوربین، صحنه عملاً "دیده" نمی شود، به این معنی که اشیا ظاهر نمی شوند زیرا رندر نمی شوند.

    Three.js انواع دوربین های مختلف را ارائه می دهد که بر نحوه برخورد رندر با اشیاء با توجه به مواردی مانند پرسپکتیو و عمق تأثیر می گذارد. در این صحنه، از PerspectiveCamera ، رایج ترین نوع دوربین در Three.js استفاده می کنید، که برای تقلید از نحوه درک چشم انسان از صحنه طراحی شده است. این بدان معناست که اشیاء دورتر از دوربین کوچکتر از اجسام نزدیکتر به نظر می رسند، صحنه یک نقطه ناپدید خواهد داشت و موارد دیگر.

    برای افزودن یک دوربین پرسپکتیو به صحنه، موارد زیر را به قلاب onAdd اضافه کنید:
    camera = new THREE.PerspectiveCamera();
    
    با PerspectiveCamera ، همچنین می‌توانید ویژگی‌هایی را که دیدگاه را تشکیل می‌دهند، از جمله سطوح نزدیک و دور، نسبت تصویر، و میدان را پیکربندی کنید. بینایی (fov). در مجموع، این ویژگی‌ها چیزی را تشکیل می‌دهند که به عنوان frustum مشاهده شناخته می‌شود، مفهومی مهم که هنگام کار در سه بعدی باید درک کرد، اما خارج از محدوده این نرم‌افزار است. پیکربندی پیش فرض دوربین PerspectiveCamera به خوبی کافی است.
  4. منابع نور را به صحنه اضافه کنید.

    به‌طور پیش‌فرض، اشیایی که در صحنه Three.js ارائه می‌شوند، بدون توجه به بافت‌هایی که روی آن‌ها اعمال می‌شوند، سیاه به نظر می‌رسند. این به این دلیل است که یک صحنه Three.js نحوه عملکرد اجسام در دنیای واقعی را تقلید می کند، جایی که دید رنگ به بازتاب نور از یک شی بستگی دارد. به طور خلاصه، بدون نور، بدون رنگ.

    Three.js انواع مختلفی از چراغ ها را ارائه می دهد که شما از دو مورد از آنها استفاده خواهید کرد:

  5. AmbientLight : منبع نور پراکنده ای را فراهم می کند که به طور یکنواخت همه اشیاء موجود در طرح را از همه زوایا روشن می کند. این به صحنه مقداری نور پایه می‌دهد تا اطمینان حاصل شود که بافت‌های روی همه اشیا قابل مشاهده هستند.
  6. DirectionalLight : نوری را که از جهتی در صحنه نشات می گیرد را ارائه می دهد. برخلاف نحوه عملکرد یک نور موقعیت‌یافته در دنیای واقعی، پرتوهای نوری که از DirectionalLight ساطع می‌شوند همگی موازی هستند و با دورتر شدن از منبع نور پخش و پخش نمی‌شوند.

    می توانید رنگ و شدت هر نور را برای ایجاد جلوه های نوری مجموع پیکربندی کنید. به عنوان مثال، در کد زیر، نور محیط یک نور سفید ملایم را برای کل صحنه فراهم می کند، در حالی که نور جهت دار یک نور ثانویه را ارائه می دهد که با زاویه رو به پایین به اجسام برخورد می کند. در مورد نور جهت، زاویه با استفاده از position.set(x, y ,z) تنظیم می‌شود که در آن هر مقدار نسبت به محور مربوطه است. بنابراین، برای مثال، position.set(0,1,0) نور را مستقیماً بالای صحنه در محور y که مستقیماً به سمت پایین است، قرار می دهد.

    برای افزودن منابع نور به صحنه، موارد زیر را به قلاب onAdd اضافه کنید:
    const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
    scene.add(ambientLight);
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
    directionalLight.position.set(0.5, -1, 0.5);
    scene.add(directionalLight);
    

قلاب onAdd شما اکنون باید به شکل زیر باشد:

    webGLOverlayView.onAdd = () => {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera();
      const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 );
      scene.add(ambientLight);
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
      directionalLight.position.set(0.5, -1, 0.5);
      scene.add(directionalLight);
    }

صحنه شما اکنون تنظیم شده و آماده رندر است. در مرحله بعد، رندر WebGL را پیکربندی کرده و صحنه را رندر خواهید کرد.

6. صحنه را رندر کنید

زمان رندر صحنه شماست تا این مرحله، هر چیزی که با Three.js ایجاد کرده‌اید در کد مقداردهی اولیه می‌شود، اما اساساً وجود ندارد زیرا هنوز در زمینه رندر WebGL رندر نشده است. WebGL محتوای دو بعدی و سه بعدی را در مرورگر با استفاده از Canvas API ارائه می کند. اگر قبلا از Canvas API استفاده کرده اید، احتمالاً با context یک بوم HTML آشنا هستید، جایی که همه چیز در آن رندر می شود. چیزی که ممکن است ندانید این است که این رابطی است که زمینه رندر گرافیکی OpenGL را از طریق WebGLRenderingContext API در مرورگر نشان می دهد.

برای آسان‌تر کردن کار با رندر WebGL، Three.js WebGLRenderer را ارائه می‌کند، پوششی که پیکربندی زمینه رندر WebGL را نسبتاً آسان می‌کند تا Three.js بتواند صحنه‌ها را در مرورگر رندر کند. با این حال، در مورد نقشه، رندر کردن صحنه Three.js در مرورگر در کنار نقشه کافی نیست. Three.js باید دقیقاً در زمینه رندرینگ مشابه نقشه رندر شود، به طوری که هم نقشه و هم هر شیء از صحنه Three.js در یک فضای جهانی رندر شوند. این امکان را برای رندر فراهم می کند تا تعاملات بین اشیاء روی نقشه و اشیاء موجود در صحنه را مدیریت کند، مانند انسداد، که روشی فانتزی برای گفتن اینکه یک شی اشیاء را در پشت خود از دید پنهان می کند، است.

خیلی پیچیده به نظر می رسد، درست است؟ خوشبختانه Three.js دوباره به کمک می آید.

  1. رندر WebGL را راه اندازی کنید.

    وقتی یک نمونه جدید از Three.js WebGLRenderer ، می‌توانید زمینه رندر WebGL خاصی را که می‌خواهید صحنه شما را در آن رندر کند در اختیار آن قرار دهید. آرگومان gl را که به قلاب onContextRestored منتقل می شود، به خاطر دارید؟ آن شی gl ، زمینه رندر WebGL نقشه است. تنها کاری که باید انجام دهید این است که زمینه، بوم و ویژگی های آن را به نمونه WebGLRenderer ، که همه از طریق شی gl در دسترس هستند. در این کد، ویژگی autoClear رندر نیز روی false تنظیم شده است تا رندر خروجی خود را در هر فریم پاک نکند.

    برای پیکربندی رندر، موارد زیر را به قلاب onContextRestored کنید:
    renderer = new THREE.WebGLRenderer({
      canvas: gl.canvas,
      context: gl,
      ...gl.getContextAttributes(),
    });
    renderer.autoClear = false;
    
  2. صحنه را رندر کنید.

    هنگامی که رندر پیکربندی شد، requestRedraw را در نمونه WebGLOverlayView کنید تا به همپوشانی بگویید که در زمان رندر شدن فریم بعدی به یک ترسیم مجدد نیاز است، سپس رندر را در render فراخوانی کنید و صحنه و دوربین Three.js را برای رندر به آن ارسال کنید. در نهایت، وضعیت زمینه رندر WebGL را پاک کنید. این یک گام مهم برای جلوگیری از تداخل وضعیت GL است، زیرا استفاده از WebGL Overlay View به حالت GL مشترک متکی است. اگر وضعیت در پایان هر فراخوانی قرعه کشی بازنشانی نشود، تداخل وضعیت GL ممکن است باعث از کار افتادن رندر شود.

    برای این کار موارد زیر را به قلاب onDraw اضافه کنید تا هر فریم اجرا شود:
    webGLOverlayView.requestRedraw();
    renderer.render(scene, camera);
    renderer.resetState();
    

onContextRestored و onDraw شما اکنون باید شبیه این باشند:

    webGLOverlayView.onContextRestored = ({gl}) => {
      renderer = new THREE.WebGLRenderer({
        canvas: gl.canvas,
        context: gl,
        ...gl.getContextAttributes(),
      });

      renderer.autoClear = false;
    }

    webGLOverlayView.onDraw = ({gl, transformer}) => {
      webGLOverlayView.requestRedraw();
      renderer.render(scene, camera);
      renderer.resetState();
    }

7. یک مدل سه بعدی را روی نقشه رندر کنید

خوب، شما همه قطعات را در جای خود قرار داده اید. شما WebGl Overlay View را راه اندازی کرده اید و یک صحنه Three.js ایجاد کرده اید، اما یک مشکل وجود دارد: چیزی در آن وجود ندارد. بنابراین، در مرحله بعد، زمان رندر کردن یک شی سه بعدی در صحنه است. برای انجام این کار، از GLTF Loader که قبلا وارد کرده اید استفاده می کنید.

مدل‌های سه‌بعدی در فرمت‌های مختلفی ارائه می‌شوند، اما برای Three.js، فرمت gLTF به دلیل اندازه و عملکرد زمان اجرا، قالب ترجیحی است. در این کد لبه، مدلی برای رندر در صحنه از قبل برای شما در /src/pin.gltf ارائه شده است.

  1. یک نمونه لودر مدل ایجاد کنید.

    موارد زیر را به onAdd اضافه کنید:
    loader = new GLTFLoader();
    
  2. یک مدل سه بعدی را بارگذاری کنید.

    لودرهای مدل ناهمزمان هستند و پس از بارگیری کامل مدل، یک تماس برگشتی را اجرا می کنند. برای بارگیری pin.gltf ، موارد زیر را به onAdd اضافه کنید:
    const source = "pin.gltf";
    loader.load(
      source,
      gltf => {}
    );
    
  3. مدل را به صحنه اضافه کنید.

    اکنون می توانید با اضافه کردن موارد زیر به callback loader ، مدل را به صحنه اضافه کنید. توجه داشته باشید که gltf.scene در حال اضافه شدن است، نه gltf :
    scene.add(gltf.scene);
    
  4. ماتریس طرح ریزی دوربین را پیکربندی کنید.

    آخرین چیزی که برای رندر کردن مدل به درستی روی نقشه نیاز دارید، تنظیم ماتریس پروجکشن دوربین در صحنه Three.js است. ماتریس طرح ریزی به عنوان یک آرایه Three.js Matrix4 مشخص می شود که یک نقطه را در فضای سه بعدی همراه با تبدیل هایی مانند چرخش، برش، مقیاس و غیره تعریف می کند.

    در مورد WebGLOverlayView ، ماتریس طرح ریزی برای اینکه به رندرکننده بگوید کجا و چگونه صحنه Three.js را نسبت به نقشه پایه رندر کند استفاده می شود. اما یک مشکل وجود دارد مکان ها روی نقشه به عنوان جفت مختصات طول و عرض جغرافیایی مشخص می شوند، در حالی که مکان ها در صحنه Three.js مختصات Vector3 هستند. همانطور که ممکن است حدس بزنید، محاسبه تبدیل بین دو سیستم بی اهمیت نیست. برای حل این مشکل، WebGLOverlayView یک شیء coordinateTransformer Transformer را به قلاب چرخه حیات OnDraw می کند که حاوی تابعی به نام fromLatLngAltitude است. fromLatLngAltitude یک شی LatLngAltitude یا LatLngAltitudeLiteral را می گیرد و به صورت اختیاری مجموعه ای از آرگومان ها را که تبدیلی را برای صحنه تعریف می کنند، می گیرد، سپس آنها را در یک ماتریس طرح نمای مدل (MVP) برای شما پنهان می کند. تنها کاری که باید انجام دهید این است که مشخص کنید در کجای نقشه می خواهید صحنه Three.js رندر شود و همچنین نحوه تبدیل آن را مشخص کنید و WebGLOverlayView بقیه کارها را انجام می دهد. سپس می توانید ماتریس MVP را به یک آرایه Three.js Matrix4 تبدیل کنید و ماتریس نمایش دوربین را روی آن تنظیم کنید.

    در کد زیر، آرگومان دوم به WebGl Overlay View می‌گوید که ارتفاع صحنه Three.js را 120 متر بالاتر از سطح زمین قرار دهد، که باعث می‌شود مدل شناور به نظر برسد.

    برای تنظیم ماتریس نمایش دوربین، موارد زیر را به قلاب onDraw کنید:
    const latLngAltitudeLiteral = {
        lat: mapOptions.center.lat,
        lng: mapOptions.center.lng,
        altitude: 120
    }
    const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
    camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);
    
  5. مدل را تبدیل کنید.

    متوجه خواهید شد که پین ​​عمود بر نقشه قرار ندارد. در گرافیک سه بعدی، علاوه بر اینکه فضای جهان دارای محورهای x، y و z خاص خود است که جهت گیری را تعیین می کند، هر جسم نیز فضای شی خاص خود را با مجموعه ای از محورهای مستقل دارد.

    در مورد این مدل، با چیزی که ما معمولاً "بالای" پین را رو به محور y در نظر می گیریم، ایجاد نشده است، بنابراین باید شی را تغییر دهید تا آن را در جهت دلخواه نسبت به فضای جهان با استفاده از فراخوانی rotation.set روی آن. توجه داشته باشید که در Three.js چرخش بر حسب رادیان مشخص شده است نه درجه. به طور کلی فکر کردن به درجه آسان‌تر است، بنابراین تبدیل مناسب باید با استفاده از فرمول degrees * Math.PI/180 انجام شود.

    علاوه بر این، مدل کمی کوچک است، بنابراین با فراخوانی scale.set(x, y ,z) آن را در همه محورها به طور یکنواخت مقیاس خواهید کرد.

    برای چرخاندن و مقیاس‌بندی مدل، موارد زیر را در callback loader onAdd قبل از scene.add(gltf.scene) اضافه کنید که gLTF را به صحنه اضافه می‌کند:
    gltf.scene.scale.set(25,25,25);
    gltf.scene.rotation.x = 180 * Math.PI/180;
    

اکنون پین نسبت به نقشه به صورت عمودی قرار می گیرد.

پین عمودی

قلاب های onAdd و onDraw شما اکنون باید به این شکل باشند:

    webGLOverlayView.onAdd = () => {
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera();
      const ambientLight = new THREE.AmbientLight( 0xffffff, 0.75 ); // soft white light
      scene.add( ambientLight );
      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
      directionalLight.position.set(0.5, -1, 0.5);
      scene.add(directionalLight);

      loader = new GLTFLoader();
      const source = 'pin.gltf';
      loader.load(
        source,
        gltf => {
          gltf.scene.scale.set(25,25,25);
          gltf.scene.rotation.x = 180 * Math.PI/180;
          scene.add(gltf.scene);
        }
      );
    }

    webGLOverlayView.onDraw = ({gl, transformer}) => {
      const latLngAltitudeLiteral = {
        lat: mapOptions.center.lat,
        lng: mapOptions.center.lng,
        altitude: 100
      }

      const matrix = transformer.fromLatLngAltitude(latLngAltitudeLiteral);
      camera.projectionMatrix = new THREE.Matrix4().fromArray(matrix);

      webGLOverlayView.requestRedraw();
      renderer.render(scene, camera);
      renderer.resetState();
    }

بعدی انیمیشن های دوربین است!

8. دوربین را متحرک کنید

اکنون که یک مدل را روی نقشه رندر کرده اید و می توانید همه چیز را به صورت سه بعدی حرکت دهید، کار بعدی که احتمالاً می خواهید انجام دهید این است که آن حرکت را به صورت برنامه ای کنترل کنید. عملکرد moveCamera به شما این امکان را می‌دهد که ویژگی‌های مرکز، بزرگ‌نمایی، شیب و عنوان نقشه را به طور همزمان تنظیم کنید و به شما کنترل دانه‌ریزی بر تجربه کاربر را می‌دهد. علاوه بر این، moveCamera را می توان در یک حلقه انیمیشن فراخوانی کرد تا انتقال سیال بین فریم ها را با نرخ فریم نزدیک به 60 فریم در ثانیه ایجاد کند.

  1. صبر کنید تا مدل بارگذاری شود.

    برای ایجاد یک تجربه کاربری یکپارچه، باید منتظر بمانید تا دوربین شروع به حرکت کنید تا زمانی که مدل gLTF بارگیری شود. برای انجام این کار، کنترل کننده رویداد onLoad بارکننده را به قلاب onContextRestored کنید:
    loader.manager.onLoad = () => {}
    
  2. یک حلقه انیمیشن ایجاد کنید.

    بیش از یک راه برای ایجاد یک حلقه انیمیشن وجود دارد، مانند استفاده از setInterval یا requestAnimationFrame . در این مورد، از تابع setAnimationLoop رندر Three.js استفاده خواهید کرد که هر بار که Three.js یک فریم جدید را ارائه می دهد، به طور خودکار هر کدی را که در callback خود اعلام می کنید، فراخوانی می کند. برای ایجاد حلقه انیمیشن، موارد زیر را در مرحله قبل به کنترل کننده رویداد onLoad اضافه کنید:
    renderer.setAnimationLoop(() => {});
    
  3. موقعیت دوربین را در حلقه انیمیشن تنظیم کنید.

    در مرحله بعد، با moveCamera تماس بگیرید تا نقشه به روز شود. در اینجا، ویژگی‌های شی mapOptions که برای بارگیری نقشه استفاده می‌شود، برای تعیین موقعیت دوربین استفاده می‌شود:
    map.moveCamera({
      "tilt": mapOptions.tilt,
      "heading": mapOptions.heading,
      "zoom": mapOptions.zoom
    });
    
  4. دوربین را در هر فریم به روز کنید.

    آخرین مرحله! شی mapOptions را در انتهای هر فریم به روز کنید تا موقعیت دوربین را برای فریم بعدی تنظیم کنید. در این کد، از دستور if برای افزایش شیب تا رسیدن به حداکثر مقدار شیب 67.5 استفاده می شود، سپس عنوان هر فریم کمی تغییر می کند تا دوربین یک چرخش کامل 360 درجه را انجام دهد. پس از تکمیل انیمیشن مورد نظر، null به setAnimationLoop می شود تا انیمیشن را لغو کند تا برای همیشه اجرا نشود.
    if (mapOptions.tilt < 67.5) {
      mapOptions.tilt += 0.5
    } else if (mapOptions.heading <= 360) {
      mapOptions.heading += 0.2;
    } else {
      renderer.setAnimationLoop(null)
    }
    

قلاب onContextRestored شما اکنون باید به شکل زیر باشد:

    webGLOverlayView.onContextRestored = ({gl}) => {
      renderer = new THREE.WebGLRenderer({
        canvas: gl.canvas,
        context: gl,
        ...gl.getContextAttributes(),
      });

      renderer.autoClear = false;

      loader.manager.onLoad = () => {
        renderer.setAnimationLoop(() => {
           map.moveCamera({
            "tilt": mapOptions.tilt,
            "heading": mapOptions.heading,
            "zoom": mapOptions.zoom
          });

          if (mapOptions.tilt < 67.5) {
            mapOptions.tilt += 0.5
          } else if (mapOptions.heading <= 360) {
            mapOptions.heading += 0.2;
          } else {
            renderer.setAnimationLoop(null)
          }
        });
      }
    }

9. تبریک می گویم

اگر همه چیز طبق برنامه پیش رفت، اکنون باید نقشه ای با یک پین سه بعدی بزرگ داشته باشید که به شکل زیر است:

پین سه بعدی نهایی

چیزی که یاد گرفتی

در این Codelab شما یک سری چیزها را یاد گرفتید. در اینجا نکات برجسته وجود دارد:

  • پیاده سازی WebGLOverlayView و قلاب های چرخه حیات آن.
  • ادغام Three.js در نقشه
  • اصول اولیه ایجاد یک صحنه Three.js، از جمله دوربین و نور.
  • بارگذاری و دستکاری مدل های سه بعدی با استفاده از Three.js.
  • کنترل و متحرک سازی دوربین برای نقشه با استفاده از moveCamera .

بعدش چی؟

WebGL، و گرافیک کامپیوتری به طور کلی، یک موضوع پیچیده است، بنابراین همیشه چیزهای زیادی برای یادگیری وجود دارد. در اینجا چند منبع برای شروع شما آورده شده است: