WebGL बदलावों को बेहतर बनाता है

Gregg Tavares
Gregg Tavares

WebGL 2D अनुवाद

3D की पहचान करने से पहले, आइए 2D के बारे में थोड़ी देर और इंतज़ार करें. कृपया मेरे साथ बने रहें. यह लेख कुछ लोगों को साफ़ तौर पर लग सकता है, लेकिन कुछ लेखों में हम इसके बारे में विस्तार से बात करेंगे.

यह लेख, WebGL की बुनियादी बातों से शुरू होने वाली सीरीज़ का अगला हिस्सा है. अगर आपने इस चैप्टर को नहीं पढ़ा है, तो मेरा सुझाव है कि पहले चैप्टर को पढ़ें, फिर यहाँ वापस आएँ. अनुवाद, गणित का कोई खास नाम है जिसका मूल मतलब है "किसी चीज़ को दूसरी जगह ले जाना". मुझे लगता है कि किसी वाक्य को अंग्रेज़ी से जैपनीज़ में ले जाने की बात सही होगी. हालांकि, इस मामले में हम ज्यामिति के बारे में बात कर रहे हैं. पहली पोस्ट में हमने जिस सैंपल कोड का इस्तेमाल किया था उसका इस्तेमाल करके, हमारे रेक्टैंगल को आसानी से अनुवाद करने में मदद मिल सकती है. इसके लिए, रेक्टैंगल में पास की गई वैल्यू को सही सेट में बदलना होगा? यह रहा एक सैंपल, जो हमारे पिछले सैंपल पर आधारित है.

  // First lets make some variables 
  // to hold the translation of the rectangle
  var translation = [0, 0];
  // then let's make a function to
  // re-draw everything. We can call this
  // function after we update the translation.
  // Draw the scene.
  function drawScene() {
     // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);
    // Setup a rectangle
    setRectangle(gl, translation[0], translation[1], width, height);

    // Draw the rectangle.
    gl.drawArrays(gl.TRIANGLES, 0, 6);
  }

अब तक तो बहुत अच्छा है. लेकिन अब कल्पना करें कि हम भी यही काम ज़्यादा जटिल आकार के साथ करना चाहते थे. मान लें कि हमें एक 'F' बनाना था, जिसमें इस तरह के छह त्रिभुज हों.

एफ़ अक्षर

खैर, ये मौजूदा कोड हैं. हमें सेटरेक्टैंगल को इस तरह के कुछ और कोड में बदलना होगा.

// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl, x, y) {
  var width = 100;
  var height = 150;
  var thickness = 30;
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
          // left column
          x, y,
          x + thickness, y,
          x, y + height,
          x, y + height,
          x + thickness, y,
          x + thickness, y + height,

          // top rung
          x + thickness, y,
          x + width, y,
          x + thickness, y + thickness,
          x + thickness, y + thickness,
          x + width, y,
          x + width, y + thickness,

          // middle rung
          x + thickness, y + thickness * 2,
          x + width * 2 / 3, y + thickness * 2,
          x + thickness, y + thickness * 3,
          x + thickness, y + thickness * 3,
          x + width * 2 / 3, y + thickness * 2,
          x + width * 2 / 3, y + thickness * 3]),
      gl.STATIC_DRAW);
}

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

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform vec2 u_translation;

void main() {
   // Add in the translation.
   vec2 position = a_position + u_translation;

   // convert the rectangle from pixels to 0.0 to 1.0
   vec2 zeroToOne = position / u_resolution;
   ...

और हम कोड में थोड़ा-बहुत बदलाव करेंगे. एक के लिए हमें ज्यामिति को सिर्फ़ एक बार सेट करना होगा.

// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl) {
  gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
          // left column
          0, 0,
          30, 0,
          0, 150,
          0, 150,
          30, 0,
          30, 150,

          // top rung
          30, 0,
          100, 0,
          30, 30,
          30, 30,
          100, 0,
          100, 30,

          // middle rung
          30, 60,
          67, 60,
          30, 90,
          30, 90,
          67, 60,
          67, 90]),
      gl.STATIC_DRAW);
}

