Three.js ব্যবহার করে এক মিলিয়ন অক্ষর অ্যানিমেটিং করা

Ilmari Heikkinen

ভূমিকা

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

আপনি যদি একটি ঐতিহ্যগত জাভাস্ক্রিপ্ট অ্যানিমেশন ব্যাকগ্রাউন্ড থেকে আসছেন, তাহলে এই সব পাগলামি মনে হবে। প্রতি ফ্রেম আপডেট করা দুই মিলিয়ন ত্রিভুজ নিশ্চিতভাবে এমন কিছু নয় যা আপনি আজ জাভাস্ক্রিপ্টের সাথে করতে চান। কিন্তু সৌভাগ্যবশত আমাদের কাছে WebGL আছে, যা আমাদের আধুনিক GPU-এর অসাধারণ শক্তিতে ট্যাপ করতে দেয়। এবং দুই মিলিয়ন অ্যানিমেটেড ত্রিভুজ একটি আধুনিক জিপিইউ এবং কিছু শেডার ম্যাজিকের সাথে বেশ সম্ভব।

দক্ষ WebGL কোড লেখা

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

কিন্তু সত্যিই দ্রুত যেতে, আপনাকে আপনার বেশিরভাগ গণনাকে শেডারে ঠেলে দিতে হবে। যে আমি এখানে কি করার চেষ্টা করছি. শেডার ব্যবহার করে এক মিলিয়ন অক্ষর অ্যানিমেট করুন।

নিবন্ধের কোডটি Three.js লাইব্রেরি ব্যবহার করে, যা WebGL কোড লেখা থেকে সমস্ত ক্লান্তিকর বয়লারপ্লেটকে বিমূর্ত করে। WebGL স্টেট সেটআপ এবং এরর হ্যান্ডলিং এর শত শত লাইন লেখার পরিবর্তে, Three.js এর সাথে আপনাকে শুধুমাত্র কয়েকটি লাইন কোড লিখতে হবে। Three.js থেকে WebGL শেডার সিস্টেমে ট্যাপ করাও সহজ।

একক ড্র কল ব্যবহার করে একাধিক অবজেক্ট আঁকা

আপনি কিভাবে একটি একক ড্র কল ব্যবহার করে একাধিক বস্তু আঁকতে পারেন তার একটি ছোট ছদ্ম-কোড উদাহরণ এখানে। ঐতিহ্যগত উপায় হল এইরকম এক সময়ে একটি বস্তু আঁকা:

for (var i=0; i<objects.length; i++) {
  // each added object requires a separate WebGL draw call
  scene.add(createNewObject(objects[i]));
}
renderer.render(scene, camera);

কিন্তু উপরের পদ্ধতিতে প্রতিটি বস্তুর জন্য একটি পৃথক ড্র কল প্রয়োজন। একসাথে একাধিক বস্তু আঁকতে, আপনি বস্তুগুলিকে একটি একক জ্যামিতিতে বান্ডিল করতে পারেন এবং একটি একক ড্র কল দিয়ে দূরে যেতে পারেন:

var geo = new THREE.Geometry();
for (var i=0; i<objects.length; i++) {
  // bundle the objects into a single geometry
  // so that they can be drawn with a single draw call
  addObjectToGeometry(geo, objects[i]);
}
// GOOD! Only one object to add to the scene!
scene.add(new THREE.Mesh(geo, material));
renderer.render(scene, camera);

ঠিক আছে, এখন যেহেতু আপনি প্রাথমিক ধারণা পেয়েছেন, আসুন ডেমো লেখাতে ফিরে যাই এবং সেই মিলিয়ন অক্ষরগুলিকে অ্যানিমেট করা শুরু করি!

জ্যামিতি এবং টেক্সচার সেট আপ করা হচ্ছে

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

ক্যানভাস 2D ব্যবহার করে আমি কীভাবে অক্ষর টেক্সচার তৈরি করি তা এখানে:

var fontSize = 16;

// The square letter texture will have 16 * 16 = 256 letters, enough for all 8-bit characters.
var lettersPerSide = 16;

var c = document.createElement('canvas');
c.width = c.height = fontSize*lettersPerSide;
var ctx = c.getContext('2d');
ctx.font = fontSize+'px Monospace';

// This is a magic number for aligning the letters on rows. YMMV.
var yOffset = -0.25;

// Draw all the letters to the canvas.
for (var i=0,y=0; y<lettersPerSide; y++) {
  for (var x=0; x<lettersPerSide; x++,i++) {
    var ch = String.fromCharCode(i);
    ctx.fillText(ch, x*fontSize, yOffset*fontSize+(y+1)*fontSize);
  }
}

// Create a texture from the letter canvas.
var tex = new THREE.Texture(c);
// Tell Three.js not to flip the texture.
tex.flipY = false;
// And tell Three.js that it needs to update the texture.
tex.needsUpdate = true;

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

বইটির জন্য জ্যামিতি তৈরি করা:

var geo = new THREE.Geometry();

