पॉलिमर के साथ शमशेर-ए-रोशनी बनाना

Lightsaber का स्क्रीनशॉट

खास जानकारी

हमने Polymer का इस्तेमाल करके, बेहतर परफ़ॉर्म करने वाला WebGL मोबाइल कंट्रोल कैसे बनाया लाइटसेबर जिसे मॉड्यूलर और कॉन्फ़िगर किया जा सकता है. हम कुछ अहम जानकारी की समीक्षा करते हैं https://lightsaber.withgoogle.com/ पर जाकर ताकि अगली बार आपको गेम का पैकेट बनाने के दौरान, अपना समय बचाने में मदद मिल सके गुस्सैल स्टॉर्मट्रूपर.

खास जानकारी

अगर आपको यह जानना है कि पॉलीमर या वेबकॉम्पोनेंट क्या है, तो तो सबसे अच्छा होगा कि काम करने वाले किसी असल प्रोजेक्ट का डेटा शेयर करके शुरुआत करें. यहां हमारे प्रोजेक्ट के लैंडिंग पेज https://lightsaber.withgoogle.com का एक सैंपल दिया गया है. यह एक सामान्य एचटीएमएल फ़ाइल है, लेकिन इसमें कुछ जादू है:

<!-- 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>

इसलिए, अगर आपको HTML5 पर आधारित ऐप्लिकेशन बनाना है, तो आपके पास कई विकल्प हैं. एपीआई, फ़्रेमवर्क, लाइब्रेरी, गेम इंजन वगैरह सारे विकल्पों के बावजूद, एक अच्छा मिक्स सेटअप पाना मुश्किल है ग्राफ़िक्स और क्लीन मॉड्यूलर के उच्च प्रदर्शन पर नियंत्रण के बीच स्ट्रक्चर और बढ़ाए जा सकने की योग्यता. हमने पाया कि पॉलीमर हमें प्रोजेक्ट व्यवस्थित है, लेकिन यह अब भी निचले लेवल की परफ़ॉर्मेंस की अनुमति देता है ऑप्टिमाइज़ेशन के लिए, हमने सोच-समझकर अपने प्रोजेक्ट का विश्लेषण किया हमारी सुविधाओं का ज़्यादा से ज़्यादा फ़ायदा पा सकें.

पॉलिमर के साथ मॉड्यूलेरिटी

Polymer एक ऐसी लाइब्रेरी है जो दोबारा इस्तेमाल किए जा सकने वाले कस्टम एलिमेंट से आपके प्रोजेक्ट को बनाने में काफ़ी मदद मिलती है. यह आपको आपके कंप्यूटर पर सिंगल एचटीएमएल फ़ाइल का इस्तेमाल करता है. इनमें सिर्फ़ स्ट्रक्चर (एचटीएमएल मार्कअप) ही नहीं, बल्कि इनलाइन स्टाइल और लॉजिक के हिसाब से बनाया जा सकता है.

यहां दिया गया उदाहरण देखें:

<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>

हालांकि, बड़े प्रोजेक्ट के लिए इन तीन लॉजिकल कॉम्पोनेंट (एचटीएमएल, सीएसएस, और जेएस) को अलग-अलग रखना और सिर्फ़ कंपाइल करने के समय उन्हें मर्ज करना मददगार हो सकता है. इसलिए, हमने प्रोजेक्ट के हर एलिमेंट के लिए एक अलग फ़ोल्डर बनाया:

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

साथ ही, हर एलिमेंट के फ़ोल्डर का इंटरनल स्ट्रक्चर एक जैसा होता है. इसमें लॉजिक (कॉफ़ी फ़ाइलें), स्टाइल (एससीएसएस फ़ाइलें), और टेंप्लेट (जेड फ़ाइल) के लिए अलग-अलग डायरेक्ट्री और फ़ाइलें होती हैं.

यहां sw-ui-logo एलिमेंट का एक उदाहरण दिया गया है:

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

और अगर आप .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')

