কেস স্টাডি - Oz আপনার উপায় খুঁজুন

ভূমিকা

"Find Your Way to Oz" হল Disney দ্বারা ওয়েবে আনা একটি নতুন Google Chrome পরীক্ষা৷ এটি আপনাকে একটি কানসাস সার্কাসের মাধ্যমে একটি ইন্টারেক্টিভ যাত্রা করার অনুমতি দেয়, যা আপনাকে একটি বিশাল ঝড়ের কবলে পড়ার পরে ওজ ভূমিতে নিয়ে যায়।

আমাদের লক্ষ্য ছিল একটি মজাদার, নিমগ্ন অভিজ্ঞতা তৈরি করতে ব্রাউজারের প্রযুক্তিগত ক্ষমতার সাথে সিনেমার সমৃদ্ধি একত্রিত করা যা ব্যবহারকারীরা একটি শক্তিশালী সংযোগ তৈরি করতে পারে।

কাজটি এই অংশে সম্পূর্ণরূপে ক্যাপচার করার জন্য একটি বিট খুব বড় তাই আমরা ডুব দিয়েছি এবং প্রযুক্তির গল্পের কিছু অধ্যায় বের করেছি যেগুলিকে আমরা আকর্ষণীয় মনে করি। পথ ধরে আমরা ক্রমবর্ধমান অসুবিধার কিছু ফোকাসড টিউটোরিয়াল বের করেছি।

অনেক লোক এই অভিজ্ঞতাকে সম্ভব করার জন্য কঠোর পরিশ্রম করেছে: এখানে তালিকাভুক্ত করা অনেক বেশি। সম্পূর্ণ গল্পের জন্য মেনু বিভাগের অধীনে ক্রেডিট পৃষ্ঠাটি পরীক্ষা করতে দয়া করে সাইটটি দেখুন

আ পিক আন্ডার দ্য হুড

ডেস্কটপে Oz যাবার উপায় খুঁজুন একটি সমৃদ্ধ নিমগ্ন বিশ্ব। আমরা 3D এবং প্রথাগত ফিল্মমেকিং অনুপ্রাণিত প্রভাবগুলির বিভিন্ন স্তর ব্যবহার করি যা একটি কাছাকাছি বাস্তবসম্মত দৃশ্য তৈরি করতে একত্রিত হয়। সবচেয়ে বিশিষ্ট প্রযুক্তি হল WebGL with Three.js, কাস্টম বিল্ট শেডার্স এবং CSS3 বৈশিষ্ট্য ব্যবহার করে DOM অ্যানিমেটেড উপাদান। এর বাইরে, ইন্টারেক্টিভ অভিজ্ঞতার জন্য getUserMedia API (WebRTC) ব্যবহারকারীকে 3D সাউন্ডের জন্য ওয়েবক্যাম এবং WebAudio থেকে সরাসরি তাদের ছবি যোগ করতে দেয়।

কিন্তু এই ধরনের প্রযুক্তিগত অভিজ্ঞতার জাদু এটি কিভাবে একত্রিত হয়। এটিও প্রধান চ্যালেঞ্জগুলির মধ্যে একটি: একটি সামঞ্জস্যপূর্ণ সমগ্র তৈরি করতে কীভাবে একটি দৃশ্যে ভিজ্যুয়াল প্রভাব এবং ইন্টারেক্টিভ উপাদানগুলিকে একসাথে মিশ্রিত করবেন? এই চাক্ষুষ জটিলতা পরিচালনা করা কঠিন ছিল: এটা বলা কঠিন যে আমরা কোন সময়ে উন্নয়নের কোন পর্যায়ে ছিলাম।

আন্তঃসংযুক্ত ভিজ্যুয়াল এফেক্ট এবং অপ্টিমাইজেশানের সমস্যা সমাধানের জন্য আমরা একটি কন্ট্রোল প্যানেলের ব্যাপক ব্যবহার করেছি যা সেই সময়ে আমরা যে সমস্ত প্রাসঙ্গিক সেটিংস পর্যালোচনা করছিলাম তা ক্যাপচার করবে। দৃশ্যটি ব্রাউজারে লাইভ সামঞ্জস্য করা যেতে পারে উজ্জ্বলতা থেকে শুরু করে ক্ষেত্রের গভীরতা, গামা ইত্যাদির জন্য... যে কেউ অভিজ্ঞতায় গুরুত্বপূর্ণ প্যারামিটারের মান পরিবর্তন করার চেষ্টা করতে পারে এবং কোনটি সবচেয়ে ভাল কাজ করে তা আবিষ্কারে অংশ নিতে পারে।

আমরা আমাদের গোপনীয়তা শেয়ার করার আগে আমরা আপনাকে সতর্ক করতে চাই যে এটি ক্র্যাশ হতে পারে, ঠিক যেমন আপনি একটি গাড়ির ইঞ্জিনের মধ্যে ঘুরতে গেলে। নিশ্চিত করুন যে আপনার কাছে গুরুত্বপূর্ণ কিছু নেই এবং সাইটের মূল url-এ যান এবং ঠিকানায় ?debug=on যোগ করুন৷ সাইটটি লোড হওয়ার জন্য অপেক্ষা করুন এবং একবার আপনি Ctrl-I কী টিপুন (টিপুন?) এবং আপনি ডানদিকে একটি ড্রপডাউন দেখতে পাবেন। আপনি যদি "ক্যামেরা পথ থেকে প্রস্থান করুন" বিকল্পটি আনচেক করেন তাহলে আপনি A, W, S, D কী এবং মাউস ব্যবহার করে স্থানের চারপাশে অবাধে চলাফেরা করতে পারেন।

ক্যামেরা পথ।

আমরা এখানে সমস্ত সেটিংসের মধ্য দিয়ে যাব না, তবে আমরা আপনাকে পরীক্ষা করার জন্য উত্সাহিত করি: কীগুলি বিভিন্ন দৃশ্যে বিভিন্ন সেটিংস প্রকাশ করে৷ চূড়ান্ত ঝড়ের ক্রমানুসারে একটি অতিরিক্ত কী রয়েছে: Ctrl-A যার সাহায্যে আপনি অ্যানিমেশন প্লেব্যাক টগল করতে পারেন এবং চারপাশে উড়তে পারেন। এই দৃশ্যে আপনি যদি Esc টিপুন (মাউস লক কার্যকারিতা থেকে প্রস্থান করতে) এবং আবার Ctrl-I টিপুন তাহলে আপনি ঝড়ের দৃশ্যের জন্য বিশেষ সেটিংস অ্যাক্সেস করতে পারবেন। চারপাশে তাকান এবং নীচের মত কিছু সুন্দর পোস্টকার্ড ভিউ ক্যাপচার করুন।

ঝড়ের দৃশ্য

এটি ঘটানোর জন্য এবং এটি আমাদের প্রয়োজনের জন্য যথেষ্ট নমনীয় ছিল তা নিশ্চিত করার জন্য, আমরা dat.gui নামক একটি সুন্দর লাইব্রেরি ব্যবহার করেছি (এটি কীভাবে ব্যবহার করবেন তার একটি অতীত টিউটোরিয়ালের জন্য এখানে দেখুন )। এটি আমাদের সাইটের দর্শকদের কাছে কী সেটিংস প্রকাশ করা হয়েছিল তা দ্রুত পরিবর্তন করার অনুমতি দেয়।

ম্যাট পেইন্টিং মত একটি বিট

অনেক ক্লাসিক ডিজনি ফিল্ম এবং অ্যানিমেশনে দৃশ্য তৈরি করার অর্থ বিভিন্ন স্তরকে একত্রিত করা। লাইভ অ্যাকশন, সেল অ্যানিমেশন, এমনকি ফিজিক্যাল সেট এবং কাচের উপর পেইন্টিং দ্বারা তৈরি উপরের স্তরগুলির স্তর ছিল: ম্যাট-পেইন্টিং নামে একটি কৌশল।

আমরা যে অভিজ্ঞতা তৈরি করেছি তার গঠন অনেক উপায়ে একই রকম; যদিও কিছু "স্তর" স্ট্যাটিক ভিজ্যুয়ালের চেয়ে অনেক বেশি। প্রকৃতপক্ষে, তারা আরও জটিল গণনা অনুসারে জিনিসগুলির চেহারাকে প্রভাবিত করে। তা সত্ত্বেও, অন্তত বড়-ছবির স্তরে আমরা ভিউ নিয়ে কাজ করছি, একটির উপরে অন্যটির সংমিশ্রণ। শীর্ষে আপনি একটি UI স্তর দেখতে পাচ্ছেন, যার নীচে একটি 3D দৃশ্য রয়েছে: নিজেই বিভিন্ন দৃশ্যের উপাদান দিয়ে তৈরি৷

শীর্ষ ইন্টারফেস স্তরটি DOM এবং CSS 3 ব্যবহার করে তৈরি করা হয়েছিল যার অর্থ হল মিথস্ক্রিয়া সম্পাদনা অনেক উপায়ে করা যেতে পারে 3D অভিজ্ঞতা থেকে স্বাধীনভাবে ইভেন্টের একটি নির্বাচিত তালিকা অনুসারে উভয়ের মধ্যে যোগাযোগের সাথে। এই যোগাযোগ ব্যাকবোন রাউটার + onHashChange HTML5 ইভেন্ট ব্যবহার করে যা নিয়ন্ত্রণ করে কোন এলাকাটি ইন/আউট করা উচিত। (প্রকল্প উত্স: /develop/coffee/router/Router.coffee)।

টিউটোরিয়াল: স্প্রাইট শীট এবং রেটিনা সমর্থন