var i=0, x=0, line=0;
for (i=0; i<BOOK.length; i++) {
  var code = BOOK.charCodeAt(i); // This is the character code for the current letter.
  if (code > lettersPerSide * lettersPerSide) {
    code = 0; // Clamp character codes to letter map size.
  }
  var cx = code % lettersPerSide; // Cx is the x-index of the letter in the map.
  var cy = Math.floor(code / lettersPerSide); // Cy is the y-index of the letter in the map.

  // Add letter vertices to the geometry.
  var v,t;
  geo.vertices.push(
    new THREE.Vector3( x*1.1+0.05, line*1.1+0.05, 0 ),
    new THREE.Vector3( x*1.1+1.05, line*1.1+0.05, 0 ),
    new THREE.Vector3( x*1.1+1.05, line*1.1+1.05, 0 ),
    new THREE.Vector3( x*1.1+0.05, line*1.1+1.05, 0 )
  );
  // Create faces for the letter.
  var face = new THREE.Face3(i*4+0, i*4+1, i*4+2);
  geo.faces.push(face);
  face = new THREE.Face3(i*4+0, i*4+2, i*4+3);
  geo.faces.push(face);

  // Compute texture coordinates for the letters.
  var tx = cx/lettersPerSide, 
      ty = cy/lettersPerSide,
      off = 1/lettersPerSide;
  var sz = lettersPerSide*fontSize;
  geo.faceVertexUvs[0].push([
    new THREE.Vector2( tx, ty+off ),
    new THREE.Vector2( tx+off, ty+off ),
    new THREE.Vector2( tx+off, ty )
  ]);
  geo.faceVertexUvs[0].push([
    new THREE.Vector2( tx, ty+off ),
    new THREE.Vector2( tx+off, ty ),
    new THREE.Vector2( tx, ty )
  ]);

  // On newline, move to the line below and move the cursor to the start of the line.
  // Otherwise move the cursor to the right.
  if (code == 10) {
    line--;
    x=0;
  } else {
    x++;
  }
}

অক্ষর অ্যানিমেট করার জন্য ভার্টেক্স শেডার

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

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

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

একটি অক্ষরের উপরের-বাম কোণে খুঁজে পেতে শীর্ষবিন্দু স্থানাঙ্কগুলিকে বৃত্তাকার করা।
একটি অক্ষরের উপরের-বাম কোণে খুঁজে পেতে শীর্ষবিন্দু স্থানাঙ্কগুলিকে বৃত্তাকার করা।

অ্যানিমেটেড ভার্টেক্স শেডারটি আরও ভালভাবে বোঝার জন্য, আমি প্রথমে একটি সাধারণ রান-অফ-দ্য-মিল ভার্টেক্স শেডারের মধ্য দিয়ে যেতে যাচ্ছি। আপনি যখন পর্দায় একটি 3D মডেল আঁকেন তখন এটি সাধারণত ঘটে। প্রতিটি 3D শীর্ষবিন্দুকে 2D স্ক্রিনে প্রজেক্ট করার জন্য মডেলের শীর্ষবিন্দুগুলিকে কয়েকটি রূপান্তর ম্যাট্রিক্স দ্বারা রূপান্তরিত করা হয়। যখনই এই তিনটি শীর্ষবিন্দু দ্বারা সংজ্ঞায়িত একটি ত্রিভুজ ভিউপোর্টের অভ্যন্তরে অবতরণ করে, তখন এটি কভার করা পিক্সেলগুলিকে রঙ করার জন্য ফ্র্যাগমেন্ট শেডার দ্বারা প্রক্রিয়া করা হয়। যাইহোক, এখানে সহজ ভার্টেক্স শেডার আছে:

varying float vUv;

void main() {
  // modelViewMatrix, position and projectionMatrix are magical
  // attributes that Three.js defines for us.

  // Transform current vertex by the modelViewMatrix
  // (bundled model world position & camera world position matrix).
  vec4 mvPosition = modelViewMatrix * position;

  // Project camera-space vertex to screen coordinates
  // using the camera's projection matrix.
  vec4 p = projectionMatrix * mvPosition;

  // uv is another magical attribute from Three.js.
  // We're passing it to the fragment shader unchanged.
  vUv = uv;

  gl_Position = p;
}

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

uniform float uTime;
uniform float uEffectAmount;

varying float vZ;
varying vec2 vUv;

// rotateAngleAxisMatrix returns the mat3 rotation matrix
// for given angle and axis.
mat3 rotateAngleAxisMatrix(float angle, vec3 axis) {
  float c = cos(angle);
  float s = sin(angle);
  float t = 1.0 - c;
  axis = normalize(axis);
  float x = axis.x, y = axis.y, z = axis.z;
  return mat3(
    t*x*x + c,    t*x*y + s*z,  t*x*z - s*y,
    t*x*y - s*z,  t*y*y + c,    t*y*z + s*x,
    t*x*z + s*y,  t*y*z - s*x,  t*z*z + c
  );
}

// rotateAngleAxis rotates a vec3 over the given axis by the given angle and
// returns the rotated vector.
vec3 rotateAngleAxis(float angle, vec3 axis, vec3 v) {
  return rotateAngleAxisMatrix(angle, axis) * v;
}

