shaders একটি ভূমিকা

Paul Lewis

ভূমিকা

প্রথমে Three.js-এর ভূমিকাটি পড়ুন, কারণ এটিই এই নথির ভিত্তি।

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

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

১. আমাদের দুটি শেডার

WebGL ফিক্সড পাইপলাইন ব্যবহারের সুযোগ দেয় না, যার সহজ অর্থ হলো এটি আপনাকে সরাসরি আপনার কোড রেন্ডার করার কোনো উপায় দেয় না। তবে এটি প্রোগ্রামেবল পাইপলাইন ব্যবহারের সুযোগ দেয়, যা আরও শক্তিশালী কিন্তু বোঝা ও ব্যবহার করা আরও কঠিন। সংক্ষেপে বলতে গেলে, প্রোগ্রামেবল পাইপলাইনের অর্থ হলো প্রোগ্রামার হিসেবে আপনাকে ভার্টেক্স এবং অন্যান্য উপাদান স্ক্রিনে রেন্ডার করার দায়িত্ব নিতে হবে। শেডার এই পাইপলাইনেরই একটি অংশ, এবং এগুলো দুই প্রকারের হয়:

  1. ভার্টেক্স শেডার
  2. ফ্র্যাগমেন্ট শেডার

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

২. ভার্টেক্স শেডার

একটি সাধারণ আদিম আকৃতি, যেমন একটি গোলক, ধরা যাক। এটি ভার্টেক্স দিয়ে গঠিত, তাই না? একটি ভার্টেক্স শেডারকে এই ভার্টেক্সগুলোর প্রত্যেকটি একে একে দেওয়া হয় এবং এটি সেগুলোকে নিয়ে কাজ করতে পারে। প্রতিটি ভার্টেক্স নিয়ে আসলে কী করা হবে, তা ভার্টেক্স শেডারের উপরই নির্ভর করে, কিন্তু এর একটি দায়িত্ব আছে: এটিকে কোনো এক সময়ে gl_Position নামক একটি জিনিস সেট করতেই হবে, যা একটি 4D ফ্লোট ভেক্টর এবং স্ক্রিনে ভার্টেক্সটির চূড়ান্ত অবস্থান। এই প্রক্রিয়াটি নিজে থেকেই বেশ আকর্ষণীয়, কারণ আমরা এখানে একটি 3D অবস্থানকে (x,y,z সহ একটি ভার্টেক্স) একটি 2D স্ক্রিনে নিয়ে আসা বা প্রজেক্ট করার কথা বলছি। সৌভাগ্যবশত, আমরা যদি Three.js-এর মতো কিছু ব্যবহার করি, তাহলে বিষয়টিকে খুব বেশি জটিল না করেই gl_Position সেট করার একটি সংক্ষিপ্ত উপায় আমাদের কাছে আছে।

৩. ফ্র্যাগমেন্ট শেডার্স

তো, আমাদের কাছে ভার্টেক্সসহ একটি অবজেক্ট আছে এবং আমরা সেগুলোকে 2D স্ক্রিনে প্রজেক্ট করেছি, কিন্তু আমরা যে রংগুলো ব্যবহার করব সেগুলোর কী হবে? টেক্সচারিং এবং লাইটিংয়েরই বা কী হবে? ঠিক এই কাজগুলোর জন্যই ফ্র্যাগমেন্ট শেডার রয়েছে। ভার্টেক্স শেডারের মতোই, ফ্র্যাগমেন্ট শেডারেরও একটিই আবশ্যিক কাজ আছে: এটিকে অবশ্যই gl_FragColor ভ্যারিয়েবলটি সেট বা ডিসকার্ড করতে হবে, যা হলো আরেকটি 4D ফ্লোট ভেক্টর এবং এটিই আমাদের ফ্র্যাগমেন্টের চূড়ান্ত রং।

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

৪. শেডার ভেরিয়েবল