इसके बाद, हमें अपने हिसाब से अनुवाद करने से पहले, u_translation को अपडेट करना होगा.

  ...
  var translationLocation = gl.getUniformLocation(
             program, "u_translation");
  ...
  // Set Geometry.
  setGeometry(gl);
  ..
  // Draw scene.
  function drawScene() {
    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Set the translation.
    gl.uniform2fv(translationLocation, translation);

    // Draw the rectangle.
    gl.drawArrays(gl.TRIANGLES, 0, 18);
  }

सूचना setGeometry को सिर्फ़ एक बार कॉल किया गया है. यह अबDrawScene में नहीं है.

अब जब हम WebGL का ड्रॉ करते हैं, तो वह व्यावहारिक रूप से सब कुछ कर रहा है. हम बस अनुवाद सेट कर रहे हैं और उसे कुछ बनाने के लिए कह रहे हैं. भले ही हमारी ज्यामिति में हज़ारों बिंदु हों, मुख्य कोड वही रहेगा.

WebGL 2D रोटेशन

मैं सीधे शुरुआत में हूं

सबसे पहले मैं आपका परिचय कराना चाहता हूं, जिसे "यूनिट सर्कल" कहते हैं. अगर आपको याद है कि आपने जूनियर हाई स्कूल का गणित (मेरे ऊपर स्लीप मोड में मत जाओ!) तो सर्कल का एक दायरा हो जाता है. वृत्त की त्रिज्या, वृत्त के केंद्र से किनारे तक की दूरी होती है. यूनिट सर्कल, 1.0 रेडियस वाला वृत्त होता है.

अगर आपको तीसरी क्लास के गणित के सवाल याद हैं, तो किसी चीज़ को 1 से गुणा करने पर वह पहले जैसा ही रहता है. इसलिए, 123 * 1 = 123. काफ़ी बुनियादी है न? खैर, इकाई वृत्त, 1.0 की त्रिज्या वाला वृत्त भी 1 का एक रूप होता है. यह 1 घूम रहा है. इस तरह, किसी चीज़ को इस यूनिट सर्कल से गुणा किया जा सकता है और इस तरह से उसे 1 से गुणा किया जा सकता है. हालांकि, इसमें जादू होता है और चीज़ें घूमती रहती हैं. हम यूनिट सर्कल के किसी भी पॉइंट से X और Y वैल्यू लेने जा रहे हैं और हम अपने पिछले सैंपल की ज्यामिति को उनसे गुणा कर देंगे. हमारे शेडर से जुड़े अपडेट यहां दिए गए हैं.

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform vec2 u_translation;
uniform vec2 u_rotation;