একটি মজার অপ্টিমাইজেশান কৌশল যা আমরা ইন্টারফেসের জন্য নির্ভর করেছিলাম সার্ভারের অনুরোধগুলি কমাতে একটি একক PNG তে অনেকগুলি ইন্টারফেস ওভারলে চিত্র একত্রিত করা। এই প্রজেক্টে ইন্টারফেসটি 70+ ইমেজ (3D টেক্সচারের গণনা না করে) দিয়ে তৈরি করা হয়েছে যাতে ওয়েবসাইটের লেটেন্সি কমাতে আগেভাগেই লোড করা হয়। আপনি এখানে লাইভ স্প্রাইট শীট দেখতে পারেন:

সাধারণ প্রদর্শন - http://findyourwaytooz.com/img/home/interface_1x.png রেটিনা প্রদর্শন - http://findyourwaytooz.com/img/home/interface_2x.png

আমরা কীভাবে স্প্রাইট শীট ব্যবহার করেছি এবং রেটিনা ডিভাইসের জন্য কীভাবে সেগুলি ব্যবহার করব এবং ইন্টারফেসটিকে যতটা সম্ভব তীক্ষ্ণ এবং ঝরঝরে করতে হবে তার কিছু টিপস এখানে রয়েছে।

স্প্রাইটশিট তৈরি করা

SpriteSheets তৈরি করতে আমরা TexturePacker ব্যবহার করেছি যা আপনার প্রয়োজনে যেকোনো ফরম্যাটে আউটপুট দেয়। এই ক্ষেত্রে আমরা EaselJS হিসাবে রপ্তানি করেছি যা সত্যিই পরিষ্কার এবং অ্যানিমেটেড স্প্রাইট তৈরি করতেও ব্যবহার করা যেতে পারে।

উত্পন্ন স্প্রাইট শীট ব্যবহার করে

একবার আপনি আপনার স্প্রাইট শীট তৈরি করলে আপনাকে এইরকম একটি JSON ফাইল দেখতে হবে:

{
   "images": ["interface_2x.png"],
   "frames": [
       [2, 1837, 88, 130],
       [2, 2, 1472, 112],
       [1008, 774, 70, 68],
       [562, 1960, 86, 86],
       [473, 1960, 86, 86]
   ],

   "animations": {
       "allow_web":[0],
       "bottomheader":[1],
       "button_close":[2],
       "button_facebook":[3],
       "button_google":[4]
   },
}

কোথায়:

  • চিত্রটি স্প্রাইট শীটের URL বোঝায়
  • ফ্রেম হল প্রতিটি UI উপাদানের স্থানাঙ্ক [x, y, প্রস্থ, উচ্চতা]
  • অ্যানিমেশন প্রতিটি সম্পদের নাম

দ্রষ্টব্য যে আমরা স্প্রাইট শীট তৈরি করতে উচ্চ ঘনত্বের চিত্রগুলি ব্যবহার করেছি, তারপরে আমরা সাধারণ সংস্করণটি তৈরি করেছি কেবল এটির আকারের অর্ধেক আকারে পরিবর্তন করেছি।

সবকিছু একসাথে করা

এখন আমরা সব সেট করছি এটা ব্যবহার করার জন্য আমাদের শুধু একটি জাভাস্ক্রিপ্ট স্নিপেট দরকার।

var SSAsset = function (asset, div) {
  var css, x, y, w, h;

  // Divide the coordinates by 2 as retina devices have 2x density
  x = Math.round(asset.x / 2);
  y = Math.round(asset.y / 2);
  w = Math.round(asset.width / 2);
  h = Math.round(asset.height / 2);

  // Create an Object to store CSS attributes
  css = {
    width                : w,
    height               : h,
    'background-image'   : "url(" + asset.image_1x_url + ")",
    'background-size'    : "" + asset.fullSize[0] + "px " + asset.fullSize[1] + "px",
    'background-position': "-" + x + "px -" + y + "px"
  };

  // If retina devices

  if (window.devicePixelRatio === 2) {

    /*
    set -webkit-image-set
    for 1x and 2x
    All the calculations of X, Y, WIDTH and HEIGHT is taken care by the browser
    */

    css['background-image'] = "-webkit-image-set(url(" + asset.image_1x_url + ") 1x,";
    css['background-image'] += "url(" + asset.image_2x_url + ") 2x)";

  }

  // Set the CSS to the DIV
  div.css(css);
};

এবং এইভাবে আপনি এটি ব্যবহার করবেন:

logo = new SSAsset(
{
  fullSize     : [1024, 1024],               // image 1x dimensions Array [x,y]
  x            : 1790,                       // asset x coordinate on SpriteSheet         
  y            : 603,                        // asset y coordinate on SpriteSheet
  width        : 122,                        // asset width
  height       : 150,                        // asset height
  image_1x_url : 'img/spritesheet_1x.png',   // background image 1x URL
  image_2x_url : 'img/spritesheet_2x.png'    // background image 2x URL
},$('#logo'));

পরিবর্তনশীল পিক্সেল ঘনত্ব সম্পর্কে আরও কিছু বোঝার জন্য আপনি বরিস স্মাসের এই নিবন্ধটি পড়তে পারেন।

3D সামগ্রী পাইপলাইন

পরিবেশ অভিজ্ঞতা একটি WebGL স্তরে সেট আপ করা হয়েছে৷ আপনি যখন একটি 3D দৃশ্য সম্পর্কে চিন্তা করেন, তখন সবচেয়ে কঠিন প্রশ্নগুলির মধ্যে একটি হল আপনি কীভাবে নিশ্চিত করবেন যে আপনি এমন সামগ্রী তৈরি করতে পারেন যা মডেলিং, অ্যানিমেশন এবং প্রভাব দিক থেকে সর্বাধিক অভিব্যক্তিপূর্ণ সম্ভাবনাকে সক্ষম করে। অনেক উপায়ে এই সমস্যার মূল বিষয়বস্তু পাইপলাইন: 3D দৃশ্যের জন্য বিষয়বস্তু তৈরি করতে অনুসরণ করার জন্য একটি সম্মত প্রক্রিয়া।

আমরা একটি বিস্ময় অনুপ্রেরণাদায়ক বিশ্ব তৈরি করতে চেয়েছিলেন; তাই আমাদের একটি কঠিন প্রক্রিয়া প্রয়োজন যা 3D শিল্পীদের এটি তৈরি করতে সক্ষম করবে। তাদের 3D মডেলিং এবং অ্যানিমেশন সফ্টওয়্যারে যতটা সম্ভব অভিব্যক্তিপূর্ণ স্বাধীনতা দেওয়া দরকার; এবং আমাদের এটিকে কোডের মাধ্যমে স্ক্রিনে রেন্ডার করতে হবে।

আমরা কিছু সময়ের জন্য এই ধরণের সমস্যা নিয়ে কাজ করছিলাম কারণ অতীতে যখনই আমরা একটি 3D সাইট তৈরি করেছি তখন আমরা যে সরঞ্জামগুলি ব্যবহার করতে পারি তাতে সীমাবদ্ধতা খুঁজে পেয়েছি৷ তাই আমরা এই টুলটি তৈরি করেছি, যাকে বলা হয় 3D লাইব্রেরিয়ান: অভ্যন্তরীণ গবেষণার একটি অংশ। এবং এটি একটি বাস্তব চাকরিতে আবেদন করার জন্য প্রস্তুত ছিল।

এই টুলটির কিছু ইতিহাস ছিল: মূলত এটি ফ্ল্যাশের জন্য ছিল, এবং এটি আপনাকে একটি একক সংকুচিত ফাইল হিসাবে একটি বড় মায়া দৃশ্য আনতে দেয় যা রানটাইম আনপ্যাক করার জন্য অপ্টিমাইজ করা হয়েছিল। এটি সর্বোত্তম ছিল কারণ এটি কার্যকরভাবে দৃশ্যটিকে মূলত একই ডেটা কাঠামোতে প্যাক করেছে যা রেন্ডার এবং অ্যানিমেশনের সময় ম্যানিপুলেট করা হয়। লোড করার সময় ফাইলটিতে খুব কম পার্সিং করা দরকার। ফ্ল্যাশে আনপ্যাক করা খুব দ্রুত ছিল কারণ ফাইলটি AMF ফর্ম্যাটে ছিল, যে ফ্ল্যাশ স্থানীয়ভাবে আনপ্যাক করতে পারে। WebGL-এ একই ফর্ম্যাট ব্যবহার করার জন্য CPU-তে একটু বেশি কাজ করতে হবে। আসলে আমাদের কোডের একটি ডেটা-আনপ্যাক জাভাস্ক্রিপ্ট স্তর পুনরায় তৈরি করতে হয়েছিল, যা মূলত সেই ফাইলগুলিকে ডিকম্প্রেস করবে এবং WebGL এর কাজ করার জন্য প্রয়োজনীয় ডেটা স্ট্রাকচারগুলি পুনরায় তৈরি করবে। পুরো 3D দৃশ্যটি আনপ্যাক করা একটি হালকা CPU-ভারী অপারেশন: ফাইন্ড ইওর ওয়ে টু ওজেজে দৃশ্য 1 আনপ্যাক করার জন্য একটি মধ্য থেকে উচ্চ প্রান্তের মেশিনে প্রায় 2 সেকেন্ডের প্রয়োজন হয়। তাই এটি ওয়েব ওয়ার্কার্স প্রযুক্তি ব্যবহার করে করা হয়, "দৃশ্য সেটআপ" সময়ে (দৃশ্যটি আসলে চালু হওয়ার আগে), যাতে ব্যবহারকারীর জন্য অভিজ্ঞতা ঝুলে না যায়।