स्टाइल शामिल करके यह देखा जा सकता है कि चीज़ों को कैसे व्यवस्थित किया गया है और लॉजिक की मदद से बनाया जा सकता है. हमारे पॉलिमर में हमारी शैलियों को शामिल करने के लिए एलिमेंट में, हम जेड के include स्टेटमेंट का इस्तेमाल करते हैं. इसलिए, हमारे पास असली इनलाइन सीएसएस है कंपाइलेशन के बाद फ़ाइल सामग्री. sw-ui-logo.js स्क्रिप्ट एलिमेंट यह काम करेगा रनटाइम पर एक्ज़ीक्यूट होता है.

Bower के साथ मॉड्यूलर डिपेंडेंसी

आम तौर पर, हम लाइब्रेरी और अन्य डिपेंडेंसी को प्रोजेक्ट लेवल पर रखते हैं. हालांकि, ऊपर दिए गए सेटअप में आपको एक bower.json दिखेगा, जो एलिमेंट के फ़ोल्डर में है: एलिमेंट लेवल की डिपेंडेंसी. इस रणनीति के पीछे का आइडिया यह है कि ऐसी स्थिति में जब आपके पास अलग-अलग एलिमेंट के साथ डिपेंडेंसी हम पक्का कर सकते हैं कि सिर्फ़ वे डिपेंडेंसी लोड हों जो इस्तेमाल किया गया है. साथ ही, अगर किसी एलिमेंट को हटाया जाता है, तो आपको इसकी डिपेंडेंसी हटाएं, क्योंकि आपने bower.json फ़ाइल भी हटा दी होगी जो इन डिपेंडेंसी के बारे में बताता है. हर एलिमेंट, पेज को अलग-अलग लोड करता है डिपेंडेंसी जो इससे जुड़ी होती हैं.

हालांकि, डिपेंडेंसी के डुप्लीकेट से बचने के लिए, हम .bowerrc फ़ाइल शामिल करते हैं फ़ोल्डर में भी मिल सकती है. इससे bower को पता चलता है कि डिपेंडेंसी को कहां सेव करना है, ताकि हम यह पक्का कर सकें कि एक ही डायरेक्ट्री में आखिर में सिर्फ़ एक डिपेंडेंसी हो:

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

इस तरह, अगर कई एलिमेंट THREE.js को डिपेंडेंसी के तौर पर एलान करते हैं, तो एक बार bower, इसे पहले एलिमेंट के लिए इंस्टॉल करता है और दूसरे एलिमेंट को पार्स करना शुरू करता है, को पता चलेगा कि यह डिपेंडेंसी पहले से ही इंस्टॉल है, तो फिर से डाउनलोड करें या डुप्लीकेट बनाएं. इसी तरह, यह उस डिपेंडेंसी को बनाए रखेगा फ़ाइलों को तब तक सेट करें, जब तक कि उसमें कम से कम एक ऐसा एलीमेंट हो जो अभी भी इसे परिभाषित करता है यह bower.json है.

बैश स्क्रिप्ट, नेस्ट किए गए एलिमेंट स्ट्रक्चर में सभी bower.json फ़ाइलें ढूंढती है. इसके बाद, यह इन डायरेक्ट्री को एक-एक करके डालता है और bower install को हर स्थिति के लिए:

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

क्विक न्यू एलिमेंट टेंप्लेट

हर बार नया एलिमेंट बनाने में थोड़ा समय लगता है: सही नामों के साथ फ़ोल्डर और बुनियादी फ़ाइल स्ट्रक्चर जनरेट करना. इसलिए, हम आसानी से एलिमेंट जनरेटर लिखने के लिए, स्लश करें.

कमांड लाइन से स्क्रिप्ट को कॉल किया जा सकता है:

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

और नया एलिमेंट बन जाता है, जिसमें फ़ाइल का पूरा स्ट्रक्चर और कॉन्टेंट शामिल होता है.