void main() {
  // Rotate the position
  vec2 rotatedPosition = vec2(
     a_position.x * u_rotation.y + a_position.y * u_rotation.x,
     a_position.y * u_rotation.y - a_position.x * u_rotation.x);

  // Add in the translation.
  vec2 position = rotatedPosition + u_translation;

साथ ही, हम JavaScript को अपडेट करते हैं, ताकि हम उन दोनों वैल्यू को पास कर सकें.

  ...
  var rotationLocation = gl.getUniformLocation(program, "u_rotation");
  ...
  var rotation = [0, 1];
  ..
  // Draw the scene.
  function drawScene() {
    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Set the translation.
    gl.uniform2fv(translationLocation, translation);

    // Set the rotation.
    gl.uniform2fv(rotationLocation, rotation);

    // Draw the rectangle.
    gl.drawArrays(gl.TRIANGLES, 0, 18);
  }

यह काम क्यों करता है? अच्छा, गणित पर ध्यान दो.

rotatedX = a_position.x * u_rotation.y + a_position.y * u_rotation.x;
rotatedY = a_position.y * u_rotation.y - a_position.x * u_rotation.x;

अपने पास रेक्टैंगल बना रहने दें, लेकिन आपको उसे घुमाना है. इसे घुमाने से पहले, सबसे ऊपर वाला दायां कोने 3.0, 9.0 पर होगा. चलिए, 12 बजे से घड़ी की दिशा में 30 डिग्री वाले यूनिट सर्कल पर कोई पॉइंट चुनते हैं.

30 डिग्री तक घुमाना

सर्कल की स्थिति 0.50 और 0.87 है

3.0 * 0.87 + 9.0 * 0.50 = 7.1
9.0 * 0.87 - 3.0 * 0.50 = 6.3

हमें इसकी ज़रूरत है

रोटेशन ड्रॉइंग

घड़ी की सुई की दिशा में 60 डिग्री के लिए भी ऐसा ही होता है

60 डिग्री तक घुमाना

सर्कल की स्थिति 0.87 और 0.50 है

3.0 * 0.50 + 9.0 * 0.87 = 9.3
9.0 * 0.50 - 3.0 * 0.87 = 1.9

आपको दिख सकता है कि जैसे-जैसे हम उस पॉइंट को घड़ी की सुई की दिशा में घुमाते हैं, X वैल्यू बढ़ती जाती है और Y भी छोटा होता जाता है. अगर 90 डिग्री X के आगे बढ़ते रहे, तो यह फिर से छोटा होने लगेगा और Y भी बड़ा होने लगेगा. यह पैटर्न हमें रोटेशन देता है. यूनिट सर्कल पर पॉइंट के लिए दूसरा नाम होता है. इन्हें साइन और कोसाइन कहते हैं. इसलिए, किसी भी दिए गए कोण के लिए हम सिर्फ़ साइन (sine) और कोसाइन (cosine) को इस तरह देख सकते हैं.

function printSineAndCosineForAnAngle(angleInDegrees) {
  var angleInRadians = angleInDegrees * Math.PI / 180;
  var s = Math.sin(angleInRadians);
  var c = Math.cos(angleInRadians);
  console.log("s = " + s + " c = " + c);
}

अगर कोड को कॉपी करके अपने JavaScript कंसोल में चिपकाया जाता है, तो printSineAndCosignForAngle(30) टाइप किया जाता है. यह आपको s = 0.49 c= 0.87 प्रिंट करता है (ध्यान दें: मैंने संख्याओं को राउंड ऑफ़ कर दिया है.) अगर आप इन्हें एक साथ रख दें, तो आप अपनी ज्यामिति को अपने मुताबिक किसी भी कोण में घुमा सकते हैं. बस उस कोण के साइन और कोसाइन पर रोटेशन सेट करें जिसे आप घुमाना चाहते हैं.

  ...
  var angleInRadians = angleInDegrees * Math.PI / 180;
  rotation[0] = Math.sin(angleInRadians);
  rotation[1] = Math.cos(angleInRadians);

उम्मीद है कि आपको अपनी बात समझ आई होगी. आगे एक आसान तरीका है. स्केल.

रेडियन क्या होते हैं?

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

आपको शायद पता होगा कि इंपीरियल मेज़रमेंट की तुलना में, मेट्रिक मेज़रमेंट वाला गणित ज़्यादा आसान होता है. इंच से फ़ीट तक जाने के लिए, इसे 12 से भाग दिया जाता है. इंच से गज़ तक जाने के लिए, इसे 36 से भाग दिया जाता है. मुझे आपके बारे में नहीं पता, लेकिन मैं इसे 36 के हिसाब से तय नहीं कर सकता. मेट्रिक की मदद से यह काफ़ी आसान है. मिलीमीटर से सेंटीमीटर में जाने के लिए, इसे 10 से भाग दिया जाता है. मिलीमीटर से मीटर तक हम 1,000 से भाग देते हैं. मैं अपने दिमाग में 1,000 का फ़र्क़ कर सकता/सकती हूं.

रेडियन बनाम डिग्री समान हैं. डिग्री गणित को मुश्किल बना देती है. रैडियन गणित को आसान बना देते हैं. एक सर्कल में 360 डिग्री होती है, लेकिन सिर्फ़ 2 गड़बड़ी रेडियन होते हैं. इसलिए, पूरा बारी 2x रेडियन है. आधा मोड़ एक रेडियन है. एक 1/4 मोड़, यानी 90 डिग्री 1/2 रेडियन है. इसलिए अगर आपको किसी चीज़ को 90 डिग्री घुमाना है, तो बस Math.PI * 0.5 का उपयोग करें. अगर आपको इसे 45 डिग्री घुमाना है, तो Math.PI * 0.25 वगैरह का इस्तेमाल करें.

कोणों, वृत्त या घुमाव वाले करीब सभी गणितीय तरीके बहुत आसानी से तब काम करते हैं जब आप रेडियन में सोचना शुरू करते हैं. इसलिए, इसे आज़माएँ. यूज़र इंटरफ़ेस (यूआई) डिसप्ले के अलावा, डिग्री के बजाय रेडियन का इस्तेमाल करें.

WebGL 2D स्केल

स्केल करना, अनुवाद करने जितना ही आसान है.

हम पोज़िशन को अपनी पसंद के स्केल से गुणा करते हैं. हमारे पिछले सैंपल में हुए बदलावों की जानकारी यहां दी गई है.

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform vec2 u_translation;
uniform vec2 u_rotation;
uniform vec2 u_scale;

void main() {
  // Scale the positon
  vec2 scaledPosition = a_position * u_scale;

  // Rotate the position
  vec2 rotatedPosition = vec2(
     scaledPosition.x * u_rotation.y +
        scaledPosition.y * u_rotation.x,
     scaledPosition.y * u_rotation.y -
        scaledPosition.x * u_rotation.x);

  // Add in the translation.
  vec2 position = rotatedPosition + u_translation;

और हम ड्रॉइंग बनाते समय स्केल सेट करने के लिए ज़रूरी JavaScript जोड़ते हैं.

  ...
  var scaleLocation = gl.getUniformLocation(program, "u_scale");
  ...
  var scale = [1, 1];
  ...
  // Draw the scene.
  function drawScene() {
    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Set the translation.
    gl.uniform2fv(translationLocation, translation);

    // Set the rotation.
    gl.uniform2fv(rotationLocation, rotation);

    // Set the scale.
    gl.uniform2fv(scaleLocation, scale);

    // Draw the rectangle.
    gl.drawArrays(gl.TRIANGLES, 0, 18);
  }

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

'F' क्यों?

मैंने जब पहली बार किसी को टेक्स्चर पर 'F' का इस्तेमाल करते देखा था. 'F' खुद ज़रूरी नहीं है. खास बात यह है कि इसका ओरिएंटेशन किसी भी दिशा से पता किया जा सकता है. उदाहरण के लिए, अगर हमने दिल ♥ या त्रिकोण △ का इस्तेमाल किया, तो हम यह नहीं बता सकते थे कि यह हॉरिज़ॉन्टल तौर पर फ़्लिप किया गया था या नहीं. सर्कल ○ और भी खराब होगा. आम तौर पर, एक रंगीन आयत हर कोने पर अलग-अलग रंगों के साथ काम करता है, लेकिन फिर आपको याद रखना होगा कि कौनसा कोना कौनसा था. F का ओरिएंटेशन तुरंत पहचानने लायक हो जाता है.

F ओरिएंटेशन

कोई भी आकार जिसके बारे में आप यह बता सकते हैं कि वह किस तरह का है, मैंने अभी-अभी 'F' का इस्तेमाल तब से किया है, जब से मैंने शुरुआत की थी.

WebGL 2D मैट्रिक

पिछले तीन चैप्टर में, हमने ज्यामिति का अनुवाद करने, ज्यामिति को घुमाने, और ज्यामिति को स्केल करने का तरीका बताया है. अनुवाद, रोटेशन, और स्केल को 'बदलाव' का एक टाइप माना जाता है. इन बदलावों में से हर एक के लिए शेडर में बदलाव करना ज़रूरी था और तीनों ट्रांसफ़ॉर्मेशन में से हर एक ऑर्डर पर निर्भर था.

उदाहरण के लिए यहां 2, 1 का स्केल, 30% का रोटेशन, और 100, 0 का अनुवाद है.

F रोटेशन और अनुवाद

और यह रहा 1,00,0 का अनुवाद, 30% का रोटेशन और 2, 1 का स्केल

F रोटेशन और स्केल

इसके नतीजे बिलकुल अलग होते हैं. इससे भी बुरा यह होता है कि अगर हमें दूसरे उदाहरण की ज़रूरत पड़ती, तो हमें एक अलग शेडर लिखना पड़ता, जिसमें अनुवाद, रोटेशन, और स्केल को अपने नए मनचाहे क्रम में लागू किया जा सके. कुछ लोग मुझसे ज़्यादा स्मार्ट होते हैं, लेकिन उन्हें पता चला कि मैट्रिक्स मैथ से सभी सारे काम किए जा सकते हैं. 2d के लिए, हम 3x3 मैट्रिक्स का इस्तेमाल करते हैं. 3x3 मैट्रिक्स एक ग्रिड की तरह होता है, जिसमें नौ बॉक्स होते हैं.

1.0 2.0 3.0
4.0 5.0 6.0
7.0 8.0 9.0

गणित निकालने के लिए, हम मैट्रिक्स के कॉलम में मौजूद संख्याओं को नीचे की ओर गुणा करते हैं और फिर नतीजों को जोड़ते हैं. हमारी स्थितियों में सिर्फ़ 2 मान, x और y हैं, लेकिन इस गणित के लिए हमें 3 मानों की ज़रूरत है, इसलिए हम तीसरे मान के लिए 1 का इस्तेमाल करेंगे. इस मामले में हमारा नतीजा होगा

newX = x * 1.0 + y * 4.0 + 1 * 7.0

newY = x * 2.0 + y * 5.0 + 1 * 8.0

extra = x * 3.0 + y * 6.0 + 1 * 9.0

शायद आप उसे देखकर सोच रहे हों कि "विषय क्या है". ठीक है, मान लेते हैं कि हमारे पास एक अनुवाद है. हम tx और ty का अनुवाद करने के लिए, तय रकम को कॉल करेंगे. इस तरह का मैट्रिक्स बनाएँ

1.00.00.0
0.01.00.0
txty1.0

अब इसे देखें

newX = x * 1.0 + y * 0.0 + 1 * tx

newY = x * 0.0 + y * 1.0 + 1 * ty

extra = x * 0.0 + y * 0.0 + 1 * 1

अगर आपको अपना बीजगणित याद है, तो हम शून्य से गुणा करने पर किसी भी जगह को मिटा सकते हैं. किसी नतीजे को एक से गुणा करने पर कुछ नहीं होता, इसलिए आइए, आसान तरीके से यह देखते हैं कि क्या हो रहा है

newX = x + tx;
newY = y + ty;

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

s = Math.sin(angleToRotateInRadians);
c = Math.cos(angleToRotateInRadians);

और हमने इस तरह का एक मैट्रिक्स बनाया

c-से॰0.0
sc0.0
0.00.01.0

हमने यह आव्यूह लागू किया

newX = x * c + y * s + 1 * 0

newY = x * -s + y * c + 1 * 0

extra = x * 0.0 + y * 0.0 + 1 * 1

सभी को 0 और 1 से गुणा करने पर मिलने वाले नतीजे ब्लैक आउट कर देते हैं

newX = x *  c + y * s;
newY = x * -s + y * c;

यह वही है जो हमारे रोटेशन सैंपल में था. आखिर में, स्केल करें. हम अपने 2 स्केल फ़ैक्टर को sx और sy कहते हैं और हम इस तरह का एक मैट्रिक्स बनाते हैं

एसएक्स0.00.0
0.0sy0.0
0.00.01.0

हमने यह आव्यूह लागू किया

newX = x * sx + y * 0 + 1 * 0

newY = x * 0 + y * sy + 1 * 0

extra = x * 0.0 + y * 0.0 + 1 * 1

जो वाकई में

newX = x * sx;
newY = y * sy;

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

function makeTranslation(tx, ty) {
  return [
    1, 0, 0,
    0, 1, 0,
    tx, ty, 1
  ];
}

function makeRotation(angleInRadians) {
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  return [
    c,-s, 0,
    s, c, 0,
    0, 0, 1
  ];
}

function makeScale(sx, sy) {
  return [
    sx, 0, 0,
    0, sy, 0,
    0, 0, 1
  ];
}

चलिए, अब अपना शेडर बदलते हैं. पुराना शेडर ऐसा दिखता था

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform vec2 u_translation;
uniform vec2 u_rotation;
uniform vec2 u_scale;

void main() {
  // Scale the positon
  vec2 scaledPosition = a_position * u_scale;

  // Rotate the position
  vec2 rotatedPosition = vec2(
     scaledPosition.x * u_rotation.y + scaledPosition.y * u_rotation.x,
     scaledPosition.y * u_rotation.y - scaledPosition.x * u_rotation.x);

  // Add in the translation.
  vec2 position = rotatedPosition + u_translation;
  ...

हमारा नया शेडर बहुत आसान हो जाएगा.

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform vec2 u_resolution;
uniform mat3 u_matrix;

void main() {
  // Multiply the position by the matrix.
  vec2 position = (u_matrix * vec3(a_position, 1)).xy;
  ...

यहां बताया गया है कि हम

  // Draw the scene.
  function drawScene() {
    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Compute the matrices
    var translationMatrix =
       makeTranslation(translation[0], translation[1]);
    var rotationMatrix = makeRotation(angleInRadians);
    var scaleMatrix = makeScale(scale[0], scale[1]);

    // Multiply the matrices.
    var matrix = matrixMultiply(scaleMatrix, rotationMatrix);
    matrix = matrixMultiply(matrix, translationMatrix);

    // Set the matrix.
    gl.uniformMatrix3fv(matrixLocation, false, matrix);

    // Draw the rectangle.
    gl.drawArrays(gl.TRIANGLES, 0, 18);
  }

अब भी, आपको पूछना पड़ सकता है, तो क्या? यह कोई फ़ायदा नहीं है . हालांकि, अब अगर हमें क्रम बदलना हो, तो हमें नया शेडर लिखने की ज़रूरत नहीं है. हम सिर्फ़ हिसाब बदल सकते हैं.

    ...
    // Multiply the matrices.
    var matrix = matrixMultiply(translationMatrix, rotationMatrix);
    matrix = matrixMultiply(matrix, scaleMatrix);
    ...

इस तरह के आव्यूहों को लागू करना खास तौर पर, हैरारकी के हिसाब से बनाए जाने वाले ऐनिमेशन के लिए ज़रूरी है. जैसे, शरीर पर हाथ, सूरज के चारों ओर ग्रह पर चंद्रमा या पेड़ की शाखाएं. हैरारकी वाले ऐनिमेशन के आसान उदाहरण के ज़रिए, हम 'F' को पांच बार बना सकते हैं, लेकिन हर बार हम पहले 'F' के मैट्रिक्स से शुरू करते हैं.

  // Draw the scene.
  function drawScene() {
    // Clear the canvas.
    gl.clear(gl.COLOR_BUFFER_BIT);

    // Compute the matrices
    var translationMatrix = makeTranslation(translation[0], translation[1]);
    var rotationMatrix = makeRotation(angleInRadians);
    var scaleMatrix = makeScale(scale[0], scale[1]);

    // Starting Matrix.
    var matrix = makeIdentity();

    for (var i = 0; i < 5; ++i) {
      // Multiply the matrices.
      matrix = matrixMultiply(matrix, scaleMatrix);
      matrix = matrixMultiply(matrix, rotationMatrix);
      matrix = matrixMultiply(matrix, translationMatrix);

      // Set the matrix.
      gl.uniformMatrix3fv(matrixLocation, false, matrix);

      // Draw the geometry.
      gl.drawArrays(gl.TRIANGLES, 0, 18);
    }
  }

ऐसा करने के लिए, हमने makeIdentity फ़ंक्शन लॉन्च किया था, जो आइडेंटिटी मैट्रिक्स बनाता है. आइडेंटिटी मैट्रिक्स एक ऐसा मैट्रिक्स है जो असरदार तरीके से 1.0 को दिखाता है. इसलिए, अगर आइडेंटिटी से गुणा करें, तो कुछ नहीं होता. ठीक वैसे ही

X * 1 = X

भी ऐसे ही

matrixX * identity = matrixX

आइडेंटिटी मैट्रिक्स बनाने के लिए कोड यह है.

function makeIdentity() {
  return [
    1, 0, 0,
    0, 1, 0,
    0, 0, 1
  ];
}

एक और उदाहरण, अब तक के हर सैंपल में 'F' सबसे ऊपर बाएं कोने में घूमता है. इसकी वजह यह है कि जिस गणित का हम इस्तेमाल कर रहे हैं वह हमेशा मूल की दिशा में घूमता है और हमारी 'F' का ऊपर वाला बायां कोना, मूल बिंदु पर होता है, (0, 0) लेकिन अब, हम मैट्रिक्स गणित कर सकते हैं और हम जिस क्रम में बदलाव लागू करते हैं उसे चुन सकते हैं, बाकी ट्रांसफ़ॉर्म को लागू करने से पहले हम मूल को आगे बढ़ा सकते हैं.

    // make a matrix that will move the origin of the 'F' to
    // its center.
    var moveOriginMatrix = makeTranslation(-50, -75);
    ...

    // Multiply the matrices.
    var matrix = matrixMultiply(moveOriginMatrix, scaleMatrix);
    matrix = matrixMultiply(matrix, rotationMatrix);
    matrix = matrixMultiply(matrix, translationMatrix);

इस तकनीक का इस्तेमाल करके, किसी भी पॉइंट से घुमाया जा सकता है या स्केल किया जा सकता है. अब आपको पता है कि Photoshop या Flash की मदद से, रोटेशन पॉइंट को कैसे बदला जा सकता है. चलो, और ज़्यादा पागल हो जाते हैं. अगर आप WebGL की बुनियादी बातों के बारे में पहले लेख पर वापस जाते हैं, तो आपको याद होगा कि हमारे पास शेडर में मौजूद कोड है, जो इस तरह दिखने वाले पिक्सल से क्लिपस्पेस में बदलता है.

  ...
  // convert the rectangle from pixels to 0.0 to 1.0
  vec2 zeroToOne = position / u_resolution;

  // convert from 0->1 to 0->2
  vec2 zeroToTwo = zeroToOne * 2.0;

  // convert from 0->2 to -1->+1 (clipspace)
  vec2 clipSpace = zeroToTwo - 1.0;

  gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);

अगर आप उनमें से हर एक चरण को बारी-बारी से देखें, तो पहला चरण, "पिक्सल से 0.0 से 1.0 में बदलना", असल में एक स्केल ऑपरेशन है. दूसरा, बड़े पैमाने पर किया जाने वाला ऑपरेशन है. अगला है अनुवाद और सबसे आखिरी में Y x -1 का स्केल. हम असल में यह सब उस मैट्रिक्स में कर सकते हैं जिसे हम शेडर में पास करते हैं. हम 2 स्केल मैट्रिक्स बना सकते हैं, एक को 1.0/रिज़ॉल्यूशन से स्केल करने के लिए, दूसरा 2.0 से स्केल करने के लिए, तीसरा को -1.0,-1.0 से अनुवाद करने के लिए, और Y को -1 से स्केल करने के लिए 4था, फिर उन सभी को आपस में गुणा कर सकते हैं, लेकिन इसके बजाय, गणित आसान है, इसलिए हम ऐसा फ़ंक्शन बनाएंगे जो दिए गए रिज़ॉल्यूशन के लिए सीधे 'प्रोजेक्शन' मैट्रिक्स बनाता है.

function make2DProjection(width, height) {
  // Note: This matrix flips the Y axis so that 0 is at the top.
  return [
    2 / width, 0, 0,
    0, -2 / height, 0,
    -1, 1, 1
  ];
}

अब हम शेडर को और भी आसान बना सकते हैं. यहां पूरा नया वर्टेक्स शेडर दिया गया है.

<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;

uniform mat3 u_matrix;

void main() {
  // Multiply the position by the matrix.
  gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
}
</script>

JavaScript में हमें प्रोजेक्शन मैट्रिक्स से गुणा करना होगा

  // Draw the scene.
  function drawScene() {
    ...
    // Compute the matrices
    var projectionMatrix =
       make2DProjection(canvas.width, canvas.height);
    ...

    // Multiply the matrices.
    var matrix = matrixMultiply(scaleMatrix, rotationMatrix);
    matrix = matrixMultiply(matrix, translationMatrix);
    matrix = matrixMultiply(matrix, projectionMatrix);
    ...
  }

हमने रिज़ॉल्यूशन सेट करने वाला कोड भी हटा दिया है. इस आखिरी चरण के साथ, अब हम छह से सात कदमों वाले एक काफ़ी मुश्किल शेडर से बढ़कर एक आसान शेडर बन गए हैं. इसमें सिर्फ़ एक चरण है, जिसमें मैट्रिक्स गणित का जादू है.

मुझे उम्मीद है कि इस लेख से मैट्रिक्स गणित को समझने में मदद मिली होगी. इसके बाद, मैं 3D पर चलते हूँ. 3D मैट्रिक्स में गणित में एक जैसे सिद्धांतों का पालन होता है. साथ ही, उनके इस्तेमाल के बारे में भी बताया जाता है. मैंने 2D के साथ शुरुआत की, ताकि उम्मीद है कि इससे लोगों को समझने में आसानी हो.