এই সহজ টুলটি সর্বাধিক 3D দৃশ্য আমদানি করতে পারে: মডেল, টেক্সচার, হাড়ের অ্যানিমেশন। আপনি একটি একক লাইব্রেরি ফাইল তৈরি করুন, যা 3D ইঞ্জিন দ্বারা লোড করা যেতে পারে। আপনি এই লাইব্রেরির মধ্যে আপনার দৃশ্যে আপনার প্রয়োজনীয় সমস্ত মডেলগুলি স্টাফ করুন, এবং, voilà, সেগুলিকে আপনার দৃশ্যে তৈরি করুন৷

যদিও আমাদের একটি সমস্যা ছিল, তা হল আমরা এখন WebGL এর সাথে কাজ করছি: ব্লকের নতুন বাচ্চা। এটি একটি চমত্কার কঠিন বাচ্চা ছিল: এটি ব্রাউজার ভিত্তিক 3D অভিজ্ঞতার জন্য মান নির্ধারণ করছিল। তাই আমরা একটি অ্যাডহক জাভাস্ক্রিপ্ট স্তর তৈরি করেছি যা 3D লাইব্রেরিয়ান সংকুচিত 3D দৃশ্য ফাইলগুলিকে গ্রহণ করবে এবং সঠিকভাবে সেগুলিকে এমন একটি বিন্যাসে অনুবাদ করবে যা WebGL বুঝতে পারবে৷

টিউটোরিয়াল: লেট দেয়ার বি উইন্ড

একটি পুনরাবৃত্ত থিম "Oz আপনার উপায় খুঁজুন" বায়ু ছিল. কাহিনির একটি থ্রেড বাতাসের একটি চমক হিসাবে গঠন করা হয়েছে।

কার্নিভালের প্রথম দৃশ্যটি তুলনামূলকভাবে শান্ত। এবং বিভিন্ন দৃশ্যের মধ্য দিয়ে যাওয়া, ব্যবহারকারী একটি ক্রমশ শক্তিশালী বাতাসের অভিজ্ঞতা লাভ করে, যা চূড়ান্ত দৃশ্যে, ঝড়ে পরিণত হয়।

তাই এটি একটি immersive বায়ু প্রভাব প্রদান গুরুত্বপূর্ণ ছিল.

এটি তৈরি করার জন্য, আমরা 3টি কার্নিভাল দৃশ্যকে এমন বস্তু দিয়ে তৈরি করেছি যেগুলি নরম ছিল এবং এইভাবে তাঁবু, ফটো বুথের পৃষ্ঠের পতাকা এবং বেলুনগুলির মতো বাতাস দ্বারা প্রভাবিত হওয়ার কথা।

নরম কাপড়.

ডেস্কটপ গেমগুলি আজকাল সাধারণত একটি মূল পদার্থবিদ্যা ইঞ্জিনকে ঘিরে তৈরি হয়। তাই যখন 3D বিশ্বে একটি সফট-অবজেক্টকে সিমুলেট করার প্রয়োজন হয়, তখন এটির জন্য একটি সম্পূর্ণ পদার্থবিজ্ঞানের সিমুলেশন চালানো হয়, একটি বিশ্বাসযোগ্য নরম-আচরণ তৈরি করে।

ওয়েবজিএল/জাভাস্ক্রিপ্টে আমাদের কাছে (এখনও) একটি সম্পূর্ণ প্রস্ফুটিত পদার্থবিদ্যা সিমুলেশন চালানোর বিলাসিতা নেই। সুতরাং Oz-এ আমাদের আসলে এটিকে অনুকরণ না করেই বাতাসের প্রভাব তৈরি করার একটি উপায় খুঁজে বের করতে হয়েছিল।

আমরা 3D মডেলে প্রতিটি বস্তুর জন্য "বায়ু সংবেদনশীলতার" তথ্য এম্বেড করেছি। 3D মডেলের প্রতিটি শীর্ষবিন্দুতে একটি "উইন্ড অ্যাট্রিবিউট" ছিল যা নির্দিষ্ট করে দেয় যে শীর্ষবিন্দুটি বাতাস দ্বারা কতটা প্রভাবিত হবে। সুতরাং, 3D বস্তুর এই নির্দিষ্ট বায়ু সংবেদনশীলতা। তারপর আমরা বায়ু নিজেই তৈরি করা প্রয়োজন.

আমরা পার্লিন নয়েজ ধারণকারী একটি চিত্র তৈরি করে এটি করেছি। এই চিত্রটি একটি নির্দিষ্ট "বাতাসের এলাকা" কভার করার উদ্দেশ্যে করা হয়েছে৷ সুতরাং, এটি সম্পর্কে চিন্তা করার একটি ভাল উপায় হল 3D দৃশ্যের একটি নির্দিষ্ট আয়তক্ষেত্রাকার অঞ্চলে মেঘের মতো শব্দের ছবি কল্পনা করা। এই চিত্রটির প্রতিটি পিক্সেল, ধূসর স্তরের মান নির্দিষ্ট করে যে 3D এলাকায় একটি নির্দিষ্ট মুহুর্তে বাতাস কতটা শক্তিশালী "এর চারপাশে"৷

বাতাসের প্রভাব তৈরি করার জন্য চিত্রটি সময়ে, ধ্রুব গতিতে, একটি নির্দিষ্ট দিকে সরানো হয়; বাতাসের দিক। এবং "বাতাস এলাকা" দৃশ্যের সবকিছুকে প্রভাবিত করে না তা নিশ্চিত করার জন্য আমরা প্রভাবের ক্ষেত্রে সীমাবদ্ধ প্রান্তের চারপাশে বাতাসের চিত্রটি মোড়ানো করি।

একটি সহজ 3D উইন্ড টিউটোরিয়াল

আসুন এখন Three.js-এ একটি সাধারণ 3D দৃশ্যে বাতাসের প্রভাব তৈরি করি।

আমরা একটি সাধারণ "প্রক্রিয়াগত ঘাস ক্ষেত্রে" বায়ু তৈরি করতে যাচ্ছি।

আসুন প্রথমে দৃশ্যটি তৈরি করি। আমরা একটি সহজ, টেক্সচার্ড সমতল ভূখণ্ড পেতে যাচ্ছি। এবং তারপর ঘাস প্রতিটি বিট, সহজভাবে একটি উলটো-ডাউন 3D শঙ্কু সঙ্গে প্রতিনিধিত্ব করা যাচ্ছে.

ঘাসে ভরা ভূখণ্ড
ঘাসে ভরা ভূখণ্ড

CoffeeScript ব্যবহার করে Three.js- এ এই সাধারণ দৃশ্যটি কীভাবে তৈরি করা যায় তা এখানে রয়েছে।

প্রথমে আমরা Three.js সেটআপ করতে যাচ্ছি, এবং এটিকে ক্যামেরা, মাউস কন্ট্রোলার এবং কিছু লাইট দিয়ে হুক আপ করতে যাচ্ছি:

constructor: ->

   @clock =  new THREE.Clock()

   @container = document.createElement( 'div' );
   document.body.appendChild( @container );

   @renderer = new THREE.WebGLRenderer();
   @renderer.setSize( window.innerWidth, window.innerHeight );
   @renderer.setClearColorHex( 0x808080, 1 )
   @container.appendChild(@renderer.domElement);

   @camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5000 );
   @camera.position.x = 5;
   @camera.position.y = 10;
   @camera.position.z = 40;

   @controls = new THREE.OrbitControls( @camera, @renderer.domElement );
   @controls.enabled = true

   @scene = new THREE.Scene();
   @scene.add( new THREE.AmbientLight 0xFFFFFF )

   directional = new THREE.DirectionalLight 0xFFFFFF
   directional.position.set( 10,10,10)
   @scene.add( directional )

   # Demo data
   @grassTex = THREE.ImageUtils.loadTexture("textures/grass.png");
   @initGrass()
   @initTerrain()

   # Stats
   @stats = new Stats();
   @stats.domElement.style.position = 'absolute';
   @stats.domElement.style.top = '0px';
   @container.appendChild( @stats.domElement );
   window.addEventListener( 'resize', @onWindowResize, false );
   @animate()

initGrass , এবং initTerrain ফাংশনটি যথাক্রমে ঘাস এবং ভূখণ্ড দিয়ে দৃশ্যকে পপুলেট করে:

initGrass:->
   mat = new THREE.MeshPhongMaterial( { map: @grassTex } )
   NUM = 15
   for i in [0..NUM] by 1
       for j in [0..NUM] by 1
           x = ((i/NUM) - 0.5) * 50 + THREE.Math.randFloat(-1,1)
           y = ((j/NUM) - 0.5) * 50 + THREE.Math.randFloat(-1,1)
           @scene.add( @instanceGrass( x, 2.5, y, 5.0, mat ) )

instanceGrass:(x,y,z,height,mat)->
   geometry = new THREE.CylinderGeometry( 0.9, 0.0, height, 3, 5 )
   mesh = new THREE.Mesh( geometry, mat )
   mesh.position.set( x, y, z )
   return mesh

এখানে আমরা 15 বাই 15 বিট ঘাসের একটি গ্রিড তৈরি করছি। আমরা প্রতিটি ঘাসের অবস্থানে কিছুটা র্যান্ডমাইজেশন যোগ করি, যাতে তারা সৈন্যদের মতো লাইনে না দাঁড়ায়, যা অদ্ভুত দেখায়।

এই ভূখণ্ডটি কেবল একটি অনুভূমিক সমতল, যা ঘাসের টুকরোগুলির গোড়ায় স্থাপন করা হয়েছে (y = 2.5)।

initTerrain:->
  @plane = new THREE.Mesh( new THREE.PlaneGeometry(60, 60, 2, 2), new THREE.MeshPhongMaterial({ map: @grassTex }))
  @plane.rotation.x = -Math.PI/2
  @scene.add( @plane )