हमने एलिमेंट फ़ाइलों के लिए टेंप्लेट तय किए हैं, जैसे कि .jade फ़ाइल टेंप्लेट ऐसा दिखता है:

// 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')

स्लश जनरेटर, वैरिएबल को असल एलिमेंट पाथ और नामों से बदल देता है.

एलिमेंट बनाने के लिए गल्प का इस्तेमाल करना

Gulp, बिल्ड प्रोसेस को कंट्रोल में रखता है. हमारे स्ट्रक्चर में, एलिमेंट बनाने के लिए, हमें Gulp को यह तरीका अपनाना होगा:

  1. एलिमेंट कंपाइल करें' .js के लिए .coffee फ़ाइलें
  2. एलिमेंट कंपाइल करें' .css के लिए .scss फ़ाइलें
  3. एलिमेंट कंपाइल करें' .html में .jade फ़ाइलें, .css फ़ाइलें एम्बेड की जा रही हैं.

ज़्यादा जानकारी:

एलिमेंट कंपाइल करना' .js के लिए .coffee फ़ाइलें

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')));
});

दूसरे और तीसरे चरण के लिए, हम gulp और compass प्लग इन का इस्तेमाल करते हैं. इससे scss को .css और .jade को .html में कंपाइल किया जाता है. यह तरीका, ऊपर बताए गए दूसरे चरण से मिलता-जुलता है.

Polymer एलिमेंट शामिल करना

असल में, पॉलिमर एलिमेंट को शामिल करने के लिए, हम एचटीएमएल इंपोर्ट का इस्तेमाल करते हैं.

<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">

प्रोडक्शन के लिए पॉलिमर एलिमेंट ऑप्टिमाइज़ करना

एक बड़े प्रोजेक्ट में कई पॉलिमर एलिमेंट हो सकते हैं. हमारे है, हमारे पास 50 से ज़्यादा हैं. अगर आपको लगता है कि हर एलिमेंट में अलग .js फ़ाइल है और कुछ लाइब्रेरी का संदर्भ दिया गया है, तो वह इससे ज़्यादा हो जाती है 100 अलग-अलग फ़ाइलें. इसका मतलब है कि ब्राउज़र को बहुत सारे अनुरोध करने होंगे, कम हो जाता है. इसी तरह, Angular के किसी बिल्ड पर लागू होने वाली कनेक्टेट और छोटा करने की प्रोसेस की तरह ही, हम प्रोडक्शन के लिए आखिर में Polymer प्रोजेक्ट को “वल्कनाइज़” करते हैं.

Vulcanize एक पॉलिमर टूल है डिपेंडेंसी ट्री को एक html फ़ाइल में फ़्लैट करता है, जिससे अनुरोधों की संख्या. यह विशेष रूप से उन ब्राउज़र के लिए बढ़िया है जो वेब कॉम्पोनेंट के साथ काम करता है.

सीएसपी (कॉन्टेंट की सुरक्षा के बारे में नीति) और पॉलीमर

सुरक्षित वेब ऐप्लिकेशन बनाते समय, आपको सीएसपी को लागू करना होगा. CSP ऐसे नियमों का एक सेट है जो क्रॉस-साइट स्क्रिप्टिंग (XSS) हमलों को रोकता है: असुरक्षित सोर्स से स्क्रिप्ट चलाना या इनलाइन स्क्रिप्ट चलाना को एचटीएमएल फ़ाइलों से हटाएं.

अब Vulcanize की मदद से जनरेट की गई, ऑप्टिमाइज़ की गई, जोड़ी गई, और छोटी की गई .html फ़ाइल में, सभी JavaScript कोड इनलाइन में हैं. यह फ़ाइल, सीएसपी के मुताबिक नहीं है. इसे ठीक करने के लिए, हम क्रिस्पर.