আপনি তিন ধরনের ভেরিয়েবল ডিক্লারেশন করতে পারেন: ইউনিফর্ম , অ্যাট্রিবিউট এবং ভ্যারিইং । এগুলো বিভ্রান্তিকর মনে হতে পারে, কারণ অন্য কোনো প্রচলিত ভাষার সাথে এগুলোর মিল নেই। এগুলোকে আপনি এভাবে ভাবতে পারেন:

  1. ইউনিফর্মগুলো ভার্টেক্স শেডার এবং ফ্র্যাগমেন্ট শেডার উভয়টিতেই পাঠানো হয় এবং এতে এমন মান থাকে যা রেন্ডার করা সম্পূর্ণ ফ্রেম জুড়ে একই থাকে। এর একটি ভালো উদাহরণ হতে পারে একটি আলোর অবস্থান।

  2. অ্যাট্রিবিউট হলো এমন মান যা প্রতিটি ভার্টেক্সে প্রয়োগ করা হয়। অ্যাট্রিবিউট শুধুমাত্র ভার্টেক্স শেডারের জন্যই উপলব্ধ। এটি এমন হতে পারে যে প্রতিটি ভার্টেক্সের একটি স্বতন্ত্র রঙ থাকবে। অ্যাট্রিবিউটের সাথে ভার্টেক্সের একটি এক-এক সম্পর্ক রয়েছে।

  3. ভ্যারিয়িং ভেরিয়েবল হলো ভার্টেক্স শেডারে ঘোষিত এমন ভেরিয়েবল যা আমরা ফ্র্যাগমেন্ট শেডারের সাথে শেয়ার করতে চাই। এটি করার জন্য, আমাদের নিশ্চিত করতে হবে যে আমরা ভার্টেক্স শেডার এবং ফ্র্যাগমেন্ট শেডার উভয় স্থানেই একই টাইপ ও নামের একটি ভ্যারিয়িং ভেরিয়েবল ঘোষণা করেছি। এর একটি ক্লাসিক ব্যবহার হলো একটি ভার্টেক্সের নর্মাল, কারণ এটি লাইটিং ক্যালকুলেশনে ব্যবহার করা যেতে পারে।

পরবর্তীতে আমরা তিন ধরনের পদ্ধতিই ব্যবহার করব, যাতে এগুলো বাস্তবে কীভাবে প্রয়োগ করা হয় সে সম্পর্কে আপনি একটি ধারণা পেতে পারেন।

না, আপনি তৈরি করতে পারেন এমন সবচেয়ে সহজ শেডারগুলোর একটি দেখুন।

৫. বনজোর্নো ওয়ার্ল্ড

তাহলে, এই হলো ভার্টেক্স শেডারের হ্যালো ওয়ার্ল্ড:

/**
* Multiply each vertex by the model-view matrix
* and the projection matrix (both provided by
* Three.js) to get a final vertex position
*/
void main() {
gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(position,1.0);
}   

এবং ফ্র্যাগমেন্ট শেডারের ক্ষেত্রেও একই কথা প্রযোজ্য:

/**
* Set the color to a lovely pink.
* Note that the color is a 4D Float
* Vector, R,G,B and A and each part
* runs from 0.0 to 1.0
*/
void main() {
gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
}

তবে খুব বেশি জটিল নয়, তাই না?

ভার্টেক্স শেডারে Three.js আমাদের দুটি ইউনিফর্ম পাঠায়। এই দুটি ইউনিফর্ম হলো ৪ডি ম্যাট্রিক্স, যাদেরকে মডেল-ভিউ ম্যাট্রিক্স এবং প্রোজেকশন ম্যাট্রিক্স বলা হয়। এগুলো ঠিক কীভাবে কাজ করে তা আপনার জানার প্রয়োজন নেই, যদিও সম্ভব হলে কোনো কিছু কীভাবে কাজ করে তা বোঝা সবসময়ই ভালো। সংক্ষেপে বলতে গেলে, এগুলোর মাধ্যমেই ভার্টেক্সের ৩ডি অবস্থানকে স্ক্রিনের চূড়ান্ত ২ডি অবস্থানে প্রজেক্ট করা হয়।

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

৬. MeshShaderMaterial ব্যবহার করা

আচ্ছা, আমরা একটি শেডার সেট আপ করেছি, কিন্তু Three.js-এর সাথে এটি কীভাবে ব্যবহার করব?