সুতরাং, আমরা এখন পর্যন্ত যা করেছি তা হল কেবলমাত্র একটি Three.js দৃশ্য তৈরি করা, এবং প্রক্রিয়াগতভাবে উত্পন্ন বিপরীত শঙ্কু দিয়ে তৈরি ঘাসের কয়েকটি বিট যোগ করা এবং একটি সাধারণ ভূখণ্ড।

এ পর্যন্ত অভিনব কিছুই নেই।

এখন, বাতাস যোগ করা শুরু করার সময়। প্রথম জিনিস, আমরা ঘাস 3D মডেলে বায়ু সংবেদনশীলতার তথ্য এম্বেড করতে চাই।

আমরা ঘাস 3D মডেলের প্রতিটি শীর্ষের জন্য একটি কাস্টম বৈশিষ্ট্য হিসাবে এই তথ্যটি এম্বেড করতে যাচ্ছি। এবং আমরা নিয়মটি ব্যবহার করতে যাচ্ছি: ঘাসের মডেলের নীচের প্রান্তে (শঙ্কুর ডগা) শূন্য সংবেদনশীলতা রয়েছে, কারণ এটি মাটির সাথে সংযুক্ত। ঘাস মডেলের উপরের অংশে (শঙ্কুর ভিত্তি) সর্বাধিক বায়ু সংবেদনশীলতা রয়েছে, কারণ এটি সেই অংশ যা মাটি থেকে অনেক দূরে।

ঘাস 3D মডেলের জন্য একটি কাস্টম বৈশিষ্ট্য হিসাবে বায়ু সংবেদনশীলতা যোগ করার জন্য, ইনস্ট্যান্সগ্রাস ফাংশনটি কীভাবে পুনরায় কোড করা হয় তা এখানে রয়েছে।

instanceGrass:(x,y,z,height)->

  geometry = new THREE.CylinderGeometry( 0.9, 0.0, height, 3, 5 )

  for i in [0..geometry.vertices.length-1] by 1
      v = geometry.vertices[i]
      r = (v.y / height) + 0.5
      @windMaterial.attributes.windFactor.value[i] = r * r * r

  # Create mesh
  mesh = new THREE.Mesh( geometry, @windMaterial )
  mesh.position.set( x, y, z )
  return mesh

আমরা পূর্বে যে MeshPhongMaterial ব্যবহার করি তার পরিবর্তে এখন আমরা একটি কাস্টম উপাদান, windMaterial ব্যবহার করি। উইন্ডম্যাটেরিয়াল উইন্ডমেশশেডারকে মোড়ানো হয় যা আমরা এক মিনিটের মধ্যে দেখতে যাচ্ছি।

সুতরাং, ইনস্ট্যান্সগ্রাস- এর কোডটি ঘাস মডেলের সমস্ত শীর্ষবিন্দুর মধ্য দিয়ে লুপ করে এবং প্রতিটি শীর্ষের জন্য এটি একটি কাস্টম শীর্ষবিন্দু যোগ করে, যাকে বলা হয় windFactor । এই windFactor 0 এ সেট করা হয়েছে, ঘাস মডেলের নীচের প্রান্তের জন্য (যেখানে এটি ভূখণ্ড স্পর্শ করার কথা) এবং এটি ঘাস মডেলের উপরের প্রান্তের জন্য 1 এর মান নির্ধারণ করে।

অন্য উপাদান যা আমাদের প্রয়োজন তা হল আমাদের দৃশ্যে প্রকৃত বায়ু যোগ করা। আলোচনা করা হয়েছে আমরা এর জন্য পার্লিন শব্দ ব্যবহার করতে যাচ্ছি। আমরা পদ্ধতিগতভাবে একটি পার্লিন নয়েজ টেক্সচার তৈরি করব।

স্বচ্ছতার জন্য আমরা এই টেক্সচারটিকে ভূখণ্ডে বরাদ্দ করতে যাচ্ছি, আগের সবুজ টেক্সচারের জায়গায়। এটি বাতাসের সাথে কী ঘটছে তা অনুভব করা সহজ করে তুলবে।

সুতরাং, এই পার্লিন নয়েজ টেক্সচারটি আমাদের ভূখণ্ডের এক্সটেনশনকে স্থানিকভাবে কভার করবে এবং টেক্সচারের প্রতিটি পিক্সেল সেই ভূখণ্ডের বায়ুর তীব্রতা নির্দিষ্ট করবে যেখানে সেই পিক্সেলটি পড়ে। ভূখণ্ডের আয়তক্ষেত্রটি আমাদের "বাতাসের ক্ষেত্র" হতে চলেছে।

পার্লিন শব্দ পদ্ধতিগতভাবে একটি শেডারের মাধ্যমে উত্পন্ন হয়, যাকে বলা হয় NoiseShader । এই শেডারটি থেকে 3d সিমপ্লেক্স নয়েজ অ্যালগরিদম ব্যবহার করে: https://github.com/ashima/webgl-noise । এর WebGL সংস্করণটি MrDoob-এর Three.js নমুনাগুলির একটি থেকে মৌখিকভাবে নেওয়া হয়েছে, এখানে: http://mrdoob.github.com/three.js/examples/webgl_terrain_dynamic.html

NoiseShader ইউনিফর্ম হিসাবে একটি সময়, একটি স্কেল এবং প্যারামিটারের একটি অফসেট সেট নেয় এবং পার্লিন শব্দের একটি চমৎকার 2D বিতরণ করে।

class NoiseShader

  uniforms:     
    "fTime"  : { type: "f", value: 1 }
    "vScale"  : { type: "v2", value: new THREE.Vector2(1,1) }
    "vOffset"  : { type: "v2", value: new THREE.Vector2(1,1) }

...

আমরা আমাদের পার্লিন নয়েজকে টেক্সচারে রেন্ডার করতে এই শেডারটি ব্যবহার করতে যাচ্ছি। এটি initNoiseShader ফাংশনে করা হয়।

initNoiseShader:->
  @noiseMap  = new THREE.WebGLRenderTarget( 256, 256, { minFilter: THREE.LinearMipmapLinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat } );
  @noiseShader = new NoiseShader()
  @noiseShader.uniforms.vScale.value.set(0.3,0.3)
  @noiseScene = new THREE.Scene()
  @noiseCameraOrtho = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2,  window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000 );
  @noiseCameraOrtho.position.z = 100
  @noiseScene.add( @noiseCameraOrtho )

  @noiseMaterial = new THREE.ShaderMaterial
      fragmentShader: @noiseShader.fragmentShader
      vertexShader: @noiseShader.vertexShader
      uniforms: @noiseShader.uniforms
      lights:false

  @noiseQuadTarget = new THREE.Mesh( new THREE.PlaneGeometry(window.innerWidth,window.innerHeight,100,100), @noiseMaterial )
  @noiseQuadTarget.position.z = -500
  @noiseScene.add( @noiseQuadTarget )

উপরের কোডটি যা করে তা হল একটি Three.js রেন্ডার টার্গেট হিসাবে noiseMap সেটআপ করা, এটিকে NoiseShader দিয়ে সজ্জিত করা, এবং তারপর এটিকে একটি অর্থোগ্রাফিক ক্যামেরা দিয়ে রেন্ডার করা, যাতে দৃষ্টিকোণ বিকৃতি এড়াতে পারে৷

যেমন আলোচনা করা হয়েছে, আমরা এখন এই টেক্সচারটিকে ভূখণ্ডের জন্য প্রধান রেন্ডারিং টেক্সচার হিসেবেও ব্যবহার করতে যাচ্ছি। বায়ু প্রভাব নিজেই কাজ করার জন্য এটি সত্যিই প্রয়োজনীয় নয়। কিন্তু এটা থাকা ভালো, যাতে আমরা আরও ভালোভাবে বুঝতে পারি যে বায়ু উৎপাদনের সাথে কী ঘটছে।

এখানে একটি টেক্সচার হিসাবে noiseMap ব্যবহার করে, initTerrain ফাংশনটি পুনরায় কাজ করা হয়েছে:

initTerrain:->
  @plane = new THREE.Mesh( new THREE.PlaneGeometry(60, 60, 2, 2), new THREE.MeshPhongMaterial( { map: @noiseMap, lights: false } ) )
  @plane.rotation.x = -Math.PI/2
  @scene.add( @plane )

এখন যেহেতু আমাদের বায়ুর টেক্সচার রয়েছে, আসুন WindMeshShader-এর দিকে নজর দেওয়া যাক, যা বায়ু অনুযায়ী ঘাসের মডেলগুলিকে বিকৃত করার জন্য দায়ী।

এই শেডারটি তৈরি করার জন্য আমরা স্ট্যান্ডার্ড Three.js MeshPhongMaterial শেডার থেকে শুরু করেছি এবং এটি পরিবর্তন করেছি। স্ক্র্যাচ থেকে শুরু না করেই কাজ করে এমন একটি শেডার দিয়ে শুরু করার এটি একটি ভাল দ্রুত এবং নোংরা উপায়।

আমরা এখানে সম্পূর্ণ শেডার কোডটি অনুলিপি করতে যাচ্ছি না (উৎস কোড ফাইলে এটি দেখতে নির্দ্বিধায়), কারণ এটির বেশিরভাগই মেশফং ম্যাটেরিয়াল শেডারের প্রতিরূপ হবে। তবে আসুন ভার্টেক্স শেডারে পরিবর্তিত, বায়ু সম্পর্কিত, অংশগুলি দেখে নেওয়া যাক।

vec4 wpos = modelMatrix * vec4( position, 1.0 );
vec4 wpos = modelMatrix * vec4( position, 1.0 );

wpos.z = -wpos.z;
vec2 totPos = wpos.xz - windMin;
vec2 windUV = totPos / windSize;
vWindForce = texture2D(tWindForce,windUV).x;

float windMod = ((1.0 - vWindForce)* windFactor ) * windScale;
vec4 pos = vec4(position , 1.0);
pos.x += windMod * windDirection.x;
pos.y += windMod * windDirection.y;
pos.z += windMod * windDirection.z;