Crisper, एचटीएमएल फ़ाइल से इनलाइन स्क्रिप्ट को अलग करता है और उन्हें सीएसपी के अनुपालन के लिए, एक बाहरी JavaScript फ़ाइल में डाल देता है. इसलिए, हमने दुनिया भर के Crisper के ज़रिए एचटीएमएल फ़ाइल अपलोड करके दो फ़ाइलों पर जाएं: elements.html और elements.js. elements.html के अंदर, elements.js जनरेट हुआ.

ऐप्लिकेशन लॉजिकल स्ट्रक्चर

Polymer में, एलिमेंट में कोई भी चीज़ हो सकती है. जैसे, बिना विज़ुअल वाली कोई सुविधा, छोटे, स्टैंडअलोन, और फिर से इस्तेमाल किए जा सकने वाले यूज़र इंटरफ़ेस (यूआई) एलिमेंट (जैसे, बटन), "पेज" जैसे बड़े मॉड्यूल, और यहां तक कि पूरे ऐप्लिकेशन कॉम्पोज़ करना.

ऐप्लिकेशन का टॉप-लेवल लॉजिकल स्ट्रक्चर
हमारे ऐप्लिकेशन का टॉप-लेवल लॉजिकल स्ट्रक्चर, पॉलीमर एलिमेंट.

पॉलिमर और पैरंट-चाइल्ड आर्किटेक्चर के साथ पोस्टप्रोसेसिंग

किसी भी 3D ग्राफ़िक्स पाइपलाइन में, हमेशा एक अंतिम चरण होता है, जहां इन्हें पूरी तस्वीर के ऊपर एक तरह के ओवरले के तौर पर जोड़ा जाता है. यह पोस्ट-प्रोसेसिंग का चरण है. इसमें चमक, गॉड-रे, फ़ील्ड की गहराई, बोकेह, धुंधलापन वगैरह जैसे इफ़ेक्ट शामिल होते हैं. इफ़ेक्ट को जोड़कर, अलग-अलग एलिमेंट पर लागू किया जाता है. यह इस बात पर निर्भर करता है कि सीन कैसे बनाया गया है. THREE.js में, JavaScript में पोस्ट-प्रोसेसिंग के लिए कस्टम शेडर बनाया जा सकता है. इसके अलावा, पैरंट-चाइल्ड स्ट्रक्चर की मदद से, Polymer में भी ऐसा किया जा सकता है.

अगर हमारे पोस्ट-प्रोसेसर के एलिमेंट का एचटीएमएल कोड देखें:

<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>

हम इफ़ेक्ट को कॉमन क्लास के तहत, नेस्ट किए गए पॉलीमर एलिमेंट के तौर पर तय करते हैं. इसके बाद, sw-experience-postprocessor.js में हम ऐसा करते हैं:

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

हम एचटीएमएल सुविधा और JavaScript की querySelectorAll का इस्तेमाल करके, सभी पोस्ट प्रोसेसर में, एचटीएमएल एलिमेंट के तौर पर नेस्ट किए गए इफ़ेक्ट, क्रम में जिनमें वे शामिल थे. इसके बाद, हम उन पर बार-बार काम करते हैं और उन्हें कंपोजर में जोड़ते हैं.

मान लें कि हमें डीओएफ़ (डेप्थ ऑफ़ फ़ील्ड) इफ़ेक्ट हटाना है और ब्लूम और विग्नेट इफ़ेक्ट का क्रम बदलना है. हमें बस बदलाव करना है पोस्ट-प्रोसेसर की परिभाषा इस तरह है:

<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>

और सीन, असल कोड की एक भी लाइन में बदलाव किए बिना चल जाएगा.

Polymer में रेंडर लूप और अपडेट लूप

