WebGPU / TSL

Starter pack

WebGPU एक नया वेब मानक है जो GPU पर ग्राफिक्स को रेंडर करने और संगणनाएं करने के लिए एक निम्न-स्तरीय API प्रदान करता है। इसे WebGL का उत्तराधिकारी बनने के लिए डिज़ाइन किया गया है, जो बेहतर प्रदर्शन और अधिक उन्नत सुविधाएँ प्रदान करता है।

शानदार खबर यह है कि इसे अब Three.js के साथ कोडबेस में न्यूनतम परिवर्तन के साथ उपयोग करना संभव है।

इस पाठ में, हम WebGPU को Three.js और React Three Fiber के साथ उपयोग करने और नए Three Shading Language (TSL) का उपयोग करके शेडर्स लिखने का अन्वेषण करेंगे।

यदि आप शेडर्स के नए हैं, तो मैं आपको इस अध्याय के साथ जारी रखने से पहले Shaders अध्याय को पूरा करने की सलाह देता हूँ।

WebGPU Renderer

WebGL के बजाय WebGPU API का उपयोग करने के लिए, हमें WebGLRenderer के बजाय WebGPURenderer का उपयोग करना होगा (Three.js दस्तावेज़ीकरण पर अभी तक कोई समर्पित अनुभाग नहीं)

React Three Fiber के साथ, <Canvas> कंपोनेंट बनाते समय, renderer की स्थापना स्वचालित रूप से की जाती है। हालांकि, हम <Canvas> कंपोनेंट के gl prop को एक फ़ंक्शन पास करके डिफ़ॉल्ट renderer को ओवरराइड कर सकते हैं।

App.jsx में, हमारे पास एक <Canvas> कंपोनेंट है जो डिफ़ॉल्ट WebGLRenderer का उपयोग करता है। आइए इसे WebGPURenderer का उपयोग करने के लिए संशोधित करें।

पहले, हमें WebGPURenderer के तैयार होने तक frameloop को रोकने की आवश्यकता है। हम इसे frameloop prop को never पर सेट करके कर सकते हैं।

// ...
import { useState } from "react";

function App() {
  const [frameloop, setFrameloop] = useState("never");

  return (
    <>
      {/* ... */}
      <Canvas
        // ...
        frameloop={frameloop}
      >
        {/* ... */}
      </Canvas>
    </>
  );
}

export default App;

अब, हमें Three.js के WebGPU संस्करण को इम्पोर्ट करने की आवश्यकता है:

import * as THREE from "three/webgpu";

WebGPU का उपयोग करते समय, हमें डिफ़ॉल्ट three मॉड्यूल के बजाय three/webgpu मॉड्यूल का उपयोग करने की आवश्यकता है। यह इसलिए है क्योंकि WebGPURenderer Three.js की डिफ़ॉल्ट बिल्ड में शामिल नहीं है।

इसके बाद, हम gl prop का उपयोग करके एक नया WebGPURenderer instance बना सकते हैं:

// ...

function App() {
  const [frameloop, setFrameloop] = useState("never");

  return (
    <>
      {/* ... */}
      <Canvas
        // ...
        gl={(canvas) => {
          const renderer = new THREE.WebGPURenderer({
            canvas,
            powerPreference: "high-performance",
            antialias: true,
            alpha: false,
            stencil: false,
            shadowMap: true,
          });
          renderer.init().then(() => {
            setFrameloop("always");
          });
          return renderer;
        }}
      >
        {/* ... */}
      </Canvas>
    </>
  );
}
// ...

हम एक नया WebGPURenderer instance बनाते हैं और इसे canvas तत्व पास करते हैं। हम renderer के लिए कुछ विकल्प भी सेट करते हैं, जैसे powerPreference, antialias, alpha, stencil, और shadowMapये विकल्प WebGLRenderer में उपयोग किए जाने वाले विकल्पों के समान होते हैं।

अंत में, renderer को इनिशियलाइज करने के लिए हम उसके init() मेथड को कॉल करते हैं। एक बार इनिशियलाइजेशन पूर्ण हो जाने के बाद, हम frameloop स्थिति को "always" पर सेट कर देते हैं ताकि रेंडरिंग शुरू हो सके।

