WebGPU / TSL
WebGPU هو معيار ويب جديد يوفر واجهة برمجة تطبيقات منخفضة المستوى لعرض الرسوميات وإجراء الحسابات على وحدة معالجة الرسوميات (GPU). تم تصميمه ليكون خليفة لـ WebGL، ويقدم أداءً أفضل وميزات أكثر تقدمًا.
خبر سار، أصبح من الممكن الآن استخدامه مع Three.js مع تغييرات طفيفة في قاعدة التعليمات البرمجية.
في هذا الدرس، سوف نستكشف كيفية استخدام WebGPU مع Three.js وReact Three Fiber، وكيفية كتابة الشيدرز باستخدام Three Shading Language (TSL) الجديدة.
إذا كنت جديدًا على الشيدرز، أوصي بإكمال فصل Shaders أولاً قبل متابعة هذا الفصل.
WebGPU Renderer
لاستخدام WebGPU API بدلاً من WebGL، نحتاج إلى استخدام WebGPURenderer
(لا يوجد قسم مخصص له حتى الآن في توثيق Three.js) بدلاً من WebGLRenderer.
مع React Three Fiber، عند إنشاء مكون <Canvas>
، يتم إعداد الـ renderer تلقائيًا. ومع ذلك، يمكننا تجاوز الـ renderer الافتراضي عن طريق تمرير وظيفة إلى خاصية gl
لمكون <Canvas>
.
في App.jsx
، لدينا مكون <Canvas>
يستخدم WebGLRenderer
الافتراضي. دعونا نقوم بتعديله لاستخدام WebGPURenderer
بدلاً منه.
أولاً، نحتاج إلى إيقاف frameloop
حتى يكون WebGPURenderer
جاهزًا. يمكننا القيام بذلك عن طريق تعيين خاصية frameloop
إلى never
.
// ... import { useState } from "react"; function App() { const [frameloop, setFrameloop] = useState("never"); return ( <> {/* ... */} <Canvas // ... frameloop={frameloop} > {/* ... */} </Canvas> </> ); } export default App;
بعد ذلك، نحتاج إلى استيراد إصدار WebGPU من Three.js:
import * as THREE from "three/webgpu";
عند استخدام WebGPU، نحتاج إلى استخدام وحدة
three/webgpu
بدلاً من الوحدة الافتراضيةthree
. ذلك لأنWebGPURenderer
غير مدرج في البناء الافتراضي لـ Three.js.
ثم، يمكننا استخدام خاصية gl
لإنشاء نسخة جديدة من WebGPURenderer
:
// ... 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
ونمرر عنصر canvas
إليها. نضبط أيضًا بعض الخيارات للـ renderer، مثل powerPreference
، antialias
، alpha
، stencil
، وshadowMap
. هذه الخيارات مشابهة للخيارات المستخدمة في WebGLRenderer
.
أخيرًا، نستدعي دالة init()
للـ renderer لتجهيزه. بمجرد اكتمال التجهيز، نقوم بتعيين حالة frameloop
إلى "always"
لبدء العرض.
لنرى النتيجة في المتصفح:
يتم الآن عرض مكعبنا باستخدام WebGPURenderer
بدلاً من WebGLRenderer
.
الأمر بسيط جدًا كما ترى! لقد قمنا بتجهيز WebGPURenderer
بنجاح في تطبيق React Three Fiber الخاص بنا. يمكنك الآن استخدام نفس واجهة برمجة التطبيقات (API) الخاصة بـ Three.js لإنشاء وتعديل الكائنات ثلاثية الأبعاد، تمامًا كما كنت تفعل مع WebGLRenderer
.
أكبر التغييرات تأتي عند كتابة الشيدرز؛ حيث أن واجهة برمجة التطبيقات الخاصة بـ WebGPU تستخدم لغة تظليل مختلفة عن WebGL، وهذا يعني أننا نحتاج لكتابة الشيدرز بطريقة مختلفة، أي باستخدام WGSL بدلاً من GLSL.
هنا يأتي دور Three Shading Language (TSL).
لغة التظليل ثلاثية الأبعاد
TSL هي لغة تظليل جديدة مصممة للاستخدام مع Three.js لكتابة shaders بطريقة أكثر سهولة للمستخدم باستخدام نهج قائم على العقد.
ميزة كبيرة لـ TSL هي أنها غير معتمدة على محرك عرض معين، مما يعني أنه يمكنك استخدام نفس الـ shaders مع محركات عرض مختلفة، مثل WebGL و WebGPU.
هذا يجعل من السهل كتابة وصيانة الـ shaders، حيث لا تحتاج إلى القلق بشأن الفروقات بين لغتي التظليل.
كما أنها مقاومة للتغيير في المستقبل، حيث إذا تم إصدار محرك عرض جديد، يمكننا استخدام نفس الـ shaders بدون أي تغييرات طالما أن TSL تدعمه.
لا تزال لغة التظليل ثلاثية الأبعاد قيد التطوير، لكنها متاحة بالفعل في الإصدارات الأحدث من Three.js. أفضل طريقة لتعلمها ومتابعة التغييرات هي التحقق من صفحة الويكي لـThree Shading Language. لقد استخدمتها بشكل مكثف لتعلم كيفية استخدامها.
المواد القائمة على العقد
لفهم كيفية إنشاء shaders باستخدام TSL، نحتاج إلى فهم ما يعنيه أن يكون النهج قائماً على العقد.
في النهج القائم على العقد، نقوم بإنشاء shaders عن طريق ربط العقد المختلفة معًا لإنشاء رسم بياني. كل عقدة تمثل عملية أو وظيفة معينة، وتُمثل الروابط بين العقد تدفق البيانات.
هذا النهج له العديد من المزايا، مثل:
- تمثيل بصري: يسهل فهم وتصور تدفق البيانات والعمليات في shader.
- قابلية إعادة الاستخدام: يمكننا إنشاء عقد قابلة لإعادة الاستخدام يمكن استخدامها في shaders مختلفة.
- المرونة: يمكننا بسهولة تعديل وتغيير سلوك shader عن طريق إضافة أو إزالة عقد.
- القدرة على التوسع: إضافة/تخصيص الميزات من المواد الموجودة أصبح سهلاً الآن.
- عدم التقيد بمحرك عرض معين: TSL ستولد الكود المناسب لمحرك العرض الهدف، سواء كان WebGL (GLSL) أو WebGPU (WGSL).
قبل أن نبدأ في كتابة الكود لأول مادة قائمة على العقد، يمكننا استخدام الـ Playground الخاص بـThree.js لتجربة النظام القائم على العقد بصريًا.
افتح الـ Playground الخاص بـThree.js وفي الأعلى، انقر على زر Examples، واختر المثال basic > fresnel
.
يجب أن ترى محرر مادة قائمة على العقد مع عقدتي color
وعقدة float
متصلة بعقدة fresnel
. (“Color A”، “Color B”، و “Fresnel Factor”)
العقدة fresnel
متصلة بلون مادة Basic Material
مما ينتج عنه تلوين الإبريق بتأثير الفريسنيل.
استخدم زر Splitscreen
لمعاينة النتيجة على اليمين.
لنفترض أننا نريد التأثير على شفافية مادة Basic Material
بناءً على الوقت. يمكننا إضافة عقدة Timer
وربطها بعقدة Fract
لإعادة ضبط الوقت إلى 0 بمجرد أن يصل إلى 1. ثم نقوم بتوصيله إلى مدخل opacity
لمادة Basic Material
.
الإبريق لدينا الآن يتلاشى للاختفاء قبل أن يتلاشى مرة أخرى.
خذ الوقت للعب مع العقد المختلفة ورؤية كيف تؤثر على المادة.
الآن لدينا فهم أساسي لكيفية عمل المادة القائمة على العقد، لنتعرف على كيفية استخدام المادة الجديدة القائمة على العقد من Three.js في React Three Fiber.
تنفيذ 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
الجديدتين في مكوناتنا.
لنقم باستبدال MeshStandardMaterial
من المكعب في مكون Experience
بـ MeshStandardNodeMaterial
:
<mesh> <boxGeometry args={[1, 1, 1]} /> <meshStandardNodeMaterial color="pink" /> </mesh>
يمكننا استخدام MeshStandardNodeMaterial
تمامًا كما نستخدم MeshStandardMaterial
.
المكعب لدينا يعتمد الآن على MeshStandardNodeMaterial
بدلاً من MeshStandardMaterial
. يمكننا الآن استخدام العقد لتخصيص المادة.
عقدة اللون
فلنتعلم كيفية إنشاء عقد مخصصة لتخصيص المواد لدينا باستخدام TSL.
أولاً، لنقم بإنشاء مكون جديد يسمى PracticeNodeMaterial.jsx
في مجلد src/components
.
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> </> ); };
لدينا الآن مستوى مع PracticeNodeMaterial
.
لتخصيص المواد لدينا، يمكننا الآن التلاعب بالعقد المختلفة المتاحة لنا باستخدام عقد مختلفة. يمكن العثور على قائمة بالعقد المتاحة في صفحة الويكي.
لنبدأ ببساطة بعقدة colorNode
لتغيير لون المواد لدينا. في PracticeNodeMaterial.jsx
:
import { color } from "three/tsl"; export const PracticeNodeMaterial = ({ colorA = "white", colorB = "orange", }) => { return <meshStandardNodeMaterial colorNode={color(colorA)} />; };
لقد قمنا بتعيين المقدار colorNode
باستخدام العقدة color
من وحدة three/tsl
. تأخذ عقدة color
لونًا كوسيطة وتعطي عقدة لون يمكن استخدامها في المواد.
هذا يعطينا نفس النتيجة كما كان من قبل، ولكن الآن يمكننا إضافة المزيد من العقد لتخصيص المواد لدينا.
لنقم باستيراد العقد mix
و uv
من وحدة three/tsl
واستخدامها لمزج لونين استنادًا إلى إحداثيات 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) وتعطي لونًا مزيجًا من اللونين استنادًا إلى المعامل.
إنه بالضبط كما هو الحال عند استخدام الدالة mix
في GLSL، ولكن الآن يمكننا استخدامها بطريقة تعتمد على العقد. (أكثر قابلية للقراءة!)
يمكننا الآن رؤية اللونين مختلطين استنادًا إلى إحداثيات 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> </> ); };
اللون الافتراضي يعمل بشكل صحيح، لكن تحديث الألوان ليس له أي تأثير.
هذا لأننا نحتاج إلى تمرير الألوان كـ uniforms
إلى meshStandardNodeMaterial
.
Uniformات
لإعلان الـ uniformات في TSL، يمكننا استخدام العقدة uniform
من الوحدة three/tsl
. تأخذ العقدة uniform
قيمة كمعامل (يمكن أن تكون من أنواع مختلفة مثل float
، vec3
، vec4
، إلخ) وتعيد عقدة uniform يمكن استخدامها في العقد المختلفة أثناء تحديثها من كود المكون لدينا.
لنقم بتغيير الألوان المرمزة بشكل صلب إلى uniformات في PracticeNodeMaterial.jsx
:
// ... 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
لتنظيم الكود بشكل أفضل، ونستخدم القيم من نوع uniform بدلاً من القيم الافتراضية التي حصلنا عليها عند إنشاء العقد الخاصة بنا.
عن طريق إرجاعهم في useMemo
لدينا الآن إمكانية الوصول إلى الـ uniformات في مكوننا.
في useFrame
يمكننا تحديث الـ uniformات:
// ... 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
عندما تقوم بتحديث كائن uniform. على سبيل المثال، uniformاتcolor
أوvec3
. بالنسبة للـ uniformات من نوعfloat
، تحتاج إلى تعيين القيمة مباشرة:uniforms.opacity.value = opacity;
الألوان الآن يتم تحديثها بشكل صحيح في الوقت الفعلي.
قبل القيام بالمزيد مع الألوان، دعونا نرى كيف يمكننا تأثير المواقع vertices في مجسمنا باستخدام positionNode
.
Node Position
تسمح لنا عقدة positionNode
بالتأثير على موضع رؤوس الشكل الهندسي لدينا.
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
One-time payment. Lifetime updates included.