Polymer की मदद से, हम रेंडरिंग और इंजन अपडेट को भी शानदार तरीके से कर सकते हैं. हमने एक timer एलिमेंट बनाया है, जो requestAnimationFrame का इस्तेमाल करता है और कंप्यूट करता है मौजूदा समय (t) और डेल्टा समय जैसे मान - इस समय से बीता हुआ समय पिछला फ़्रेम (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()

इसके बाद, हम t और dt प्रॉपर्टी को अपने इंजन (experience.jade):

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

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

और हम इंजन में t और dt के बदलावों को सुनते हैं और जब भी मान बदलने के बाद, _update फ़ंक्शन को कॉल किया जाएगा:

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

अगर आपको एफ़पीएस का इस्तेमाल करना है, तो पॉलीमर का डेटा हटाएं सूचित करने के लिए ज़रूरी कुछ मिलीसेकंड सेव करने के लिए रेंडर लूप में बाइंडिंग जानकारी दी गई है. हमने कस्टम ऑब्ज़र्वर को इस तरह लागू किया:

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
    # ...

addUpdateListener फ़ंक्शन एक कॉलबैक स्वीकार करता है और उसे कॉलबैक अरे. इसके बाद, अपडेट लूप में, हम हर कॉलबैक को दोहराते हैं और हम डेटा बाइंडिंग को बायपास करते हुए, उसे सीधे dt और t आर्ग्युमेंट के साथ एक्ज़ीक्यूट करते हैं या इवेंट ट्रिगर होने में मदद मिलती है. जब कॉलबैक चालू न हो, तब हमने removeUpdateListener फ़ंक्शन, जो आपको पहले जोड़े गए कॉलबैक को हटाने देता है.

THREE.js में एक शमशेर-ए-रोशनी

THREE.js, WebGL के निम्न स्तर की जानकारी को हटा देता है और हमें फ़ोकस करने देता है समस्या पर ध्यान दें. हमारी समस्या स्टॉर्मट्रूपर्स से लड़ रही है और हमें हथियार. तो चलो एक शमशेर-ए-रोशनी बनाते हैं.

लाइटसैबर को दो हाथों से इस्तेमाल किए जाने वाले किसी भी पुराने हथियार से अलग बनाने वाला मुख्य फ़ीचर, उसका चमकता हुआ ब्लेड है. यह मुख्य रूप से दो हिस्सों से बना है: बीम और पगडंडी जिसे एक जगह से दूसरी जगह ले जाने पर देखा जा सकता है. हमने इसे एक चमकदार सिलिंडर के आकार से बनाया था और एक डाइनैमिक ट्रेल, जो खिलाड़ी के हिलने-डुलने पर उसके साथ-साथ चलता है.

द ब्लेड

ब्लेड दो सब ब्लेड से बना होता है. अंदर और बाहर. दोनों ही अपने-अपने मटीरियल के साथ THREE.js मेश हैं.

इनर ब्लेड

इनर ब्लेड के लिए, हमने कस्टम शेडर के साथ कस्टम मटीरियल का इस्तेमाल किया. हम दो बिंदुओं से बनी रेखा को लेते हैं और इन दोनों बिंदुओं के बीच की रेखा को किसी समतल पर प्रोजेक्ट करते हैं. मोबाइल से लड़ते समय, इस विमान को कंट्रोल किया जाता है. इससे, सेबर को गहराई और ओरिएंटेशन का एहसास मिलता है.

गोल चमकती हुई चीज़ की भावना पैदा करने के लिए हम मुख्य बिंदु से समतल पर किसी भी बिंदु के ऑर्थोगोनल बिंदु की दूरी दो पॉइंट A और B को जोड़ने वाली लाइन. कोई बिंदु जितना पास होगा मुख्य ऐक्सिस, जितना चमकदार है.

इनर ब्लेड ग्लो

नीचे दिए गए स्रोत से पता चलता है कि हम तीव्रता कंट्रोल करने के लिए vFactor का हिसाब कैसे लगाते हैं इसके बाद, वर्टेक्स शेडर में जाकर, इसका इस्तेमाल सीन के साथ ब्लेंड करने के लिए करें फ़्रैगमेंट शेडर.

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" )

};

ब्लेड के बाहरी हिस्से का ग्लो