/**
* Assume we have jQuery to hand and pull out
* from the DOM the two snippets of text for
* each of our shaders
*/
var shaderMaterial = new THREE.MeshShaderMaterial({
vertexShader:   $('vertexshader').text(),
fragmentShader: $('fragmentshader').text()
});

সেখান থেকে Three.js আপনার দেওয়া মেটেরিয়ালযুক্ত মেশটির সাথে সংযুক্ত শেডারগুলো কম্পাইল ও রান করবে। সত্যি বলতে, এর চেয়ে সহজ আর কিছু হয় না। অবশ্য, এর চেয়েও সহজ উপায় হয়তো আছে, কিন্তু আমরা আপনার ব্রাউজারে চলমান একটি 3D নিয়ে কথা বলছি, তাই আমি ধরে নিচ্ছি আপনি কিছুটা জটিলতা আশা করেন।

আমরা আমাদের MeshShaderMaterial-এ আরও দুটি প্রোপার্টি যোগ করতে পারি: uniforms এবং attributes। এ দুটিই ভেক্টর, ইন্টিজার বা ফ্লোট নিতে পারে, কিন্তু আমি আগেই যেমন বলেছি, uniforms পুরো ফ্রেম বা সমস্ত ভার্টেক্সের জন্য একই থাকে, তাই এগুলোর মান সাধারণত একক হয়। অন্যদিকে, Attributes হলো প্রতি-ভার্টেক্স ভ্যারিয়েবল, তাই এগুলো একটি অ্যারে হবে বলে আশা করা হয়। attributes অ্যারের মানের সংখ্যা এবং মেশের ভার্টেক্সের সংখ্যার মধ্যে একটি এক-এক সম্পর্ক থাকা উচিত।

৭. পরবর্তী পদক্ষেপ

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

৮. নকল আলো

এখন, রঙটি এমনভাবে আপডেট করুন যাতে এটি একটি সমতল রঙের বস্তু না থাকে। আমরা দেখতে পারতাম Three.js কীভাবে লাইটিং পরিচালনা করে, কিন্তু আমি নিশ্চিত আপনি বুঝতে পারছেন যে এটি আমাদের প্রয়োজনের চেয়ে বেশি জটিল, তাই আমরা এটি নকল করব। আপনার অবশ্যই Three.js-এর অংশ হিসেবে থাকা চমৎকার শেডারগুলো এবং ক্রিস মিল্ক ও গুগলের অসাধারণ WebGL প্রজেক্ট, রোম- এর শেডারগুলো দেখা উচিত। আমাদের শেডারগুলোতে ফিরে আসা যাক। আমরা আমাদের ভার্টেক্স শেডার আপডেট করব যাতে প্রতিটি ভার্টেক্সের নর্মাল ফ্র্যাগমেন্ট শেডারে সরবরাহ করা যায়। আমরা এটি একটি ভ্যারিইং দিয়ে করি:

// create a shared variable for the
// VS and FS containing the normal
varying vec3 vNormal;

void main() {

// set the vNormal value with
// the attribute value passed
// in by Three.js
vNormal = normal;

gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(position,1.0);
}

এবং ফ্র্যাগমেন্ট শেডারে আমরা একই ভেরিয়েবল নামটি সেট করব এবং তারপরে ভার্টেক্স নর্মালের সাথে এমন একটি ভেক্টরের ডট প্রোডাক্ট ব্যবহার করব যা গোলকের উপর এবং ডান দিক থেকে আসা একটি আলোকে প্রতিনিধিত্ব করে। এর চূড়ান্ত ফলাফল হিসেবে আমরা একটি 3D প্যাকেজের ডিরেকশনাল লাইটের মতো একটি প্রভাব পাই।

// same name and type as VS
varying vec3 vNormal;

void main() {

// calc the dot product and clamp
// 0 -> 1 rather than -1 -> 1
vec3 light = vec3(0.5,0.2,1.0);
    
// ensure it's normalized
light = normalize(light);

// calculate the dot product of
// the light to the vertex normal
float dProd = max(0.0, dot(vNormal, light));

// feed into our frag color
gl_FragColor = vec4(dProd, dProd, dProd, 1.0);

}