चलो देखते हैं ब्राउजर में परिणाम:

हमारा क्यूब अब WebGLRenderer के बजाय WebGPURenderer का उपयोग करके रेंडर किया गया है।

यह कितना आसान है! हमने सफलतापूर्वक अपने React Three Fiber एप्लिकेशन में एक WebGPURenderer सेट किया है। आप अब उसी Three.js API का उपयोग करके 3D ऑब्जेक्ट्स को बना और हेरफेर कर सकते हैं, जैसे कि आप WebGLRenderer के साथ करेंगे।

सबसे बड़े बदलाव शेडर्स लिखने के समय आते हैं। WebGPU API WebGL से एक अलग shading भाषा का उपयोग करता है, जिसका अर्थ है कि हमें शेडर्स को एक अलग तरीके से लिखना होगा। WGSL में GLSL के बजाय।

यहां Three Shading Language (TSL) काम आती है।

Three Shading Language

TSL एक नई शेडिंग भाषा है जिसे Three.js के साथ उपयोग करने के लिए डिज़ाइन किया गया है ताकि शेडर्स को एक अधिक उपयोगकर्ता-मैत्रीपूर्ण तरीके से एक नोड-आधारित दृष्टिकोण का उपयोग करके लिखा जा सके।

TSL का एक बड़ा लाभ यह है कि यह रेंडरर-अज्ञेयवादी है, जिसका अर्थ है कि आप विभिन्न रेंडरर्स के साथ समान शेडर्स का उपयोग कर सकते हैं, जैसे कि WebGL और WebGPU

इससे शेडर्स को लिखना और बनाए रखना आसान हो जाता है, क्योंकि आपको दोनों शेडिंग भाषाओं के बीच के अंतरों के बारे में चिंता करने की आवश्यकता नहीं होती है।

यह भविष्य-प्रूफ भी है, क्योंकि यदि कोई नया रेंडरर जारी किया जाता है, तो हम बिना किसी परिवर्तन के समान शेडर्स का उपयोग कर सकते हैं, जब तक कि TSL इसका समर्थन करता है।

Three Shading Language अभी भी विकास में है, लेकिन यह पहले से ही Three.js के नवीनतम संस्करणों में उपलब्ध है। इसे सीखने का सबसे अच्छा तरीका और परिवर्तनों पर नज़र रखने का तरीका यह है कि आप Three Shading Language विकी पेज देखें। मैंने इसे व्यापक रूप से इसका उपयोग करना सीखा है।

नोड आधारित मटेरियल

यह समझने के लिए कि TSL के साथ शेडर्स कैसे बनाए जाते हैं, हमें समझना होगा कि नोड-आधारित दृष्टिकोण क्या होता है।

एक नोड-आधारित दृष्टिकोण में, हम ग्राफ बनाने के लिए विभिन्न नोड्स को जोड़कर शेडर्स बनाते हैं। प्रत्येक नोड एक विशिष्ट ऑपरेशन या फ़ंक्शन का प्रतिनिधित्व करता है, और नोड्स के बीच के कनेक्शन डेटा के प्रवाह का प्रतिनिधित्व करते हैं।

इस दृष्टिकोण के कई फायदे हैं, जैसे कि:

  • दृश्य प्रतिनिधित्व: एक शेडर में डेटा और ऑपरेशन्स के प्रवाह को समझना और दृश्य बनाना आसान होता है।
  • पुन: प्रयोज्यता: हम पुन: प्रयोज्य नोड्स बना सकते हैं जो विभिन्न शेडर्स में उपयोग किए जा सकते हैं।
  • लचीलापन: हम आसानी से एक शेडर के व्यवहार को बदल सकते हैं और उसमें जोड़ सकते हैं या नोड्स को हटा सकते हैं।
  • विस्तारशीलता: मौजूदा मटेरियल से फीचर्स को जोड़ना/अनुकूलित करना अब आसान है।
  • अज्ञेय: TSL लक्ष्य रेंडरर के लिए उपयुक्त कोड उत्पन्न करेगा, चाहे वह WebGL (GLSL) हो या WebGPU (WGSL)।