बाहरी चमक के लिए हम एक अलग रेंडरबफ़र में रेंडर करते हैं और पोस्ट-प्रोसेसिंग ब्लूम इफ़ेक्ट और फ़ाइनल इमेज के साथ ब्लेंड करके, पसंदीदा चमक. नीचे दी गई इमेज में, वे तीन अलग-अलग इलाके दिखाए गए हैं जहां अगर आपको एक अच्छी सैबर चाहिए, तो जैसे, सफ़ेद कोर, बीच में नीले रंग का चमकना, और बाहरी चमक.

आउटर ब्लेड

Lightsaber Trail

शमशेर-ए-रोशनी के पीछे-पीछे ले जाते ही असली दुनिया का असली असर सामने आता है पेश कर रही हूँ. हमने त्रिभुजों के प्रशंसक से ट्रेल बनाया डाइनैमिक रूप से शमशेर-ए-रोशनी की हलचल के आधार पर. इसके बाद, इन्हें विज़ुअल को बेहतर बनाने के लिए पोस्टप्रोसेसर को भेजा जाता है. बनाने के लिए फ़ैन ज्यामिति में हमारा एक लाइन सेगमेंट है और यह इसके पिछले बदलाव पर आधारित है जाल में हम एक नया त्रिभुज बनाते हैं, जो नीचे एक निश्चित लंबाई के बाद पूंछ वाले हिस्से को बाहर निकालें.

शमशेर-ए-रोशनी वाला रास्ता बाईं ओर
लाइटसेबर ट्रेल दाईं ओर

एक बार मेश मिल जाने के बाद, हम इसे एक आसान मटीरियल असाइन करते हैं और इसे पोस्टप्रोसेसर का इस्तेमाल करें. हम उसी ब्लूम इफ़ेक्ट का इस्तेमाल करते हैं जिसे हमने ब्लेड के बाहरी हिस्से पर लागू किया था. इससे हमें स्मूद ट्रेल मिलता है, जैसा कि आप देख सकते हैं:

पूरी पगडंडी

रास्ते के चारों ओर चमक

आखिरी हिस्सा पूरा होने के लिए, हमें असल में आस-पास की चमक को संभालना था हैं, जिसे कई तरीक़ों से बनाया जा सकता है. हमारा समाधान यहां विस्तार से बात नहीं करते, क्योंकि परफ़ॉर्मेंस की वजह से कस्टम इस बफ़र के लिए शेडर का इस्तेमाल करें, जो रेंडर बफ़र. इसके बाद, हम इस आउटपुट को फ़ाइनल रेंडर में जोड़ते हैं. यहां आपके पास रास्ते के चारों ओर की चमक देखें:

ग्लो वाली पगडंडी

नतीजा

Polymer एक बेहतरीन लाइब्रेरी और कॉन्सेप्ट है. यह वैसा ही है जैसा आम तौर पर WebComponents हैं. यह सिर्फ़ आप पर निर्भर करता है कि आप इससे क्या बनाते हैं. यह यहाँ से कुछ भी हो सकता है: एक पूर्ण आकार के WebGL ऐप्लिकेशन का आसान यूज़र इंटरफ़ेस (यूआई) बटन है. पिछले चैप्टर में हमने आपको Polymer ऐप्लिकेशन का बेहतर तरीके से इस्तेमाल करने के कुछ सुझाव और तरकीबें दिखाई हैं और ऐसे जटिल मॉड्यूल को स्ट्रक्चर करने का तरीका बताएं जो बेहतर परफ़ॉर्म कर सकें करते हैं. हमने आपको WebGL में एक सुंदर दिखने वाले लाइटसेबर पाने का तरीका भी दिखाया. इसलिए, अगर आपने इन सभी बातों का ध्यान रखा है, तो प्रोडक्शन सर्वर पर डिप्लॉय करने से पहले, अपने Polymer एलिमेंट को वल्कनाइज़ करना न भूलें. साथ ही, अगर आपको सीएसपी का पालन करना है, तो Crisper का इस्तेमाल करना न भूलें.

गेम प्ले