mvPosition = modelViewMatrix *  pos;

সুতরাং, এই শেডারটি যা করে তা হল প্রথমে শীর্ষবিন্দুর 2D, xz (অনুভূমিক) অবস্থানের উপর ভিত্তি করে windUV টেক্সচার লুকআপ স্থানাঙ্ক গণনা করা। এই UV স্থানাঙ্কটি পার্লিন নয়েজ উইন্ড টেক্সচার থেকে বায়ু শক্তি, vWindForce সন্ধান করতে ব্যবহৃত হয়।

এই vWindForce মান, শীর্ষবিন্দু নির্দিষ্ট windFactor , উপরে আলোচিত কাস্টম অ্যাট্রিবিউটের সাথে সংমিশ্রিত হয়েছে, যাতে শীর্ষবিন্দুর কতটা বিকৃতি প্রয়োজন তা গণনা করা যায়। বাতাসের সামগ্রিক শক্তি নিয়ন্ত্রণ করার জন্য আমাদের কাছে একটি গ্লোবাল উইন্ডস্কেল , প্যারামিটার এবং একটি উইন্ডডিরেকশন , ভেক্টর রয়েছে, যা নির্দিষ্ট করে দেয় যে বাতাসের বিকৃতিটি কোন দিকে হতে হবে।

সুতরাং, এটি আমাদের ঘাসের টুকরোগুলির বায়ু ভিত্তিক বিকৃতি তৈরি করে। যদিও আমরা এখনও সম্পন্ন করিনি। এটি এখন যেমন, এই বিকৃতিটি স্থির, এবং এটি বাতাসের অঞ্চলের প্রভাবকে প্রকাশ করবে না।

যেমনটি আমরা উল্লেখ করেছি, আমাদের সময়ের সাথে সাথে বাতাসের এলাকা জুড়ে শব্দের টেক্সচারটি স্লাইড করতে হবে, যাতে আমাদের কাচ তরঙ্গায়িত হতে পারে।

এটি সময়ের সাথে সাথে স্থানান্তরিত করে করা হয়, vOffset ইউনিফর্ম যা NoiseShader-এ পাস করা হয়। এটি একটি vec2 পরামিতি, যা আমাদের একটি নির্দিষ্ট দিক (আমাদের বাতাসের দিক) বরাবর শব্দ অফসেট নির্দিষ্ট করতে দেয়।

আমরা এটি রেন্ডার , ফাংশনে করি, যা প্রতিটি ফ্রেমে কল করা হয়:

render: =>
  delta = @clock.getDelta()

  if @windDirection
      @noiseShader.uniforms[ "fTime" ].value += delta * @noiseSpeed
      @noiseShader.uniforms[ "vOffset" ].value.x -= (delta * @noiseOffsetSpeed) * @windDirection.x
      @noiseShader.uniforms[ "vOffset" ].value.y += (delta * @noiseOffsetSpeed) * @windDirection.z
...

আর তা হল! আমরা এইমাত্র বাতাস দ্বারা প্রভাবিত "প্রক্রিয়াগত ঘাস" দিয়ে একটি দৃশ্য তৈরি করেছি।

মিশ্রণে ধুলো যোগ করা

এখন আমাদের দৃশ্য একটু মশলা করা যাক. চলুন একটু উড়ন্ত ধুলো যোগ করি, যাতে দৃশ্যটিকে আরও আকর্ষণীয় করে তোলা যায়।

ধুলো যোগ করা
ধুলো যোগ করা

ধূলিকণা বায়ু দ্বারা প্রভাবিত হওয়ার কথা, তাই আমাদের বাতাসের দৃশ্যে চারপাশে ধুলো উড়তে থাকাটা নিখুঁতভাবে বোঝা যায়।

ধুলো একটি কণা সিস্টেম হিসাবে initDust ফাংশন সেটআপ করা হয়.

initDust:->
  for i in [0...5] by 1
      shader = new WindParticleShader()
      params = {}
      params.fragmentShader = shader.fragmentShader
      params.vertexShader   = shader.vertexShader
      params.uniforms       = shader.uniforms
      params.attributes     = { speed: { type: 'f', value: [] } }

      mat  = new THREE.ShaderMaterial(params)
      mat.map = shader.uniforms["map"].value = THREE.ImageUtils.loadCompressedTexture("textures/dust#{i}.dds")
      mat.size = shader.uniforms["size"].value = Math.random()
      mat.scale = shader.uniforms["scale"].value = 300.0
      mat.transparent = true
      mat.sizeAttenuation = true
      mat.blending = THREE.AdditiveBlending
      shader.uniforms["tWindForce"].value      = @noiseMap
      shader.uniforms[ "windMin" ].value       = new THREE.Vector2(-30,-30 )
      shader.uniforms[ "windSize" ].value      = new THREE.Vector2( 60, 60 )
      shader.uniforms[ "windDirection" ].value = @windDirection            

      geom = new THREE.Geometry()
      geom.vertices = []
      num = 130
      for k in [0...num] by 1

          setting = {}

          vert = new THREE.Vector3
          vert.x = setting.startX = THREE.Math.randFloat(@dustSystemMinX,@dustSystemMaxX)
          vert.y = setting.startY = THREE.Math.randFloat(@dustSystemMinY,@dustSystemMaxY)
          vert.z = setting.startZ = THREE.Math.randFloat(@dustSystemMinZ,@dustSystemMaxZ)

          setting.speed =  params.attributes.speed.value[k] = 1 + Math.random() * 10
          
          setting.sinX = Math.random()
          setting.sinXR = if Math.random() < 0.5 then 1 else -1
          setting.sinY = Math.random()
          setting.sinYR = if Math.random() < 0.5 then 1 else -1
          setting.sinZ = Math.random()
          setting.sinZR = if Math.random() < 0.5 then 1 else -1

          setting.rangeX = Math.random() * 5
          setting.rangeY = Math.random() * 5
          setting.rangeZ = Math.random() * 5

          setting.vert = vert
          geom.vertices.push vert
          @dustSettings.push setting

      particlesystem = new THREE.ParticleSystem( geom , mat )
      @dustSystems.push particlesystem
      @scene.add particlesystem

এখানে 130টি ধূলিকণা তৈরি হয়। এবং মনে রাখবেন যে তাদের প্রত্যেকে একটি বিশেষ WindParticleShader দিয়ে সজ্জিত হয়।

এখন, প্রতিটি ফ্রেমে, আমরা বাতাস থেকে স্বাধীনভাবে কফিস্ক্রিপ্ট ব্যবহার করে কণাগুলির চারপাশে একটু একটু করে ঘুরতে যাচ্ছি। এখানে কোড আছে.

moveDust:(delta)->

  for setting in @dustSettings

    vert = setting.vert
    setting.sinX = setting.sinX + (( 0.002 * setting.speed) * setting.sinXR)
    setting.sinY = setting.sinY + (( 0.002 * setting.speed) * setting.sinYR)
    setting.sinZ = setting.sinZ + (( 0.002 * setting.speed) * setting.sinZR) 

    vert.x = setting.startX + ( Math.sin(setting.sinX) * setting.rangeX )
    vert.y = setting.startY + ( Math.sin(setting.sinY) * setting.rangeY )
    vert.z = setting.startZ + ( Math.sin(setting.sinZ) * setting.rangeZ )

এর পাশাপাশি আমরা বায়ু অনুযায়ী প্রতিটি কণার অবস্থান অফসেট করতে যাচ্ছি। এটি WindParticleShader এ করা হয়। বিশেষ করে ভার্টেক্স শেডারে।

এই শেডারের কোডটি Three.js ParticleMaterial এর একটি পরিবর্তিত সংস্করণ, এবং এটির মূলটি দেখতে কেমন:

vec4 mvPosition;
vec4 wpos = modelMatrix * vec4( position, 1.0 );
wpos.z = -wpos.z;
vec2 totPos = wpos.xz - windMin;
vec2 windUV = totPos / windSize;
float vWindForce = texture2D(tWindForce,windUV).x;
float windMod = (1.0 - vWindForce) * windScale;
vec4 pos = vec4(position , 1.0);
pos.x += windMod * windDirection.x;
pos.y += windMod * windDirection.y;
pos.z += windMod * windDirection.z;

mvPosition = modelViewMatrix *  pos;

fSpeed = speed;
float fSize = size * (1.0 + sin(time * speed));

#ifdef USE_SIZEATTENUATION
    gl_PointSize = fSize * ( scale / length( mvPosition.xyz ) );
#else,
    gl_PointSize = fSize;
#endif

gl_Position = projectionMatrix * mvPosition;

এই ভার্টেক্স শেডারটি ঘাসের বায়ু ভিত্তিক বিকৃতির জন্য যা ছিল তার থেকে আলাদা নয়। এটি ইনপুট হিসাবে পার্লিন নয়েজ টেক্সচার নেয়, এবং ডাস্ট ওয়ার্ল্ড পজিশনের উপর নির্ভর করে এটি একটি vWindForce দেখায়, শব্দ টেক্সচারের মান। তারপরে, এটি ধুলো কণার অবস্থান পরিবর্তন করতে এই মানটি ব্যবহার করে।

রাইডার্স অন দ্য স্টর্ম

আমাদের WebGL দৃশ্যগুলির মধ্যে সবচেয়ে দুঃসাহসিক দৃশ্য সম্ভবত শেষ দৃশ্য ছিল, যেটি আপনি দেখতে পারবেন যদি আপনি বেলুনের মাধ্যমে টর্নেডোর চোখে আপনার পথটি ক্লিক করেন এবং সাইটে আপনার যাত্রার শেষ প্রান্তে পৌঁছাতে পারেন এবং আসন্ন প্রকাশের একচেটিয়া ভিডিও।