हमारे पहले नोड-आधारित मटेरियल का कोडिंग शुरू करने से पहले, हम ऑनलाइन Three.js खेल का मैदान का उपयोग करके नोड-सिस्टम के साथ विज़ुअल रूप से प्रयोग कर सकते हैं।

Three.js खेल का मैदान खोलें और ऊपर Examples बटन पर क्लिक करें, और basic > fresnel चुनें।

Three.js playground

आपको दो color नोड्स और एक float नोड के साथ एक नोड-आधारित मटेरियल संपादक देखना चाहिए, जो कि fresnel नोड से जुड़ा हुआ है। (Color A, Color B, और Fresnel Factor)

fresnel नोड Basic Material के रंग से जुड़ा हुआ है जिसके परिणामस्वरूप चायदानी को एक फ्रेनल प्रभाव के साथ रंगना होता है।

Three.js playground

परिणाम का पूर्वावलोकन करने के लिए Splitscreen बटन का उपयोग करें।

मान लें कि हम Basic Material की अपारदर्शिता को समय के आधार पर प्रभावित करना चाहते हैं। हम एक Timer नोड जोड़ सकते हैं और इसे Fract नोड से कनेक्ट कर सकते हैं ताकि यह समय को 0 पर रीसेट कर दे जब यह 1 तक पहुंच जाए। फिर हम इसे Basic Material के opacity इनपुट से कनेक्ट करते हैं।

हमारी चायदानी अब चुंधाने लगती है फिर गायब हो जाती है और फिर से चुंधाने लगती है।

विभिन्न नोड्स के साथ खेलने के लिए समय निकालें और देखें कि वे मटेरियल को कैसे प्रभावित करते हैं।

अब जबकि हमारे पास नोड-आधारित मटेरियल के काम करने के तरीके की एक बुनियादी समझ है, आइए देखें कि React Three Fiber में Three.js से नए नोड-आधारित मटेरियल का उपयोग कैसे किया जाता है।

React Three Fiber कार्यान्वयन

अब तक, WebGL के साथ, हम हमारे सामग्री बनाने के लिए MeshBasicMaterial, MeshStandardMaterial, या यहां तक कि कस्टम ShaderMaterial का उपयोग कर रहे थे।

जब हम WebGPU का उपयोग करते हैं, तो हमें नए सामग्री का उपयोग करने की आवश्यकता होती है जो TSL के साथ संगत होते हैं। उनके नाम उसी प्रकार के होते हैं जैसे कि हमने पहले Node के साथ Material से पहले उपयोग किए थे:

  • MeshBasicMaterial -> MeshBasicNodeMaterial
  • MeshStandardMaterial -> MeshStandardNodeMaterial
  • MeshPhysicalMaterial -> MeshPhysicalNodeMaterial
  • ...

उन्हें React Three Fiber के साथ डिक्लरेटिव रूप से उपयोग करने के लिए, हमें उन्हें extend करना होता है। App.jsx में:

// ...
import { extend } from "@react-three/fiber";

extend({
  MeshBasicNodeMaterial: THREE.MeshBasicNodeMaterial,
  MeshStandardNodeMaterial: THREE.MeshStandardNodeMaterial,
});
// ...

React Three Fiber के भविष्य के संस्करणों में, यह स्वचालित रूप से किया जा सकता है।

अब हम अपने घटकों में नई MeshBasicNodeMaterial और MeshStandardNodeMaterial का उपयोग कर सकते हैं।

आइए हमारे Experience घटक में क्यूब की MeshStandardMaterial को MeshStandardNodeMaterial से बदलें:

<mesh>
  <boxGeometry args={[1, 1, 1]} />
  <meshStandardNodeMaterial color="pink" />
</mesh>

WebGPU गुलाबी क्यूब

हम MeshStandardNodeMaterial का उपयोग उसी तरह कर सकते हैं जैसे हम MeshStandardMaterial का उपयोग करते थे।

हमारा क्यूब अब MeshStandardMaterial के बजाय MeshStandardNodeMaterial पर निर्भर कर रहा है। अब हम सामग्री को अनुकूलित करने के लिए नोड्स का उपयोग कर सकते हैं।

रंग नोड