void main() {
  // Compute the index of the letter (assuming 80-character max line length).
  float idx = floor(position.y/1.1)*80.0 + floor(position.x/1.1);

  // Round down the vertex coords to find the bottom-left corner point of the letter.
  vec3 corner = vec3(floor(position.x/1.1)*1.1, floor(position.y/1.1)*1.1, 0.0);

  // Find the midpoint of the letter.
  vec3 mid = corner + vec3(0.5, 0.5, 0.0);

  // Rotate the letter around its midpoint by an angle and axis dependent on
  // the letter's index and the current time.
  vec3 rpos = rotateAngleAxis(idx+uTime,
    vec3(mod(idx,16.0), -8.0+mod(idx,15.0), 1.0), position - mid) + mid;

  // uEffectAmount controls the amount of animation applied to the letter.
  // uEffectAmount ranges from 0.0 to 1.0.
  float effectAmount = uEffectAmount;

  vec4 fpos = vec4( mix(position,rpos,effectAmount), 1.0 );
  fpos.x += -35.0;

  // Apply spinning motion to individual letters.
  fpos.z += ((sin(idx+uTime*2.0)))*4.2*effectAmount;
  fpos.y += ((cos(idx+uTime*2.0)))*4.2*effectAmount;

  vec4 mvPosition = modelViewMatrix * fpos;

  // Apply wavy motion to the entire text.
  mvPosition.y += 10.0*sin(uTime*0.5+mvPosition.x/25.0)*effectAmount;
  mvPosition.x -= 10.0*cos(uTime*0.5+mvPosition.y/25.0)*effectAmount;

  vec4 p = projectionMatrix * mvPosition;

  // Pass texture coordinates and the vertex z-coordinate to the fragment shader.
  vUv = uv;
  vZ = p.z;

  // Send the final vertex position to WebGL.
  gl_Position = p;
}

ভার্টেক্স শেডার ব্যবহার করতে, আমি একটি THREE.ShaderMaterial ব্যবহার করি, একটি উপাদানের প্রকার যা আপনাকে কাস্টম শেডার ব্যবহার করতে এবং তাদের জন্য ইউনিফর্ম নির্দিষ্ট করতে দেয়। ডেমোতে আমি কীভাবে THREE.ShaderMaterial ব্যবহার করছি তা এখানে:

// First, set up uniforms for the shader.
var uniforms = {

  // map contains the letter map texture.
  map: { type: "t", value: 1, texture: tex },

  // uTime is the urrent time.
  uTime: { type: "f", value: 1.0 },

  // uEffectAmount controls the amount of animation applied to the letters.
  uEffectAmount: { type: "f", value: 0.0 }
};

// Next, set up the THREE.ShaderMaterial.
var shaderMaterial = new THREE.ShaderMaterial({
  uniforms: uniforms,

  // I have my shaders inside HTML elements like
  // <script id="vertex" type="text/x-glsl-vert">... shaderSource ... <script>

  // The below gets the contents of the vertex shader script element.
  vertexShader: document.querySelector('#vertex').textContent,

  // The fragment shader is a bit special as well, drawing a rotating
  // rainbow gradient.
  fragmentShader: document.querySelector('#fragment').textContent
});

// I set depthTest to false so that the letters don't occlude each other.
shaderMaterial.depthTest = false;

প্রতিটি অ্যানিমেশন ফ্রেমে, আমি শেডার ইউনিফর্ম আপডেট করি:

// I'm controlling the uniforms through a proxy control object.
// The reason I'm using a proxy control object is to
// have different value ranges for the controls and the uniforms.
var controller = {
  effectAmount: 0
};

// I'm using <a href="http://code.google.com/p/dat-gui/">DAT.GUI</a> to do a quick & easy GUI for the demo.
var gui = new dat.GUI();
gui.add(controller, 'effectAmount', 0, 100);

var animate = function(t) {
  uniforms.uTime.value += 0.05;
  uniforms.uEffectAmount.value = controller.effectAmount/100;
  bookModel.position.y += 0.03;

  renderer.render(scene, camera);
  requestAnimationFrame(animate, renderer.domElement);
};
animate(Date.now());

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

উদ্বেগ অবশিষ্ট

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

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

আরেকটি উদ্বেগ হল যে 256 অক্ষরগুলি অ-ASCII পাঠ্যগুলি করার জন্য খুব কম। যদি টেক্সচার ম্যাপের আকার 4096x4096 তে ঠেলে হরফের আকার 8px এ হ্রাস করে, আপনি টেক্সচার মানচিত্রে সম্পূর্ণ UCS-2 অক্ষর সেট করতে পারেন। যাইহোক, 8px ফন্ট সাইজ খুব পাঠযোগ্য নয়। বড় ফন্ট সাইজ করতে, আপনি আপনার ফন্টের জন্য একাধিক টেক্সচার ব্যবহার করতে পারেন। একটি উদাহরণের জন্য এই স্প্রাইট অ্যাটলাস ডেমো দেখুন। আরেকটি জিনিস যা সাহায্য করবে তা হল আপনার পাঠ্যে ব্যবহৃত অক্ষরগুলি তৈরি করা।

সারসংক্ষেপ

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

তথ্যসূত্র