বেলুন চালানোর দৃশ্য

আমরা যখন এই দৃশ্যটি তৈরি করেছি তখন আমরা জানতাম যে আমাদের অভিজ্ঞতার জন্য একটি কেন্দ্রীয় বৈশিষ্ট্য থাকা দরকার যা প্রভাবশালী হবে। স্পিনিং টর্নেডো কেন্দ্রবিন্দু হিসাবে কাজ করবে এবং অন্যান্য বিষয়বস্তুর স্তরগুলি একটি নাটকীয় প্রভাব তৈরি করার জন্য এই বৈশিষ্ট্যটিকে ঢালাই করবে। এটি অর্জনের জন্য আমরা এই অদ্ভুত শেডারের চারপাশে সেট করা একটি ফিল্ম স্টুডিওর সমতুল্য কী তৈরি করেছি।

বাস্তবসম্মত যৌগ তৈরি করতে আমরা একটি মিশ্র পদ্ধতি ব্যবহার করেছি। কিছু ছিল চাক্ষুষ কৌশল যেমন একটি লেন্স ফ্লেয়ার ইফেক্ট তৈরি করার জন্য হালকা আকার বা বৃষ্টির ফোঁটা যা আপনি যে দৃশ্যটি দেখছেন তার উপরে স্তর হিসাবে অ্যানিমেট করে। অন্যান্য ক্ষেত্রে আমাদের চারপাশে নড়াচড়া করার জন্য সমতল পৃষ্ঠগুলি আঁকা হয়েছিল, যেমন একটি কণা সিস্টেম কোড অনুসারে নিচু উড়ন্ত মেঘের স্তরগুলি চলমান। টর্নেডোর চারপাশে প্রদক্ষিণ করা ধ্বংসাবশেষের বিটগুলি টর্নেডোর সামনে এবং পিছনে সরানোর জন্য একটি 3D দৃশ্যের স্তর ছিল।

আমাদের এইভাবে দৃশ্যটি তৈরি করার মূল কারণটি ছিল আমরা যে অন্যান্য প্রভাবগুলি প্রয়োগ করছিলাম তার সাথে ভারসাম্য রেখে টর্নেডো শেডার পরিচালনা করার জন্য আমাদের পর্যাপ্ত GPU ছিল তা নিশ্চিত করা। প্রাথমিকভাবে আমাদের বড় GPU ব্যালেন্সিং সমস্যা ছিল কিন্তু পরে এই দৃশ্যটি অপ্টিমাইজ করা হয়েছে এবং মূল দৃশ্যের তুলনায় হালকা হয়ে গেছে।

টিউটোরিয়াল: দ্য স্টর্ম শ্যাডার

চূড়ান্ত ঝড়ের ক্রম তৈরি করার জন্য অনেকগুলি বিভিন্ন কৌশল একত্রিত করা হয়েছিল, কিন্তু এই কাজের কেন্দ্রবিন্দু ছিল একটি কাস্টম GLSL শেডার যা দেখতে টর্নেডোর মতো। কণা ভিত্তিক অ্যানিমেশন এবং এমনকি টুইস্টেড জ্যামিতিক আকারের 3D অ্যানিমেশন থেকে আকর্ষণীয় জ্যামিতিক ঘূর্ণি তৈরি করতে আমরা ভার্টেক্স শেডার থেকে বিভিন্ন কৌশল চেষ্টা করেছি। কোনও প্রভাবই টর্নেডোর অনুভূতি পুনরায় তৈরি করে বা প্রক্রিয়াকরণের ক্ষেত্রে খুব বেশি প্রয়োজন বলে মনে হয় না।

একটি সম্পূর্ণ ভিন্ন প্রকল্প অবশেষে আমাদের উত্তর প্রদান করেছে। ম্যাক্স প্ল্যাঙ্ক ইনস্টিটিউট (brainflight.org) থেকে মাউসের মস্তিষ্কের ম্যাপ আউট করার জন্য বিজ্ঞানের জন্য গেমস জড়িত একটি সমান্তরাল প্রকল্প আকর্ষণীয় ভিজ্যুয়াল প্রভাব তৈরি করেছে। আমরা একটি কাস্টম ভলিউম্যাট্রিক শেডার ব্যবহার করে একটি মাউস নিউরনের ভিতরের সিনেমা তৈরি করতে পেরেছি।

একটি কাস্টম ভলিউমেট্রিক শেডার ব্যবহার করে একটি মাউস নিউরনের ভিতরে
একটি কাস্টম ভলিউমেট্রিক শেডার ব্যবহার করে একটি মাউস নিউরনের ভিতরে

আমরা দেখতে পেলাম যে মস্তিষ্কের কোষের ভেতরটা অনেকটা টর্নেডোর ফানেলের মতো দেখতে। এবং যেহেতু আমরা একটি ভলিউমেট্রিক কৌশল ব্যবহার করছিলাম আমরা জানতাম যে আমরা মহাকাশের সমস্ত দিক থেকে এই শেডারটি দেখতে পারি। আমরা ঝড়ের দৃশ্যের সাথে একত্রিত করার জন্য শেডারের রেন্ডার সেট করতে পারি, বিশেষত যদি মেঘের স্তরের নীচে এবং একটি নাটকীয় পটভূমির উপরে স্যান্ডউইচ করা হয়।

শেডার কৌশলটি একটি কৌশল জড়িত যা মূলত একটি একক GLSL শেডার ব্যবহার করে একটি সরলীকৃত রেন্ডার অ্যালগরিদম সহ একটি সম্পূর্ণ অবজেক্ট রেন্ডার করে যাকে দূরত্ব ক্ষেত্রের সাথে রে মার্চিং রেন্ডারিং বলা হয়। এই কৌশলে একটি পিক্সেল শেডার তৈরি করা হয় যা স্ক্রিনের প্রতিটি বিন্দুর জন্য একটি পৃষ্ঠের নিকটতম দূরত্ব অনুমান করে।

অ্যালগরিদমের একটি ভাল রেফারেন্স iq দ্বারা ওভারভিউতে পাওয়া যেতে পারে: রেন্ডারিং ওয়ার্ল্ডস উইথ টু ট্রায়াঙ্গেল - ইনিগো কুইলেজ । এছাড়াও glsl.heroku.com-এ শেডার্সের গ্যালারি অন্বেষণ করে, এই কৌশলটির অনেক উদাহরণ সেখানে পাওয়া যাবে যেগুলির সাথে পরীক্ষা করা যেতে পারে।

শেডারের হৃদয় মূল ফাংশন দিয়ে শুরু হয়, এটি ক্যামেরার রূপান্তর সেট আপ করে এবং একটি লুপে প্রবেশ করে যা বারবার একটি পৃষ্ঠের দূরত্ব মূল্যায়ন করে। কল RaytraceFoggy (direct_vector, max_iterations, color, color_multiplier ) হল যেখানে মূল রশ্মি মার্চিং গণনা হয়।

for(int i=0;i < number_of_steps;i++) // run the ray marching loop
{
  old_d=d;
  float shape_value=Shape(q); // find out the approximate distance to or density of the tornado cone
  float density=-shape_value;
  d=max(shape_value*step_scaling,0.0);// The max function clamps values smaller than 0 to 0

  float step_dist=d+extra_step; // The point is advanced by larger steps outside the tornado,
  //  allowing us to skip empty space quicker.

  if (density>0.0) {  // When density is positive, we are inside the cloud
    float brightness=exp(-0.6*density);  // Brightness decays exponentially inside the cloud

    // This function combines density layers to create a translucent fog
    FogStep(step_dist*0.2,clamp(density, 0.0,1.0)*vec3(1,1,1), vec3(1)*brightness, colour, multiplier); 
  }
  if(dist>max_dist || multiplier.x < 0.01) { return;  } // if we've gone too far stop, we are done
  dist+=step_dist; // add a new step in distance
  q=org+dist*dir; // trace its direction according to the ray casted
}

ধারণাটি হল যে আমরা টর্নেডোর আকারে অগ্রসর হওয়ার সাথে সাথে আমরা নিয়মিতভাবে পিক্সেলের চূড়ান্ত রঙের মানতে রঙের অবদান যোগ করি, সেইসাথে রশ্মির সাথে অস্বচ্ছতায় অবদান রাখি। এটি টর্নেডোর টেক্সচারে একটি স্তরযুক্ত নরম গুণমান তৈরি করে।

টর্নেডোর পরবর্তী মূল দিকটি হল প্রকৃত আকৃতি যা অনেকগুলি ফাংশন রচনা করে তৈরি করা হয়। এটি একটি শঙ্কু যা দিয়ে শুরু করা হয়, যা একটি জৈব রুক্ষ প্রান্ত তৈরি করার জন্য শব্দের সাথে গঠিত হয় এবং পরবর্তীতে এটির প্রধান অক্ষ বরাবর পেঁচানো হয় এবং সময়মতো ঘোরানো হয়।

mat2 Spin(float angle){
  return mat2(cos(angle),-sin(angle),sin(angle),cos(angle)); // a rotation matrix
}

// This takes noise function and makes ridges at the points where that function crosses zero
float ridged(float f){ 
  return 1.0-2.0*abs(f);
}

// the isosurface shape function, the surface is at o(q)=0 
float Shape(vec3 q) 
{
    float t=time;

    if(q.z < 0.0) return length(q);

    vec3 spin_pos=vec3(Spin(t-sqrt(q.z))*q.xy,q.z-t*5.0); // spin the coordinates in time

    float zcurve=pow(q.z,1.5)*0.03; // a density function dependent on z-depth

    // the basic cloud of a cone is perturbed with a distortion that is dependent on its spin 
    float v=length(q.xy)-1.5-zcurve-clamp(zcurve*0.2,0.1,1.0)*snoise(spin_pos*vec3(0.1,0.1,0.1))*5.0; 

    // create ridges on the tornado
    v=v-ridged(snoise(vec3(Spin(t*1.5+0.1*q.z)*q.xy,q.z-t*4.0)*0.3))*1.2; 

    return v;
}