आइए सीखते हैं कि कैसे हम TSL का उपयोग करके अपने मैटेरियल्स को पर्सनलाइज़ करने के लिए कस्टम नोड्स बनाएं।

सबसे पहले, src/components फोल्डर में PracticeNodeMaterial.jsx नाम का एक नया घटक बनाएं।

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  return <meshStandardNodeMaterial color={colorA} />;
};

और Experience.jsx में, हमारे क्यूब को PracticeNodeMaterial का उपयोग करते हुए एक प्लेन के साथ बदल दें:

// ...
import { PracticeNodeMaterial } from "./PracticeNodeMaterial";

export const Experience = () => {
  return (
    <>
      {/* ... */}

      <mesh rotation-x={-Math.PI / 2}>
        <planeGeometry args={[2, 2, 200, 200]} />
        <PracticeNodeMaterial />
      </mesh>
    </>
  );
};

WebGPU Plane

हमारे पास PracticeNodeMaterial के साथ एक प्लेन है।

अपने मैटेरियल को कस्टमाइज़ करने के लिए, अब हम विभिन्न नोड्स का उपयोग कर सकते हैं जो हमारे पास उपलब्ध हैं। उपलब्ध नोड्स की सूची wiki page में पाई जा सकती है।

चलिए colorNode नोड के साथ शुरू करते हैं ताकि हम अपने मैटेरियल के रंग को बदल सकें। PracticeNodeMaterial.jsx में:

import { color } from "three/tsl";

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  return <meshStandardNodeMaterial colorNode={color(colorA)} />;
};

हमने colorNode प्रोप को three/tsl मॉड्यूल के color नोड का उपयोग करके सेट किया। color नोड एक रंग को एक तर्क के रूप में लेता है और एक रंग नोड को वापस करता है जिसे मैटेरियल में उपयोग किया जा सकता है।

यह हमें पहले के समान परिणाम देता है, लेकिन अब हम अपने मैटेरियल को कस्टमाइज़ करने के लिए और अधिक नोड्स जोड़ सकते हैं।

चलिए three/tsl मॉड्यूल से mix और uv नोड्स को आयात करें और उन्हें प्लेन के UV कॉर्डीनेट्स पर आधारित दो रंगों को मिलाने के लिए उपयोग करें।

import { color, mix, uv } from "three/tsl";

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  return (
    <meshStandardNodeMaterial
      colorNode={mix(color(colorA), color(colorB), uv())}
    />
  );
};

यह विभिन्न नोड्स को कार्यान्वित करेगा ताकि मैटेरियल का अंतिम रंग प्राप्त हो सके। mix नोड दो रंग और एक फैक्टर (इस मामले में, UV कॉर्डीनेट्स) लेता है और फैक्टर के आधार पर दोनों रंगों का मिश्रित रंग वापस करता है।

यह GLSL में mix फ़ंक्शन का उपयोग करने के समान ही है, लेकिन अब हम इसे एक नोड-बेस्ड दृष्टिकोण में उपयोग कर सकते हैं। (काफी अधिक पठनीय!)

WebGPU Plane with Mix

हम अब दो रंगों को प्लेन के UV कॉर्डीनेट्स के आधार पर मिश्रित देख सकते हैं।

यह अद्भुत है कि हम शून्य से शुरू नहीं कर रहे हैं। हम मौजूदा MeshStandardNodeMaterial का उपयोग कर रहे हैं और इसमें केवल अपने कस्टम नोड्स जोड़ रहे हैं। इसका मतलब है कि MeshStandardNodeMaterial की छायाएँ, लाइट्स, और अन्य सभी विशेषताएँ अभी भी उपलब्ध हैं।

इनलाइन नोड्स को डिक्लेयर करना बहुत सरल नोड लॉजिक के लिए ठीक है, लेकिन अधिक जटिल लॉजिक के लिए, मैं आपको नोड्स (और बाद में uniforms, और अधिक) को useMemo हुक में डिक्लेयर करने की सिफारिश करता हूँ:

// ...
import { useMemo } from "react";

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  const { nodes } = useMemo(() => {
    return {
      nodes: {
        colorNode: mix(color(colorA), color(colorB), uv()),
      },
    };
  }, []);

  return <meshStandardNodeMaterial {...nodes} />;
};