সুতরাং, ডট প্রোডাক্ট কাজ করার কারণ হলো, দুটি ভেক্টর দেওয়া থাকলে এটি এমন একটি সংখ্যা দেয় যা বলে দেয় ভেক্টর দুটি কতটা 'সদৃশ'। নর্মালাইজড ভেক্টরের ক্ষেত্রে, যদি তারা হুবহু একই দিকে নির্দেশ করে, তাহলে মান হয় ১। আর যদি তারা বিপরীত দিকে নির্দেশ করে, তাহলে মান হয় -১। আমরা সেই সংখ্যাটি নিয়ে আমাদের লাইটিং-এর ক্ষেত্রে প্রয়োগ করি। তাই, উপরের ডানদিকের একটি ভার্টেক্সের মান হবে ১-এর কাছাকাছি বা সমান (সম্পূর্ণ আলোকিত), যেখানে পাশের একটি ভার্টেক্সের মান হবে ০-এর কাছাকাছি এবং পেছনের দিকে হবে -১। যেকোনো নেগেটিভ মানের জন্য আমরা মানটিকে ০-তে সীমাবদ্ধ করে দিই, কিন্তু যখন আপনি সংখ্যাগুলো বসান, তখন আমরা যে সাধারণ লাইটিং দেখছি, সেটিই পাওয়া যায়।

এরপর কী? আচ্ছা, কিছু ভার্টেক্সের অবস্থান নিয়ে একটু নাড়াচাড়া করে দেখলে ভালোই হবে।

৯. বৈশিষ্ট্য

এখন আমি চাই আমরা একটি অ্যাট্রিবিউট ব্যবহার করে প্রতিটি ভার্টেক্সের সাথে একটি র‍্যান্ডম সংখ্যা যুক্ত করি। আমরা এই সংখ্যাটি ব্যবহার করে ভার্টেক্সটিকে তার নর্মাল বরাবর বাইরের দিকে ঠেলে দেব। এর ফলে এক ধরনের অদ্ভুত স্পাইক বল তৈরি হবে যা প্রতিবার পেজ রিফ্রেশ করার সাথে সাথে পরিবর্তিত হবে। এটি এখনই অ্যানিমেটেড হবে না (সেটি পরে করা হবে), কিন্তু কয়েকবার পেজ রিফ্রেশ করলেই আপনি দেখতে পাবেন যে এটি র‍্যান্ডমাইজড হয়ে গেছে।

প্রথমে ভার্টেক্স শেডারে অ্যাট্রিবিউটটি যোগ করে শুরু করুন:

attribute float displacement;
varying vec3 vNormal;

void main() {

vNormal = normal;

// push the displacement into the three
// slots of a 3D vector so it can be
// used in operations with other 3D
// vectors like positions and normals
vec3 newPosition = position +
                    normal *
                    vec3(displacement);

gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(newPosition,1.0);
}

এটা দেখতে কেমন লাগছে?

আসলে খুব বেশি পার্থক্য নেই! এর কারণ হলো MeshShaderMaterial-এ অ্যাট্রিবিউটটি সেট করা হয়নি, তাই শেডারটি কার্যকরভাবে একটি শূন্য মান ব্যবহার করছে। এই মুহূর্তে এটি এক প্রকার প্লেসহোল্ডারের মতো কাজ করছে। একটু পরেই আমরা জাভাস্ক্রিপ্টে MeshShaderMaterial-এ অ্যাট্রিবিউটটি যোগ করব এবং Three.js স্বয়ংক্রিয়ভাবে আমাদের জন্য এই দুটিকে সংযুক্ত করে দেবে।

আরও একটি উল্লেখযোগ্য বিষয় হলো, আমাকে আপডেট করা অবস্থানটি একটি নতুন vec3 ভেরিয়েবলে অ্যাসাইন করতে হয়েছিল, কারণ মূল অ্যাট্রিবিউটটি, অন্য সব অ্যাট্রিবিউটের মতোই, রিড-অনলি ছিল।

১০. MeshShaderMaterial আপডেট করা