এই ধরণের শেডার তৈরিতে জড়িত কাজটি জটিল। আপনি যে ক্রিয়াকলাপগুলি তৈরি করছেন তার বিমূর্তকরণের সাথে জড়িত সমস্যাগুলির বাইরেও গুরুতর অপ্টিমাইজেশন এবং ক্রস প্ল্যাটফর্ম সামঞ্জস্যপূর্ণ সমস্যা রয়েছে যা আপনি উত্পাদনে কাজটি ব্যবহার করার আগে আপনাকে ট্রেস এবং সমাধান করতে হবে।

সমস্যার প্রথম অংশ: আমাদের দৃশ্যের জন্য এই শেডারটি অপ্টিমাইজ করা। এটি মোকাবেলা করার জন্য আমাদের একটি "নিরাপদ" পদ্ধতি থাকা দরকার যদি শেডারটি খুব ভারী হতে চলেছে। এটি করার জন্য আমরা দৃশ্যের বাকি অংশ থেকে একটি ভিন্ন নমুনাযুক্ত রেজোলিউশনে টর্নেডো শেডারটি রচনা করেছি। এটি stormTest.coffee ফাইল থেকে (হ্যাঁ, এটি একটি পরীক্ষা ছিল!)

আমরা একটি রেন্ডার টার্গেট দিয়ে শুরু করি যা দৃশ্যের প্রস্থ এবং উচ্চতার সাথে মেলে যাতে আমরা টর্নেডো শেডার থেকে দৃশ্যের রেজোলিউশনের স্বাধীনতা পেতে পারি। এবং তারপরে আমরা যে ফ্রেম রেট পাচ্ছি তার উপর গতিশীলভাবে নির্ভরশীল ঝড় শেডারের রেজোলিউশনের ডাউনস্যাম্পলিং সিদ্ধান্ত নিই।

...
Line 1383
@tornadoRT = new THREE.WebGLRenderTarget( @SCENE_WIDTH, @SCENE_HEIGHT, paramsN )

... 
Line 1403 
# Change settings based on FPS
if @fpsCount > 0
    if @fpsCur < 20
        @tornadoSamples = Math.min( @tornadoSamples + 1, @MAX_SAMPLES )
    if @fpsCur > 25
        @tornadoSamples = Math.max( @tornadoSamples - 1, @MIN_SAMPLES )
    @tornadoW = @SCENE_WIDTH  / @tornadoSamples // decide tornado resWt
    @tornadoH = @SCENE_HEIGHT / @tornadoSamples // decide tornado resHt

অবশেষে আমরা একটি সরলীকৃত sal2x অ্যালগরিদম ব্যবহার করে টর্নেডোকে স্ক্রিনে রেন্ডার করি, (অবরুদ্ধ চেহারা এড়াতে) @line 1107 stormTest.coffee-এ। এর মানে হল যে আরও খারাপ ক্ষেত্রে আমরা একটি আরও ঝাপসা টর্নেডো শেষ করে থাকি তবে অন্তত এটি ব্যবহারকারীর কাছ থেকে নিয়ন্ত্রণ না নিয়েই কাজ করে।

পরবর্তী অপ্টিমাইজেশান পদক্ষেপের জন্য অ্যালগরিদমে ডুব দেওয়া প্রয়োজন৷ শেডারে ড্রাইভিং কম্পিউটেশনাল ফ্যাক্টর হল পুনরাবৃত্তি যা প্রতিটি পিক্সেলে পৃষ্ঠ ফাংশনের দূরত্ব আনুমানিক করার চেষ্টা করার জন্য সঞ্চালিত হয়: রেমার্চিং লুপের পুনরাবৃত্তির সংখ্যা। একটি বৃহত্তর ধাপের আকার ব্যবহার করে আমরা তার মেঘলা পৃষ্ঠের বাইরে থাকার সময় কম পুনরাবৃত্তি সহ টর্নেডো পৃষ্ঠের অনুমান পেতে পারি। ভিতরে থাকাকালীন আমরা নির্ভুলতার জন্য ধাপের আকার হ্রাস করব এবং কুয়াশাচ্ছন্ন প্রভাব তৈরি করতে মানগুলিকে মিশ্রিত করতে সক্ষম হব। এছাড়াও ঢালাই রশ্মির গভীরতার অনুমান পেতে একটি বাউন্ডিং সিলিন্ডার তৈরি করা ভাল গতি দিয়েছে।

সমস্যার পরবর্তী অংশটি ছিল নিশ্চিত করা যে এই শেডারটি বিভিন্ন ভিডিও কার্ডে চলবে। আমরা প্রতিবার কিছু পরীক্ষা করেছিলাম এবং আমরা যে সামঞ্জস্যের সমস্যাগুলির মধ্যে পড়তে পারি তার জন্য একটি অন্তর্দৃষ্টি তৈরি করা শুরু করেছি। আমরা অন্তর্দৃষ্টির চেয়ে অনেক ভালো কিছু করতে না পারার কারণ হল যে আমরা সবসময় ত্রুটির বিষয়ে ভালো ডিবাগিং তথ্য পেতে পারি না। একটি সাধারণ দৃশ্যকল্প হল শুধুমাত্র একটি GPU ত্রুটি যার সাথে আরও কিছু চলতে হবে, বা এমনকি একটি সিস্টেম ক্র্যাশ!

ক্রস ভিডিও বোর্ড সামঞ্জস্যের সমস্যাগুলির একই রকম সমাধান ছিল: নিশ্চিত করুন যে স্থির ধ্রুবকগুলি সংজ্ঞায়িত হিসাবে সুনির্দিষ্ট ডেটা টাইপের প্রবেশ করানো হয়েছে, IE: float-এর জন্য 0.0 এবং int-এর জন্য 0৷ দীর্ঘ ফাংশন লেখার সময় যত্ন নিন; একাধিক সহজ ফাংশন এবং অন্তর্বর্তী ভেরিয়েবলের মধ্যে জিনিসগুলিকে ভেঙে ফেলা বাঞ্ছনীয় কারণ কম্পাইলাররা নির্দিষ্ট ক্ষেত্রে সঠিকভাবে পরিচালনা করছে না বলে মনে হচ্ছে। নিশ্চিত করুন যে টেক্সচারগুলি 2-এর শক্তি, খুব বেশি বড় নয় এবং লুপে টেক্সচার ডেটা দেখার সময় "সাবধান" ব্যায়াম করুন৷

সামঞ্জস্যের ক্ষেত্রে আমাদের সবচেয়ে বড় সমস্যা ছিল ঝড়ের আলোর প্রভাব থেকে। আমরা টর্নেডোর চারপাশে মোড়ানো একটি প্রিমেড টেক্সচার ব্যবহার করেছি যাতে আমরা এর উইস্পে রঙ করতে পারি। এটি একটি চমত্কার প্রভাব ছিল, এবং টর্নেডোকে দৃশ্যের রঙে মিশ্রিত করা সহজ করে তুলেছিল কিন্তু অন্যান্য প্ল্যাটফর্মে চালানোর চেষ্টা করতে অনেক সময় লেগেছিল।

টর্নেডো

মোবাইল ওয়েব সাইট

মোবাইল অভিজ্ঞতা ডেস্কটপ সংস্করণের সরাসরি অনুবাদ হতে পারে না কারণ প্রযুক্তি এবং প্রক্রিয়াকরণের প্রয়োজনীয়তাগুলি খুব ভারী ছিল। আমাদের নতুন কিছু তৈরি করতে হয়েছিল, যা বিশেষভাবে মোবাইল ব্যবহারকারীকে লক্ষ্য করে।

আমরা ভেবেছিলাম যে কার্নিভাল ফটো-বুথ ডেস্কটপ থেকে একটি মোবাইল ওয়েব অ্যাপ্লিকেশন হিসেবে পাওয়া ভালো হবে যা ব্যবহারকারীর মোবাইল ক্যামেরা ব্যবহার করবে। এমন কিছু যা আমরা এখন পর্যন্ত দেখিনি।

স্বাদ যোগ করার জন্য, আমরা CSS3 এ 3D রূপান্তর কোড করেছি। এটিকে জাইরোস্কোপ এবং অ্যাক্সিলোমিটারের সাথে সংযুক্ত করার পরে, আমরা অভিজ্ঞতায় অনেক গভীরতা যোগ করতে সক্ষম হয়েছি। সাইটটি আপনি যেভাবে আপনার ফোন ধরে রাখেন, সরান এবং দেখেন তাতে সাড়া দেয়।

এই নিবন্ধটি লেখার সময়, আমরা ভেবেছিলাম কীভাবে মোবাইল ডেভেলপমেন্ট প্রক্রিয়াটি মসৃণভাবে চালানো যায় সে সম্পর্কে আপনাকে কিছু ইঙ্গিত দেওয়া মূল্যবান হবে। এখানে তারা যান! এগিয়ে যান এবং দেখুন আপনি এটি থেকে কি শিখতে পারেন!

মোবাইল টিপস এবং কৌশল