यह बिल्कुल पहले जैसा ही करता है, लेकिन अब हम और अधिक नोड्स को nodes ऑब्जेक्ट में जोड़ सकते हैं और उन्हें meshStandardNodeMaterial को अधिक संगठित/सामान्य तरीके से पास कर सकते हैं।

colorA और colorB प्रोप्स को बदलकर, यह शेडर की पुनः संकलन का कारण नहीं बनेगा, useMemo हुक के धन्यवाद।

चलो मैटेरियल के रंगों को बदलने के लिए controls जोड़ते हैं। Experience.jsx में:

// ...
import { useControls } from "leva";

export const Experience = () => {
  const { colorA, colorB } = useControls({
    colorA: { value: "skyblue" },
    colorB: { value: "blueviolet" },
  });
  return (
    <>
      {/* ... */}

      <mesh rotation-x={-Math.PI / 2}>
        <planeGeometry args={[2, 2, 200, 200]} />
        <PracticeNodeMaterial colorA={colorA} colorB={colorB} />
      </mesh>
    </>
  );
};

डिफॉल्ट रंग सही ढंग से काम कर रहा है, लेकिन रंगों के अपडेट का प्रभाव नहीं पड़ता है।

यह इसलिए है क्योंकि हमें meshStandardNodeMaterial को uniforms के रूप में रंगों को पास करने की आवश्यकता है।

Uniforms

TSL में uniforms को घोषित करने के लिए, हम three/tsl मॉड्यूल से uniform node का उपयोग कर सकते हैं। uniform node एक मान को आर्ग्युमेंट के रूप में लेता है (यह विभिन्न प्रकारों का हो सकता है जैसे float, vec3, vec4, आदि) और एक यूनिफॉर्म node लौटाता है जिसे विभिन्न नोड्स में उपयोग किया जा सकता है और हमारे कंपोनेंट कोड से अपडेट किया जा सकता है।

PracticeNodeMaterial.jsx में हार्डकोडेड रंगों को uniforms में बदलें:

// ...
import { uniform } from "three/tsl";

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  const { nodes, uniforms } = useMemo(() => {
    const uniforms = {
      colorA: uniform(color(colorA)),
      colorB: uniform(color(colorB)),
    };

    return {
      nodes: {
        colorNode: mix(uniforms.colorA, uniforms.colorB, uv()),
      },
      uniforms,
    };
  }, []);

  return <meshStandardNodeMaterial {...nodes} />;
};

हम कोड के बेहतर संगठन के लिए एक uniforms ऑब्जेक्ट घोषित करते हैं, और नोड्स के निर्माण के समय प्राप्त डिफ़ॉल्ट मान के बजाय यूनिफॉर्म मानों का उपयोग करते हैं।

उन्हें useMemo में लौटाकर, अब हमारे कंपोनेंट में यूनिफॉर्म्स तक पहुंच है।

useFrame में हम यूनिफॉर्म्स को अपडेट कर सकते हैं:

// ...
import { useFrame } from "@react-three/fiber";

export const PracticeNodeMaterial = ({
  colorA = "white",
  colorB = "orange",
}) => {
  // ...

  useFrame(() => {
    uniforms.colorA.value.set(colorA);
    uniforms.colorB.value.set(colorB);
  });

  return <meshStandardNodeMaterial {...nodes} />;
};

जब आप एक ऑब्जेक्ट यूनिफॉर्म को अपडेट करते हैं तो value.set मेथड का उपयोग करें। उदाहरण के लिए, color या vec3 यूनिफॉर्म्स। float यूनिफॉर्म्स के लिए, आपको मान को सीधे सेट करना होगा: uniforms.opacity.value = opacity;

अब रंग सही तरीके से रियल-टाइम में अपडेट हो रहे हैं।

रंग में अधिक करने से पहले, देखते हैं कि हम कैसे अपने plane के वर्टेक्स के पोजीशन को positionNode का उपयोग करके प्रभावित कर सकते हैं।

Position Node

positionNode नोड हमें हमारी ज्यामिति के वर्टेक्स के पोजीशन को प्रभावित करने की अनुमति देता है।

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.