আমাদের ডিসপ্লেসমেন্টকে শক্তি জোগানোর জন্য প্রয়োজনীয় অ্যাট্রিবিউট দিয়ে সরাসরি আমাদের MeshShaderMaterial আপডেট করা শুরু করুন। মনে রাখবেন: অ্যাট্রিবিউটগুলো হলো প্রতি-ভার্টেক্স মান, তাই আমাদের গোলকের প্রতিটি ভার্টেক্সের জন্য একটি করে মান প্রয়োজন। যেমন:

var attributes = {
displacement: {
    type: 'f', // a float
    value: [] // an empty array
}
};

// create the material and now
// include the attributes property
var shaderMaterial = new THREE.MeshShaderMaterial({
attributes:     attributes,
vertexShader:   $('#vertexshader').text(),
fragmentShader: $('#fragmentshader').text()
});

// now populate the array of attributes
var vertices = sphere.geometry.vertices;
var values = attributes.displacement.value
for(var v = 0; v < vertices.length; v++) {
values.push(Math.random() * 30);
}

এখন আমরা একটি বিকৃত গোলক দেখতে পাচ্ছি, কিন্তু মজার ব্যাপার হলো এই যে, সমস্ত সরণ ঘটছে জিপিইউ-তে।

১১. এটিকে অ্যানিমেট করুন

আমাদের এটা অবশ্যই অ্যানিমেট করা উচিত। আমরা এটা কীভাবে করব? আচ্ছা, এর জন্য আমাদের দুটো জিনিস প্রস্তুত করতে হবে:

  1. প্রতিটি ফ্রেমে কী পরিমাণ সরণ প্রয়োগ করা হবে, তা অ্যানিমেট করার জন্য একটি ইউনিফর্ম ব্যবহার করা হয়। এর জন্য আমরা সাইন বা কোসাইন ব্যবহার করতে পারি, কারণ এদের মান -১ থেকে ১ পর্যন্ত হয়ে থাকে।
  2. JS-এ একটি অ্যানিমেশন লুপ

আমরা MeshShaderMaterial এবং Vertex Shader উভয়টিতেই ইউনিফর্মটি যোগ করব। প্রথমে Vertex Shader:

uniform float amplitude;
attribute float displacement;
varying vec3 vNormal;

void main() {

vNormal = normal;

// multiply our displacement by the
// amplitude. The amp will get animated
// so we'll have animated displacement
vec3 newPosition = position +
                    normal *
                    vec3(displacement *
                        amplitude);

gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(newPosition,1.0);
}

এরপর আমরা MeshShaderMaterial আপডেট করি:

// add a uniform for the amplitude
var uniforms = {
amplitude: {
    type: 'f', // a float
    value: 0
}
};

// create the final material
var shaderMaterial = new THREE.MeshShaderMaterial({
uniforms:       uniforms,
attributes:     attributes,
vertexShader:   $('#vertexshader').text(),
fragmentShader: $('#fragmentshader').text()
});

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

আমাদের জাভাস্ক্রিপ্টে এখন রেন্ডার কলটিকে একটি ফাংশনের মধ্যে রাখতে হবে এবং তারপর requestAnimationFrame ব্যবহার করে সেটিকে কল করতে হবে। এর ভেতরে আমাদের ইউনিফর্মের ভ্যালুটিও আপডেট করতে হবে।

var frame = 0;
function update() {

// update the amplitude based on
// the frame value
uniforms.amplitude.value = Math.sin(frame);
frame += 0.1;

renderer.render(scene, camera);

// set up the next call
requestAnimFrame(update);
}
requestAnimFrame(update);

উপসংহার

আর এটাই! এখন আপনি দেখতে পাচ্ছেন যে এটি একটি অদ্ভুত (এবং কিছুটা পরাবাস্তব) স্পন্দনশীল ভঙ্গিতে অ্যানিমেট হচ্ছে।

শেডার বিষয়টি নিয়ে আরও অনেক কিছু আলোচনা করার আছে, এবং আমি আশা করি এই ভূমিকাটি আপনার জন্য সহায়ক হয়েছে। এখন আপনি শেডার দেখলে তা বুঝতে পারবেন এবং নিজের কিছু চমৎকার শেডার তৈরি করার আত্মবিশ্বাস অর্জন করবেন।