Membuat Lightsaber dengan Polimer

Screenshot Lightsaber

Ringkasan

Cara kami menggunakan Polymer untuk membuat Lightsaber berperforma tinggi yang dikontrol seluler WebGL yang modular dan dapat dikonfigurasi. Kami meninjau beberapa detail utama project kami https://lightsaber.withgoogle.com/ untuk membantu Anda menghemat waktu saat membuat lightsaber Anda sendiri saat Anda bertemu dengan sekelompok Stormtrooper yang marah.

Ringkasan

Jika Anda bertanya-tanya apa yang kami pikirkan tentang Polimer atau {i>WebComponent<i} adalah langkah terbaik untuk memulai dengan membagikan ekstrak dari proyek kerja yang sebenarnya. Berikut ini adalah contoh yang diambil dari halaman muka proyek kami https://lightsaber.withgoogle.com. Penting file HTML biasa tetapi memiliki beberapa keajaiban di dalamnya:

<!-- Element-->
<dom-module id="sw-page-landing">
    <!-- Template-->
    <template>
    <style>
        <!-- include elements/sw/pages/sw-page-landing/styles/sw-page-landing.css-->
    </style>
    <div class="centered content">
        <sw-ui-logo></sw-ui-logo>
        <div class="connection-url-wrapper">
        <sw-t key="landing.type" class="type"></sw-t>
        <div id="url" class="connection-url">.</div>
        <sw-ui-toast></sw-ui-toast>
        </div>
    </div>
    <div class="disclaimer epilepsy">
        <sw-t key="disclaimer.epilepsy" class="type"></sw-t>
    </div>
    <sw-ui-footer state="extended"></sw-ui-footer>
    </template>
    <!-- Polymer element script-->
    <script src="scripts/sw-page-landing.js"></script>
</dom-module>

Jadi ada banyak pilihan di luar sana saat Anda ingin membuat aplikasi berbasis HTML5. API, Framework, Library, Game Engine, dll. Terlepas dari semua pilihan yang ada, sulit untuk mendapatkan konfigurasi yang baik antara kontrol atas performa grafis tinggi dan modular bersih struktur dan skalabilitas. Kami menemukan bahwa Polymer bisa membantu kami menjaga proyek terorganisir sambil tetap memungkinkan kinerja tingkat rendah pengoptimalan, dan kami dengan cermat merancang cara memecah proyek menjadi komponen untuk memanfaatkan kemampuan Polymer sebaik mungkin.

Modularitas dengan Polimer

Polymer adalah library yang memungkinkan banyak kekuatan atas bagaimana proyek Anda dibangun dari elemen khusus yang dapat digunakan kembali. Modul ini memungkinkan Anda menggunakan modul yang berfungsi penuh dan mandiri yang terdapat dalam sebuah {i>file<i} HTML tunggal. Mereka tidak hanya berisi struktur (markup HTML) tetapi juga gaya dan logika inline.

Lihat contoh di bawah:

<link rel="import" href="bower_components/polymer/polymer.html">

<dom-module id="picture-frame">
    <template>
    <!-- scoped CSS for this element -->
    <style>
        div {
        display: inline-block;
        background-color: #ccc;
        border-radius: 8px;
        padding: 4px;
        }
    </style>
    <div>
        <!-- any children are rendered here -->
        <content></content>
    </div>
    </template>

    <script>
    Polymer({
        is: "picture-frame",
    });
    </script>
</dom-module>

Tetapi pada proyek yang lebih besar, mungkin akan membantu untuk memisahkan ketiga (HTML, CSS, JS) dan hanya menggabungkannya pada waktu kompilasi. Jadi, satu hal yang kami lakukan adalah memberi setiap elemen dalam project folder terpisahnya sendiri:

src/elements/
|-- elements.jade
`-- sw
    |-- debug
    |   |-- sw-debug
    |   |-- sw-debug-performance
    |   |-- sw-debug-version
    |   `-- sw-debug-webgl
    |-- experience
    |   |-- effects
    |   |-- sw-experience
    |   |-- sw-experience-controller
    |   |-- sw-experience-engine
    |   |-- sw-experience-input
    |   |-- sw-experience-model
    |   |-- sw-experience-postprocessor
    |   |-- sw-experience-renderer
    |   |-- sw-experience-state
    |   `-- sw-timer
    |-- input
    |   |-- sw-input-keyboard
    |   `-- sw-input-remote
    |-- pages
    |   |-- sw-page-calibration
    |   |-- sw-page-connection
    |   |-- sw-page-connection-error
    |   |-- sw-page-error
    |   |-- sw-page-experience
    |   `-- sw-page-landing
    |-- sw-app
    |   |-- bower.json
    |   |-- scripts
    |   |-- styles
    |   `-- sw-app.jade
    |-- system
    |   |-- sw-routing
    |   |-- sw-system
    |   |-- sw-system-audio
    |   |-- sw-system-config
    |   |-- sw-system-environment
    |   |-- sw-system-events
    |   |-- sw-system-remote
    |   |-- sw-system-social
    |   |-- sw-system-tracking
    |   |-- sw-system-version
    |   |-- sw-system-webrtc
    |   `-- sw-system-websocket
    |-- ui
    |   |-- experience
    |   |-- sw-preloader
    |   |-- sw-sound
    |   |-- sw-ui-button
    |   |-- sw-ui-calibration
    |   |-- sw-ui-disconnected
    |   |-- sw-ui-final
    |   |-- sw-ui-footer
    |   |-- sw-ui-help
    |   |-- sw-ui-language
    |   |-- sw-ui-logo
    |   |-- sw-ui-mask
    |   |-- sw-ui-menu
    |   |-- sw-ui-overlay
    |   |-- sw-ui-quality
    |   |-- sw-ui-select
    |   |-- sw-ui-toast
    |   |-- sw-ui-toggle-screen
    |   `-- sw-ui-volume
    `-- utils
        `-- sw-t

Selain itu, folder setiap elemen memiliki struktur internal yang sama dengan direktori dan file terpisah untuk logika (file coffee), gaya (file scss), dan template (file jade).

Berikut adalah contoh elemen sw-ui-logo:

sw-ui-logo/
|-- bower.json
|-- scripts
|   `-- sw-ui-logo.coffee
|-- styles
|   `-- sw-ui-logo.scss
`-- sw-ui-logo.jade

Dan jika Anda melihat ke dalam file .jade:

// Element
dom-module(id='sw-ui-logo')

    // Template
    template
    style
        include elements/sw/ui/sw-ui-logo/styles/sw-ui-logo.css

    img(src='[[url]]')

    // Polymer element script
    script(src='scripts/sw-ui-logo.js')

Anda dapat melihat cara mengaturnya dengan rapi dengan menyertakan gaya dan logika dari file terpisah. Untuk menyertakan gaya dalam elemen Polymer, kita menggunakan pernyataan include Jade, sehingga kita memiliki konten file CSS inline yang sebenarnya setelah kompilasi. Elemen skrip sw-ui-logo.js akan dieksekusi pada runtime.

Dependensi Modular dengan Bower

Biasanya, kita menyimpan library dan dependensi lainnya di level project. Namun dalam penyiapan di atas, Anda akan melihat bower.json yang berada dalam folder elemen: dependensi tingkat elemen. Ide di balik pendekatan ini adalah dalam situasi di mana Anda memiliki banyak elemen dengan dependensi, kita dapat memastikan untuk memuat hanya dependensi yang digunakan. Dan jika Anda menghapus suatu elemen, Anda tidak perlu mengingat menghapus dependensinya karena Anda juga akan menghapus file bower.json yang mendeklarasikan dependensi ini. Setiap elemen secara independen memuat dependensi yang terkait dengannya.

Namun, untuk menghindari duplikasi dependensi, kami menyertakan file .bowerrc di setiap {i>folder <i}elemen. Ini memberi tahu bower di mana harus menyimpan dependensi sehingga kita dapat memastikan bahwa hanya ada satu di akhir direktori:

{
    "directory" : "../../../../../bower_components"
}

Dengan cara ini jika beberapa elemen mendeklarasikan THREE.js sebagai dependensi, sekali bower menginstalnya untuk elemen pertama dan mulai menguraikan yang kedua, aplikasi akan menyadari bahwa dependensi ini telah terinstal dan tidak akan mengunduh ulang atau menggandakannya. Demikian pula, hal ini akan membuat dependensi itu selama ada setidaknya satu elemen yang masih mendefinisikannya di bower.json-nya.

Skrip bash menemukan semua file bower.json dalam struktur elemen bertingkat. Kemudian, skrip tersebut memasuki direktori ini satu per satu dan mengeksekusi bower install di setiap elemen tersebut:

echo installing bower components...
modules=$(find /vagrant/app -type f -name "bower.json" -not -path "*node_modules*" -not -path "*bower_components*")
for module in $modules; do
    pushd $(dirname $module)
    bower install --allow-root -q
    popd
done

Template Elemen Baru Cepat

Butuh sedikit waktu setiap kali Anda ingin membuat elemen baru: menghasilkan folder dan struktur file dasar dengan nama yang benar. Kita gunakan Slush untuk menulis generator elemen sederhana.

Anda dapat memanggil skrip dari command line:

$ slush element path/to/your/element-name

Dan elemen baru telah dibuat, termasuk semua struktur dan konten file.

Kami menetapkan template untuk file elemen, mis. template file .jade terlihat seperti berikut:

// Element
dom-module(id='<%= name %>')

    // Template
    template
    style
        include elements/<%= path %>/styles/<%= name %>.css

    span This is a '<%= name %>' element.

    // Polymer element script
    script(src='scripts/<%= name %>.js')

Generator Slush mengganti variabel dengan jalur dan nama elemen yang sebenarnya.

Menggunakan Gulp untuk Membangun Elemen

Gulp menjaga proses build tetap terkontrol. Dan dalam struktur kita, untuk membangun elemen yang kita butuhkan Gulp untuk mengikuti langkah-langkah berikut:

  1. Mengompilasi file .coffee elemen ke .js
  2. Mengompilasi elemen .scss file ke .css
  3. Mengompilasi elemen .jade file ke .html, menyematkan file .css.

Secara lebih detail:

Mengompilasi elemen .coffee file ke .js

gulp.task('elements-coffee', function () {
    return gulp.src(abs(config.paths.app + '/elements/**/*.coffee'))
    .pipe($.replaceTask({
        patterns: [{json: getVersionData()}]
    }))
    .pipe($.changed(abs(config.paths.static + '/elements'), {extension: '.js'}))
    .pipe($.coffeelint())
    .pipe($.coffeelint.reporter())
    .pipe($.sourcemaps.init())
    .pipe($.coffee({
    }))
    .on('error', gutil.log)
    .pipe($.sourcemaps.write())
    .pipe(gulp.dest(abs(config.paths.static + '/elements')));
});

Untuk langkah 2 dan 3, kita menggunakan gulp dan plugin kompas untuk mengompilasi scss ke .css dan .jade ke .html, dengan pendekatan yang serupa dengan 2 di atas.

Termasuk Elemen Polimer

Untuk benar-benar menyertakan elemen Polymer, kita menggunakan impor HTML.

<link rel="import" href="elements.html">

<!-- Polymer -->
<link rel="import" href="../bower_components/polymer/polymer.html">

<!-- Custom elements -->
<link rel="import" href="sw/sw-app/sw-app.html">
<link rel="import" href="sw/system/sw-system/sw-system.html">
<link rel="import" href="sw/system/sw-routing/sw-routing.html">
<link rel="import" href="sw/system/sw-system-version/sw-system-version.html">
<link rel="import" href="sw/system/sw-system-environment/sw-system-environment.html">
<link rel="import" href="sw/pages/sw-page-landing/sw-page-landing.html">
<link rel="import" href="sw/pages/sw-page-connection/sw-page-connection.html">
<link rel="import" href="sw/pages/sw-page-calibration/sw-page-calibration.html">
<link rel="import" href="sw/pages/sw-page-experience/sw-page-experience.html">
<link rel="import" href="sw/ui/sw-preloader/sw-preloader.html">
<link rel="import" href="sw/ui/sw-ui-overlay/sw-ui-overlay.html">
<link rel="import" href="sw/ui/sw-ui-button/sw-ui-button.html">
<link rel="import" href="sw/ui/sw-ui-menu/sw-ui-menu.html">

Mengoptimalkan elemen Polimer untuk produksi

Sebuah proyek besar bisa memiliki banyak elemen Polimer. Di proyek, kita punya lebih dari lima puluh. Jika Anda menganggap setiap elemen memiliki file .js terpisah dan beberapa yang memiliki library yang direferensikan, file tersebut menjadi lebih dari 100 file terpisah. Ini berarti banyak permintaan yang perlu dibuat oleh browser, mengakibatkan penurunan performa. Mirip dengan proses penggabungan dan minifikasi yang kita akan berlaku untuk build Angular, kita "vulcanize" project Polymer di tujuan akhir untuk produksi.

Vulcanize adalah alat Polymer yang meratakan hierarki dependensi menjadi satu file html, sehingga mengurangi jumlah permintaan. Ini sangat bagus untuk browser yang tidak mendukung komponen web secara native.

CSP (Kebijakan Keamanan Konten) dan Polimer

Saat mengembangkan aplikasi web yang aman, Anda perlu mengimplementasikan CSP. CSP adalah serangkaian aturan yang mencegah serangan pembuatan skrip lintas situs (XSS): eksekusi skrip dari sumber yang tidak aman, atau mengeksekusi skrip inline dari file HTML.

Sekarang file .html yang dioptimalkan, digabungkan, dan diminifikasi telah dibuat oleh Vulcanize memiliki semua kode JavaScript sebaris di non-CSP format font. Untuk mengatasinya, kami menggunakan alat yang disebut Crisper.

Crisper memisahkan skrip inline dari file HTML dan memasukkannya ke dalam satu file JavaScript eksternal untuk kepatuhan CSP. Jadi, kita teruskan objek vulkanik melalui Crisper dan berakhir dengan dua file: elements.html dan elements.js. Di dalam elements.html, kode ini juga menangani pemuatan elements.js yang dihasilkan.

Struktur Logis Aplikasi

Di Polymer, elemen dapat berupa apa saja, mulai dari utilitas non-visual hingga elemen UI yang kecil, mandiri, dan dapat digunakan kembali (seperti tombol) hingga modul yang lebih besar seperti "halaman" dan bahkan menyusun aplikasi lengkap.

Struktur logis tingkat atas aplikasi
Struktur logis tingkat atas dari aplikasi yang direpresentasikan dengan Elemen polimer.

Pascapemrosesan dengan Arsitektur Induk-turunan dan Polymer

Dalam setiap pipeline grafis 3D, selalu ada langkah terakhir di mana efek ditambahkan di atas seluruh gambar sebagai semacam overlay. Ini adalah pascapemrosesan, dan melibatkan efek seperti cahaya, sinar dewa, kedalaman bidang, bokeh, buram, dll. Efek digabungkan dan diterapkan ke elemen yang berbeda sesuai dengan bagaimana adegan dibangun. Di THREE.js, kita dapat membuat shader kustom untuk pascapemrosesan di JavaScript atau kita bisa melakukannya dengan Polymer, Berkat struktur induk-turunannya.

Jika Anda melihat kode HTML elemen pasca-pemroses kami:

<dom-module id="sw-experience-postprocessor">
    <!-- Template-->
    <template>
    <sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
    <sw-experience-effect-dof class="effect"></sw-experience-effect-dof>
    <sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
    </template>
    <!-- Polymer element script-->
    <script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>

Kita menetapkan efek sebagai elemen Polimer bertingkat pada class yang sama. Kemudian, di sw-experience-postprocessor.js kita melakukan hal ini:

effects = @querySelectorAll '.effect'
@composer.addPass effect.getPass() for effect in effects

Kita menggunakan fitur HTML dan querySelectorAll JavaScript untuk menemukan semua efek yang disarangkan sebagai elemen HTML di dalam {i>post processor<i}, sesuai urutan tempat token tersebut ditentukan. Kemudian, kita akan melakukan iterasi dan menambahkannya ke composer.

Sekarang, katakanlah kita ingin menghapus efek DOF (Depth of Field) dan mengubah urutan efek mekar dan vinyet. Yang perlu kita lakukan hanyalah mengedit definisi pasca-prosesor menjadi sesuatu seperti:

<dom-module id="sw-experience-postprocessor">
    <!-- Template-->
    <template>
    <sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
    <sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
    </template>
    <!-- Polymer element script-->
    <script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>

dan tampilan akan berjalan, tanpa mengubah satu baris kode yang sebenarnya.

Loop render dan loop update di Polymer

Dengan Polymer, kami juga dapat melakukan rendering dan update mesin dengan elegan. Kita membuat elemen timer yang menggunakan requestAnimationFrame dan menghitung nilai seperti waktu saat ini (t) dan waktu delta - waktu yang berlalu dari frame terakhir (dt):

Polymer
    is: 'sw-timer'

    properties:
    t:
        type: Number
        value: 0
        readOnly: true
        notify: true
    dt:
        type: Number
        value: 0
        readOnly: true
        notify: true

    _isRunning: false
    _lastFrameTime: 0

    ready: ->
    @_isRunning = true
    @_update()

    _update: ->
    if !@_isRunning then return
    requestAnimationFrame => @_update()
    currentTime = @_getCurrentTime()
    @_setT currentTime
    @_setDt currentTime - @_lastFrameTime
    @_lastFrameTime = @_getCurrentTime()

    _getCurrentTime: ->
    if window.performance then performance.now() else new Date().getTime()

Kemudian, kita menggunakan data binding untuk mengikat properti t dan dt ke mesin (experience.jade):

sw-timer(
    t='{ % templatetag openvariable % }t}}',
    dt='{ % templatetag openvariable % }dt}}'
)

sw-experience-engine(
    t='[t]',
    dt='[dt]'
)

Selain itu, kita memproses perubahan t dan dt di mesin dan setiap kali nilai berubah, fungsi _update akan dipanggil:

Polymer
    is: 'sw-experience-engine'

    properties:
    t:
        type: Number

    dt:
        type: Number

    observers: [
    '_update(t)'
    ]

    _update: (t) ->
    dt = @dt
    @_physics.update dt, t
    @_renderer.render dt, t

Jika ingin FPS, Anda dapat menghapus data Polymer binding dalam loop render untuk menghemat beberapa milidetik yang diperlukan untuk memberi tahu elemen tentang perubahan. Kami menerapkan observer kustom sebagai berikut:

sw-timer.coffee:

addUpdateListener: (listener) ->
    if @_updateListeners.indexOf(listener) == -1
    @_updateListeners.push listener
    return

removeUpdateListener: (listener) ->
    index = @_updateListeners.indexOf listener
    if index != -1
    @_updateListeners.splice index, 1
    return

_update: ->
    # ...
    for listener in @_updateListeners
        listener @dt, @t
    # ...

Fungsi addUpdateListener menerima callback dan menyimpannya di . Kemudian, dalam loop update, kita mengulangi setiap callback dan kita menjalankannya dengan argumen dt dan t secara langsung, mengabaikan data binding atau pengaktifan peristiwa. Setelah callback tidak lagi dimaksudkan untuk aktif, kita menambahkan Fungsi removeUpdateListener yang memungkinkan Anda menghapus callback yang ditambahkan sebelumnya.

Lightsaber di THREE.js

THREE.js mengabstraksikan detail WebGL tingkat rendah dan memungkinkan kami untuk fokus menyelesaikan masalah. Dan masalah kita adalah melawan Stormtrooper dan kita memerlukan senjata. Mari kita buat lightsaber.

Pisau yang berkilauan adalah yang membedakan lightsaber dari yang lain senjata dua tangan. Jalur ini utamanya terdiri dari dua bagian: balok dan jalur yang terlihat saat memindahkannya. Kami membuatnya dengan bentuk silinder berwarna cerah dan jalur dinamis yang mengikutinya saat pemain bergerak.

The Blade

Pisau terdiri dari dua sub-blade. Bagian dalam dan luar. Keduanya adalah THREE.js mesh dengan materialnya masing-masing.

Pisau Dalam

Untuk pisau bagian dalam, kita menggunakan material kustom dengan shader kustom. Kita mengambil garis yang dibuat oleh dua titik dan memproyeksikan garis di antara dua titik ini pada bidang. Pada dasarnya, bidang ini adalah apa yang Anda kontrol saat bertarung dengan ponsel Anda, ini memberikan kesan kedalaman dan orientasi dengan pedang itu.

Untuk menciptakan nuansa objek bulat yang bercahaya, kita melihat jarak titik ortogonal dari titik mana pun pada bidang dari garis utama yang menghubungkan dua titik A dan B seperti di bawah. Semakin dekat titik ke sumbu utama akan semakin terang.

Glow bilah dalam

Sumber di bawah ini menunjukkan cara kami menghitung vFactor untuk mengontrol intensitas dalam shader verteks, lalu menggunakannya untuk menyatu dengan shader fragmen.

THREE.LaserShader = {

    uniforms: {
    "uPointA": {type: "v3", value: new THREE.Vector3(0, -1, 0)},
    "uPointB": {type: "v3", value: new THREE.Vector3(0, 1, 0)},
    "uColor": {type: "c", value: new THREE.Color(1, 0, 0)},
    "uMultiplier": {type: "f", value: 3.0},
    "uCoreColor": {type: "c", value: new THREE.Color(1, 1, 1)},
    "uCoreOpacity": {type: "f", value: 0.8},
    "uLowerBound": {type: "f", value: 0.4},
    "uUpperBound": {type: "f", value: 0.8},
    "uTransitionPower": {type: "f", value: 2},
    "uNearPlaneValue": {type: "f", value: -0.01}
    },

    vertexShader: [

    "uniform vec3 uPointA;",
    "uniform vec3 uPointB;",
    "uniform float uMultiplier;",
    "uniform float uNearPlaneValue;",
    "varying float vFactor;",

    "float getDistanceFromAB(vec2 a, vec2 b, vec2 p) {",

        "vec2 l = b - a;",
        "float l2 = dot( l, l );",
        "float t = dot( p - a, l ) / l2;",
        "if( t < 0.0 ) return distance( p, a );",
        "if( t > 1.0 ) return distance( p, b );",
        "vec2 projection = a + (l * t);",
        "return distance( p, projection );",

    "}",

    "vec3 getIntersection(vec4 a, vec4 b) {",

        "vec3 p = a.xyz;",
        "vec3 q = b.xyz;",
        "vec3 v = normalize( q - p );",
        "float t = ( uNearPlaneValue - p.z ) / v.z;",
        "return p + (v * t);",

    "}",

    "void main() {",

        "vec4 a = modelViewMatrix * vec4(uPointA, 1.0);",
        "vec4 b = modelViewMatrix * vec4(uPointB, 1.0);",
        "if(a.z > uNearPlaneValue) a.xyz = getIntersection(a, b);",
        "if(b.z > uNearPlaneValue) b.xyz = getIntersection(a, b);",
        "a = projectionMatrix * a; a /= a.w;",
        "b = projectionMatrix * b; b /= b.w;",
        "vec4 p = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
        "gl_Position = p;",
        "p /= p.w;",
        "float d = getDistanceFromAB(a.xy, b.xy, p.xy) * gl_Position.z;",
        "vFactor = 1.0 - clamp(uMultiplier * d, 0.0, 1.0);",

    "}"

    ].join( "\n" ),

    fragmentShader: [

    "uniform vec3 uColor;",
    "uniform vec3 uCoreColor;",
    "uniform float uCoreOpacity;",
    "uniform float uLowerBound;",
    "uniform float uUpperBound;",
    "uniform float uTransitionPower;",
    "varying float vFactor;",

    "void main() {",

        "vec4 col = vec4(uColor, vFactor);",
        "float factor = smoothstep(uLowerBound, uUpperBound, vFactor);",
        "factor = pow(factor, uTransitionPower);",
        "vec4 coreCol = vec4(uCoreColor, uCoreOpacity);",
        "vec4 finalCol = mix(col, coreCol, factor);",
        "gl_FragColor = finalCol;",

    "}"

    ].join( "\n" )

};

Kilau Bilah Luar

Untuk glow luar, kita merender ke renderbuffer terpisah dan menggunakan efek mekar pasca-pemrosesan ini dan menyatu dengan gambar akhir untuk mendapatkan kilau yang diinginkan. Gambar di bawah ini menunjukkan tiga wilayah berbeda yang Anda jika Anda menginginkan pedang yang layak. Yaitu inti putih, bagian tengah cahaya biru dan cahaya luar.

Bilah luar

Jalan Setapak Lightsaber

Jejak lightsaber adalah kunci efek penuh seperti aslinya dalam serial Star Wars. Kami membuat jalan setapak dengan penggemar segitiga yang dibuat secara dinamis berdasarkan gerakan lightsaber. Penggemar ini kemudian diteruskan ke pascaprosesor untuk peningkatan visual lebih lanjut. Untuk membuat geometri kipas, kita memiliki segmen garis dan berdasarkan transformasi sebelumnya dan transformasi saat ini, kita membuat segitiga baru di mesh, yang menghapus bagian ekor setelah panjang tertentu.

Jalur Lightsaber kiri
Jalur Lightsaber di kanan

Setelah memiliki {i>mesh<i}, kita menetapkan materi sederhana untuknya, dan meneruskannya ke pascaprosesor untuk menciptakan efek yang mulus. Kita menggunakan efek mekaran yang sama yang kita menerapkan kilau bilah luar dan mendapatkan jejak yang mulus seperti yang dapat Anda lihat:

Jalur lengkap

Berpendar di sekitar jalan setapak

Agar hasil akhir benar-benar lengkap, kami harus menangani cahaya di sekitar {i>track<i}, yang dapat dibuat dengan berbagai cara. Solusi yang kami jangan bahas secara mendetail, karena alasan performa adalah membuat untuk buffer ini yang menghasilkan tepi halus di sekitar penjepit pada renderbuffer. Kemudian, kita menggabungkan output ini dalam render akhir, di sini Anda dapat melihat cahaya yang mengelilingi jejak:

Jalan setapak bercahaya

Kesimpulan

Polymer adalah library dan konsep yang canggih (sama seperti WebComponents secara umum). Anda bebas menentukan cara untuk menggunakannya. Data bisa apa saja, tombol UI sederhana ke aplikasi WebGL berukuran penuh. Di bab sebelumnya kami telah menunjukkan beberapa tips dan trik cara menggunakan Polymer secara efisien dalam produksi dan cara menyusun modul yang lebih kompleks yang juga berfungsi ya. Kami juga menunjukkan cara mendapatkan lightsaber yang tampak bagus di WebGL. Jadi jika Anda menggabungkan semua itu, ingatlah untuk Vulcanize elemen Polymer sebelum men-deploy ke server produksi dan jika Anda tidak lupa menggunakan Crisper jika Anda ingin tetap mematuhi CSP, semoga Anda berhasil.

Gameplay