প্রিলোডার এমন কিছু যা প্রয়োজন, এমন কিছু নয় যা এড়ানো উচিত। আমরা জানি, মাঝে মাঝে পরেরটি ঘটে। এটি প্রধানত কারণ আপনার প্রজেক্ট বাড়ার সাথে সাথে আপনার প্রিলোড করা জিনিসগুলির তালিকা বজায় রাখতে হবে। কি খারাপ, আপনি যদি বিভিন্ন সংস্থান এবং একই সময়ে অনেকগুলিকে টেনে নিচ্ছেন তবে কীভাবে আপনার লোডিং অগ্রগতি গণনা করা উচিত তা খুব স্পষ্ট নয়। এখানেই আমাদের কাস্টম এবং খুব সাধারণ বিমূর্ত ক্লাস 'টাস্ক' কাজে আসে। এর মূল ধারণাটি হল অন্তহীনভাবে নেস্টেড কাঠামোকে অনুমতি দেওয়া যেখানে একটি টাস্কের নিজস্ব সাব-টাস্ক থাকতে পারে, যার তাদের ইত্যাদি থাকতে পারে... উপরন্তু, প্রতিটি টাস্ক তার সাব-টাস্কের অগ্রগতির সাথে সাপেক্ষে তার অগ্রগতি গণনা করে (কিন্তু পিতামাতার অগ্রগতির সাথে নয়)। Task থেকে প্রাপ্ত সমস্ত MainPreloadTask, AssetPreloadTask এবং TemplatePreFetchTask তৈরি করে, আমরা একটি কাঠামো তৈরি করেছি যা দেখতে এইরকম:

প্রিলোডার

এই ধরনের পদ্ধতি এবং টাস্ক ক্লাসের জন্য ধন্যবাদ, আমরা সহজেই বিশ্বব্যাপী অগ্রগতি (MainPreloadTask), বা সম্পদের অগ্রগতি (AssetPreloadTask), বা টেমপ্লেট লোড করার অগ্রগতি (TemplatePreFetchTask) সহজেই জানতে পারি। এমনকি একটি নির্দিষ্ট ফাইলের অগ্রগতি। এটি কীভাবে করা হয় তা দেখতে, /m/javascripts/raw/util/Task.js-এ টাস্ক ক্লাস এবং /m/javascripts/preloading/task- এ প্রকৃত কার্য বাস্তবায়ন দেখুন। উদাহরণ স্বরূপ, আমরা কিভাবে /m/javascripts/preloading/task/MainPreloadTask.js ক্লাস সেট আপ করি তা থেকে এটি একটি নির্যাস যা আমাদের চূড়ান্ত প্রিলোডিং র্যাপার:

Package('preloading.task', [
  Import('util.Task'),
...

  Class('public MainPreloadTask extends Task', {

    _public: {
      
  MainPreloadTask : function() {
        
    var subtasks = [
      new AssetPreloadTask([
        {name: 'cutout/cutout-overlay-1', ext: 'png', type: ImagePreloader.TYPE_BACKGROUND, responsive: true},
        {name: 'journey/scene1', ext: 'jpg', type: ImagePreloader.TYPE_IMG, responsive: false}, ...
...
      ]),

      new TemplatePreFetchTask([
        'page.HomePage',
        'page.CutoutPage',
        'page.JourneyToOzPage1', ...
...
      ])
    ];
    
    this._super(subtasks);

      }
    }
  })
]);

/m/javascripts/preloading/task/subtask/AssetPreloadTask.js ক্লাসে, এটি MainPreloadTask-এর সাথে কীভাবে যোগাযোগ করে (শেয়ার করা টাস্ক বাস্তবায়নের মাধ্যমে) লক্ষ্য করার পাশাপাশি, আমরা কীভাবে প্ল্যাটফর্ম নির্ভর সম্পদগুলিকে লোড করি তাও লক্ষ করার মতো। মূলত, আমাদের চার ধরণের চিত্র রয়েছে। মোবাইল স্ট্যান্ডার্ড (.ext, যেখানে ext হল ফাইল এক্সটেনশন, সাধারণত .png বা .jpg), মোবাইল রেটিনা (-2x.ext), ট্যাবলেট স্ট্যান্ডার্ড (-tab.ext) এবং ট্যাবলেট রেটিনা (-tab-2x.ext)। MainPreloadTask-এ শনাক্তকরণ এবং চারটি অ্যাসেট অ্যারে হার্ডকোড করার পরিবর্তে, আমরা শুধু বলি প্রিলোড করার জন্য অ্যাসেটের নাম এবং এক্সটেনশন কী এবং অ্যাসেটটি প্ল্যাটফর্ম নির্ভর হলে (প্রতিক্রিয়াশীল = সত্য/মিথ্যা)। তারপর, AssetPreloadTask আমাদের জন্য ফাইলের নাম তৈরি করবে:

resolveAssetUrl : function(assetName, extension, responsive) {
  return AssetPreloadTask.ASSETS_ROOT + assetName + (responsive === true ? ((Detection.getInstance().tablet ? '-tab' : '') + (Detection.getInstance().retina ? '-2x' : '')) : '') + '.' +  extension;
}

ক্লাস চেইনের আরও নিচে, প্রকৃত কোড যা সম্পদ প্রিলোডিং করে তা নিম্নরূপ দেখায় ( /m/javascripts/raw/util/ImagePreloader.js ):

loadUrl : function(url, type, completeHandler) {
  if(type === ImagePreloader.TYPE_BACKGROUND) {
    var $bg = $('<div>').hide().css('background-image', 'url(' + url + ')');
    this.$preloadContainer.append($bg);
  } else {
    var $img= $('<img />').attr('src', url).hide();
    this.$preloadContainer.append($img);
  }

  var image = new Image();
  this.cache[this.generateKey(url)] = image;
  image.onload = completeHandler;
  image.src = url;
}

generateKey : function(url) {
  return encodeURIComponent(url);
}

টিউটোরিয়াল: HTML5 ফটো বুথ (iOS6/Android)

OZ মোবাইল ডেভেলপ করার সময়, আমরা খুঁজে পেয়েছি যে আমরা কাজের পরিবর্তে ফটো বুথের সাথে খেলার জন্য অনেক সময় ব্যয় করি :D এটি শুধুমাত্র মজার কারণ। তাই আমরা আপনার সাথে খেলার জন্য একটি ডেমো তৈরি করেছি।

মোবাইল ফটো বুথ
মোবাইল ফটো বুথ

আপনি এখানে একটি লাইভ ডেমো দেখতে পারেন (এটি আপনার iPhone বা Android ফোনে চালান):

http://u9html5rocks.appspot.com/demos/mobile_photo_booth

এটি সেট আপ করার জন্য, আপনার একটি বিনামূল্যের Google App Engine অ্যাপ্লিকেশন উদাহরণ প্রয়োজন যেখানে আপনি ব্যাকএন্ড চালাতে পারেন৷ সামনের প্রান্তের কোডটি জটিল নয় তবে কয়েকটি সম্ভাব্য গোটচা রয়েছে। আসুন এখন সেগুলি দিয়ে যাই:

  1. অনুমোদিত চিত্র ফাইলের ধরন আমরা চাই যে লোকেরা কেবল ছবি আপলোড করতে সক্ষম হোক (যেহেতু এটি একটি ফটো বুথ, ভিডিও বুথ নয়)। তাত্ত্বিকভাবে, আপনি HTML এ ফিল্টারটি নির্দিষ্ট করতে পারেন, নিম্নরূপ: input id="fileInput" class="fileInput" type="file" name="file" accept="image/*" তবে এটি iOS এ কাজ করে বলে মনে হচ্ছে শুধুমাত্র, তাই একবার একটি ফাইল নির্বাচন করা হলে আমাদের RegExp-এর বিরুদ্ধে একটি অতিরিক্ত চেক যোগ করতে হবে:
   this.$fileInput.fileupload({
          
   dataType: 'json',
   autoUpload : true,
   
   add : function(e, data) {
     if(!data.files[0].name.match(/(\.|\/)(gif|jpe?g|png)$/i)) {
      return self.onFileTypeNotSupported();
     }
   }
   });
  1. একটি আপলোড বা ফাইল নির্বাচন বাতিল করা আরেকটি অসঙ্গতি যা আমরা উন্নয়ন প্রক্রিয়ার সময় লক্ষ্য করেছি তা হল কিভাবে বিভিন্ন ডিভাইস একটি বাতিল ফাইল নির্বাচনকে বিজ্ঞপ্তি দেয়। iOS ফোন এবং ট্যাবলেট কিছুই করে না, তারা মোটেও অবহিত করে না। তাই এই ক্ষেত্রে আমাদের কোন বিশেষ পদক্ষেপের প্রয়োজন নেই, যাইহোক, Android ফোনগুলি যেভাবেই হোক add() ফাংশনটি ট্রিগার করে, এমনকি কোনো ফাইল নির্বাচন না করলেও। এটি কীভাবে পূরণ করবেন তা এখানে:
    add : function(e, data) {

    if(data.files.length === 0 || (data.files[0].size === 0 && data.files[0].name === "" && data.files[0].fileName === "")) {
            
    return self.onNoFileSelected();

    } else if(data.files.length > 1) {

    return self.onMultipleFilesSelected();            
    }
    }

বাকিগুলি প্ল্যাটফর্ম জুড়ে বরং মসৃণ কাজ করে। আনন্দ কর!

উপসংহার

ফাইন্ড ইওর ওয়ে টু ওজেডের বিশাল আকার এবং জড়িত বিভিন্ন প্রযুক্তির বিস্তৃত মিশ্রণের প্রেক্ষিতে, এই নিবন্ধে আমরা যে পদ্ধতিগুলি ব্যবহার করেছি তার মধ্যে কয়েকটি কভার করতে সক্ষম হয়েছি।

আপনি যদি পুরো এনচিলাডা অন্বেষণ করতে আগ্রহী হন, তাহলে এই লিঙ্কে আপনার পথের সন্ধান করুন এর পুরো সোর্স কোডটি দেখতে নির্দ্বিধায় দেখুন।

ক্রেডিট

সম্পূর্ণ ক্রেডিট তালিকার জন্য এখানে ক্লিক করুন

তথ্যসূত্র