نستخدم الفسيفساء وأقنعة البكسل والأقنعة الهندسية في تظليل Voronoi لتزيين الموقع

الصورة

هذه المقالة هي استمرار منطقي لإدخال تظليل البرمجة لمصممي التخطيط . في ذلك ، قمنا بعمل قالب لإنشاء تأثيرات ثنائية الأبعاد مختلفة باستخدام صور تظليل ونظرنا في بعض الأمثلة. في هذه المقالة ، سنضيف المزيد من القوام ، ونطبق تقسيم Voronoi عمليًا لإنشاء فسيفساء منهم ، ونتحدث عن إنشاء أقنعة مختلفة في التظليل ، وحول البيكسل ، ونتناول أيضًا بعض مشاكل بناء الجملة GLSL القديمة التي لا تزال موجودة في متصفحاتنا.


تمامًا مثل المرة الأخيرة ، سيكون هناك حد أدنى من النظرية وحد أقصى من الممارسة والتفكير بلغة يومية دنيوية. سيجد المبتدئون هنا سلسلة من الإجراءات التي تحتوي على نصائح وملاحظات مفيدة ، وقد يجد بائعو الواجهة الأمامية المتمرسون بعض الأفكار للإلهام.


أظهر استطلاع للرأي في مقال سابق أن موضوع تأثيرات WebGL للمواقع قد لا يهم فقط الحروف المطبوعة ، ولكن أيضًا لزملائنا من التخصصات الأخرى. من أجل عدم إرباكهم بأحدث ميزات ES ، فإننا نقتصر عن عمد على تركيبات بناء أكثر تقليدية يفهمها الجميع. ومرة أخرى ، استرعي انتباه القراء إلى حقيقة أن المحررين المدمجين من CodePen يؤثرون على أداء ما يتم القيام به فيهم.


ولكن دعونا نبدأ ...


قالب للعمل مع تظليل


بالنسبة لأولئك الذين لم يقرأوا المقالة السابقة ، قمنا بعمل هذا القالب للعمل مع تظليل:



يتم إنشاء طائرة فيه (في حالتنا ، مربع) يتم فيها رسم نسيج الصورة. لا تبعيات غير ضرورية وتظليل قمة الرأس بسيط للغاية. ثم قمنا بتطوير هذا القالب ، ولكن الآن سنبدأ من اللحظة التي لا يوجد فيها منطق في تظليل القطع حتى الآن.


فسيفساء


الفسيفساء هي طائرة مقسمة إلى مناطق صغيرة ، حيث تمتلئ كل منطقة بلون معين أو ، كما في حالتنا ، نسيج. كيف يمكننا حتى تحطيم طائرتنا إلى قطع؟ من الواضح أنه يمكنك كسرها إلى مستطيلات. ولكن هذا الأمر سهل للغاية بالفعل بمساعدة SVG ، لسحب WebGL إلى هذه المهمة وإخفاء كل شيء عن أي هدف على الإطلاق.


لكي تكون الفسيفساء مثيرة للاهتمام ، يجب أن تحتوي على أجزاء مختلفة ، في الشكل والحجم. هناك طريقة واحدة بسيطة للغاية ، ولكنها في نفس الوقت طريقة مسلية للغاية لبناء مثل هذا القسم. تُعرف باسم فسيفساء Voronoi أو قسم Dirichlet ، وعلى ويكيبيديا ، كتب أن ديكارت استخدم شيئًا مشابهًا في القرن السابع عشر البعيد. الفكرة شيء مثل هذا:


  • خذ مجموعة من النقاط على متن الطائرة.
  • لكل نقطة في الطائرة ، ابحث عن النقطة الأقرب إليها من هذه المجموعة.
  • هذا كل شئ. تنقسم الطائرة إلى مناطق متعددة الأضلاع ، يتم تحديد كل منها بإحدى النقاط في المجموعة.

ربما من الأفضل إظهار هذه العملية بمثال عملي. هناك خوارزميات مختلفة لإنشاء هذا القسم ، ولكننا سنعمل في الجبين ، لأن حساب شيء ما لكل نقطة على المستوى هو مجرد مهمة للتظليل. نحتاج أولاً إلى عمل مجموعة من النقاط العشوائية. حتى لا يتم تحميل كود الأمثلة ، سنقوم بعمل متغير عام لهم.


function createPoints() { for (let i = 0; i < NUMBER_OF_POINTS; i++) { POINTS.push([Math.random(), Math.random()]); } } 

الآن نحن بحاجة لتمريرها إلى تظليل. البيانات عالمية ، لذا سنستخدم المُعدل uniform . لكن هناك نقطة واحدة دقيقة: لا يمكننا تمرير مصفوفة فقط. يبدو أن القرن الحادي والعشرين في الفناء ، ولكن مع ذلك لن يأتي شيء منه. نتيجة لذلك ، يجب عليك نقل مجموعة من النقاط واحدة في كل مرة.


 for (let i = 0; i < NUMBER_OF_POINTS; i++) { GL.uniform2fv(GL.getUniformLocation(PROGRAM, 'u_points[' + i + ']'), POINTS[i]); } 

اليوم ، سنواجه غالبًا مشاكل مماثلة من التناقض بين ما هو متوقع وما هو في المتصفحات الحقيقية. عادة تستخدم برامج WebGL التعليمية THREE.js وهذه المكتبة تخفي بعض الأوساخ في حد ذاتها ، كما فعلت jQuery ذات مرة في مهامها ، ولكن إذا قمت بإزالتها ، فإنها تؤذي دماغك حقًا.


في تظليل الجزء ، لدينا متغير صفيف للنقاط. يمكننا فقط إنشاء صفائف ذات طول ثابت. لنبدأ بـ 10 نقاط:


 #define NUMBER_OF_POINTS 10 uniform vec2 u_points[NUMBER_OF_POINTS]; 

تأكد من أن كل هذا يعمل عن طريق رسم الدوائر في أماكن النقاط. غالبًا ما يتم استخدام هذا الرسم لمختلف الأوليات الهندسية أثناء التصحيح - فهي مرئية بوضوح ويمكنك أن تفهم على الفور ما هو موقعه وأين تتحرك.


استخدم "رسم" الدوائر والخطوط والمعالم الأخرى للكائنات غير المرئية التي بنيت عليها الرسوم المتحركة. سيعطي هذا أدلة واضحة حول كيفية عملها ، خاصة إذا كانت الخوارزميات معقدة لفهمها بسرعة دون إعداد مسبق. ثم يمكن التعليق على كل هذا وتركه للزملاء - سيقولون الشكر.

 for (int i = 0; i < NUMBER_OF_POINTS; i++) { if (distance(texture_coord, u_points[i]) < 0.02) { gl_FragColor = WHITE; break; } } 


جيد. دعنا نضيف أيضًا بعض الحركة إلى النقاط. دعهم يتحركون في دائرة لتبدأ ، ثم سنعود إلى هذه المشكلة لاحقًا. يتم أيضًا وضع المعاملات على العين ، فقط لإبطاء حركتها قليلاً وتقليل اتساع التذبذبات.


 function movePoints(timeStamp) { if (timeStamp) { for (let i = 0; i < NUMBER_OF_POINTS; i++) { POINTS[i][0] += Math.sin(i * timeStamp / 5000.0) / 500.0; POINTS[i][1] += Math.cos(i * timeStamp / 5000.0) / 500.0; } } } 

عد إلى التظليل. للتجارب المستقبلية ، سنجد أعدادًا مفيدة من المجالات التي سيتم تقسيم كل شيء فيها. لذلك نجد النقطة الأقرب إلى البكسل الحالي من المجموعة ونحفظ رقم تلك النقطة - إنها رقم المنطقة.


 float min_distance = 1.0; int area_index = 0; for (int i = 0; i < NUMBER_OF_POINTS; i++) { float current_distance = distance(texture_coord, u_points[i]); if (current_distance < min_distance) { min_distance = current_distance; area_index = i; } } 

لاختبار الأداء ، نرسم مرة أخرى كل شيء بألوان زاهية:


 gl_FragColor = texture2D(u_texture, texture_coord); gl_FragColor.g = abs(sin(float(area_index))); gl_FragColor.b = abs(sin(float(area_index))); 

غالبًا ما يتم استخدام تركيبة الوحدة (القيمة المطلقة) والوظائف المحدودة (خاصة الخطية والخطية) عند العمل مع تأثيرات مماثلة. من ناحية ، يضيف هذا قليلاً من العشوائية ، ومن ناحية أخرى ، يعطي على الفور نتيجة طبيعية من 0 إلى 1 ، وهو أمر مريح للغاية - لدينا العديد من القيم التي تقع بالضبط ضمن هذه الحدود.

سنجد أيضًا نقاطًا أكثر أو أقل مسافة من عدة نقاط من المجموعة ، ونلونها. هذا الإجراء لا يحمل حمولة خاصة ، لكن النظر إلى النتيجة لا يزال مثيرًا للاهتمام.


 int number_of_near_points = 0; for (int i = 0; i < NUMBER_OF_POINTS; i++) { if (distance(texture_coord, u_points[i]) < min_distance + EPSILON) { number_of_near_points++; } } if (number_of_near_points > 1) { gl_FragColor.rgb = vec3(1.0); } 

يجب أن تحصل على شيء مثل هذا:



لا تزال هذه مسودة ، وسنواصل الانتهاء منها. ولكن الآن المفهوم العام لمثل هذا الفصل في الطائرة واضح.


فسيفساء من الصور


من الواضح أنه في شكله النقي لا يوجد فائدة كبيرة من مثل هذا التقسيم. لتوسيع آفاقك وللمتعة فقط ، يمكنك اللعب معه ، ولكن على موقع حقيقي ، سيكون من المفيد إضافة بضع صور أخرى وإنشاء فسيفساء منها. دعونا نعيد وظيفة إنشاء القوام قليلاً ، بحيث يكون هناك أكثر من واحد.


 function createTextures() { for (let i = 0; i < URLS.textures.length; i++) { createTexture(i); } } function createTexture(index) { const image = new Image(); image.crossOrigin = 'anonymous'; image.onload = () => { const texture = GL.createTexture(); GL.activeTexture(GL['TEXTURE' + index]); GL.bindTexture(GL.TEXTURE_2D, texture); GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, true); GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGB, GL.RGB, GL.UNSIGNED_BYTE, image); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.LINEAR); GL.uniform1i(GL.getUniformLocation(PROGRAM, 'u_textures[' + index + ']'), index); }; image.src = URLS.textures[index]; } 

لم يحدث شيء غير عادي ، لقد استبدلنا الأصفار بمعلمة index وأعدنا استخدام الرمز الحالي لتحميل الأنسجة الثلاثة. في الظل ، لدينا الآن مجموعة من القوام:


 #define NUMBER_OF_TEXTURES 3 uniform sampler2D u_textures[NUMBER_OF_TEXTURES]; 

يمكننا الآن استخدام رقم المنطقة المحفوظ سابقًا لتحديد أحد الأنسجة الثلاثة. لكن ...


ولكن قبل ذلك ، أود أن أقوم بحفر صغير. عن قرحة. حول بناء الجملة. لغة جافا سكريبت الحديثة (ES6 بشكل مشروط) هي لغة جميلة. يسمح لك بالتعبير عن أفكارك عند ظهورها ، ولا يقصر إطار العمل على أي نموذج برمجي محدد ، ويكمل بعض النقاط بالنسبة لنا ويسمح لك بالتركيز على الفكرة أكثر من تنفيذها. بالنسبة لمنشئ المحتوى - هذا كل ما في الأمر. يعتقد بعض الناس أنه يمنح الكثير من الحرية ويتحول إلى TypeScript على سبيل المثال. Pure C هي لغة أكثر صرامة. كما أنه يسمح بالكثير ، يمكنك إغراء أي شيء عليه ، ولكن بعد JS يُنظر إليه على أنه محرج قليلاً أو قديم الطراز أو شيء. ومع ذلك ، لا يزال جيدًا. GLSL كما هو موجود في المتصفحات هو مجرد شيء. ليس فقط أنه ترتيب أكثر صرامة من C ، ولكنه لا يزال يفتقر إلى العديد من العوامل المألوفة وبنيات بناء الجملة. ربما تكون هذه هي المشكلة الأكبر عند كتابة تظليل أكثر أو أقل تعقيدًا لـ WebGL. وراء الرعب الذي يتحول إليه الرمز ، قد يكون من الصعب جدًا إلقاء نظرة على الخوارزمية الأصلية. يعتقد بعض المبرمجين أنه حتى يتعلموا C ، فإن الطريق إلى التظليل مغلق لهم. لذا: معرفة C لن تساعد بشكل خاص هنا. هنا نوع من العالم الخاص به. عالم الجنون والديناصورات والعكازات.


كيف يمكنني اختيار واحد من ثلاثة مواد لها رقم واحد - رقم المنطقة. يتبادر إلى الذهن الباقي من قسمة الرقم على عدد القوام. فكرة عظيمة. فقط العامل % ، الذي تكتبه الأيدي نفسها بالفعل ، ليس هنا. إن الانطباع بفهم هذه الحقيقة موصوف بشكل جيد في الصورة:


الصورة


بالطبع ، تقول ، "نعم ، ليست مشكلة ، هناك وظيفة mod - دعنا نأخذها!". ولكن اتضح أنها لا تقبل رقمين صحيحين ، كسرين فقط. حسنًا ، قم بعمل float منها. نحصل أيضًا على float ، لكننا بحاجة إلى int . عليك تحويل كل شيء مرة أخرى ، وإلا فهناك فرصة غير وهمية للحصول على خطأ في الترجمة.


 int texture_index = int(mod(float(area_index), float(NUMBER_OF_TEXTURES))); 

وهنا سؤال بلاغي: ربما سيكون من الأسهل إدراك وظيفتها لبقية التقسيم الصحيح من محاولة تجميعها من الأساليب القياسية؟ ولا تزال هذه وظيفة بسيطة ، ويحدث أنه يتم الحصول على تسلسلات متضمنة للغاية من هذه التحولات لم يعد من الواضح فيها ما يحدث.


حسنًا ، دعنا نتركها كما هي الآن. ما عليك gl_FragColor أخذ لون البكسل المطلوب من النسيج المحدد وتعيينه إلى المتغير gl_FragColor . اذن؟ هل فعلنا هذا بالفعل؟ ثم تظهر هذه القطة مرة أخرى. لا يمكنك استخدام غير ثابت عند الوصول إلى صفيف. وكل ما حسبناه لم يعد ثابتًا. Ba-dum-tsss !!!


عليك القيام بشيء مثل هذا:


 if (texture_index == 0) { gl_FragColor = texture2D(u_textures[0], texture_coord); } else if (texture_index == 1) { gl_FragColor = texture2D(u_textures[1], texture_coord); } else if (texture_index == 2) { gl_FragColor = texture2D(u_textures[2], texture_coord); } 

توافق ، مثل هذا الرمز هو طريق مباشر إلى govnokod.ru ، ولكن مع ذلك ، فهو مختلف بأي شكل من الأشكال. حتى switch-case ليس هنا على الأقل لإدانة هذا الخزي بطريقة أو بأخرى. هناك بالفعل عكاز آخر أقل وضوحًا يحل نفس المشكلة:


 for (int i = 0; i < 3; i++) { if (texture_index == i) { gl_FragColor = texture2D(u_textures[i], texture_coord); } } 

عدادات الدورة ، التي تزداد بمقدار واحد ، يمكن أن يحسب المترجم على أنه ثابت. لكن هذا لم ينجح مع مجموعة من القوام - في Chrome الأخير ، ظهر خطأ قائلًا إنه كان من المستحيل القيام بذلك مع مجموعة من القوام. مع مجموعة من الأرقام ، عملت. خمن لماذا يعمل مع صفيف واحد ، ولكن ليس مع صفيف آخر؟ إذا كنت تعتقد أن نظام الصب في JS مليء بالسحر - قم بفرز النظام "الثابت - غير الثابت" في GLSL. الشيء المضحك هو أن النتائج تعتمد أيضًا على بطاقة الفيديو المستخدمة ، لذلك قد تنهار العكازات الصعبة التي عملت على بطاقة رسومات NVIDIA بشكل جيد على AMD.


من الأفضل تجنب مثل هذه القرارات بناءً على افتراضات حول المترجم. تميل إلى الانكسار ويصعب اختبارها.

الحزن هو الحزن. ولكن إذا أردنا القيام بأشياء مثيرة للاهتمام ، فنحن بحاجة إلى التجريد من كل هذا والاستمرار.


في الوقت الحالي ، حصلنا على فسيفساء من الصور. لكن هناك تفصيل واحد: إذا كانت النقاط قريبة جدًا من بعضها البعض ، فهناك انتقال سريع لمنطقتين. انها ليست جميلة جدا تحتاج إلى إضافة بعض الخوارزمية التي لا تسمح بالاقتراب من النقاط. يمكنك إنشاء خيار بسيط ، يتم فيه تحديد المسافات بين النقاط ، وإذا كان أقل من قيمة معينة ، فإننا ننفصل بينهما. لا يخلو هذا الخيار من عيوب ، على وجه الخصوص ، فهو يؤدي في بعض الأحيان إلى ارتعاش بسيط في النقاط ، ولكن في كثير من الحالات قد يكون كافيًا ، خاصة أنه لا توجد حسابات كثيرة جدًا هنا. ستكون الخيارات الأكثر تقدمًا هي نظام الشحنات المتحركة و "شبكة العنكبوت" التي يتم فيها توصيل أزواج من النقاط بواسطة الينابيع غير المرئية. إذا كنت مهتمًا بتنفيذها ، فيمكنك بسهولة العثور على جميع الصيغ في كتاب مراجع الفيزياء للمدرسة الثانوية.


 for (let i = 0; i < NUMBER_OF_POINTS; i++) { for (let j = i; j < NUMBER_OF_POINTS; j++) { let deltaX = POINTS[i][0] - POINTS[j][0]; let deltaY = POINTS[i][1] - POINTS[j][1]; let distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 0.1) { POINTS[i][0] += 0.001 * Math.sign(deltaX); POINTS[i][1] += 0.001 * Math.sign(deltaY); POINTS[j][0] -= 0.001 * Math.sign(deltaX); POINTS[j][1] -= 0.001 * Math.sign(deltaY); } } } 

المشكلة الرئيسية في هذا النهج ، بالإضافة إلى تلك التي استخدمناها في التظليل ، هي مقارنة جميع النقاط مع الكل. لا تحتاج إلى أن تكون عالم رياضيات كبيرًا لفهم أن عدد حسابات المسافة سيكون مذهلاً إذا لم نحصل على 10 نقاط ، ولكن 1000. نعم ، حتى 100 كافية لإبطاء كل شيء. لذلك ، من المنطقي تطبيقه على عدد قليل من النقاط فقط.


إذا أردنا عمل مثل هذه الفسيفساء لعدد كبير من النقاط ، فيمكننا استخدام التقسيم المألوف للطائرة إلى مربعات متطابقة. الفكرة هي وضع نقطة واحدة في كل مربع ، ثم إجراء جميع المقارنات فقط مع نقاط من المربعات المجاورة. فكرة جيدة ، لكن التجارب أظهرت أنه مع وجود عدد كبير من النقاط ، لا تزال أجهزة الكمبيوتر المحمولة غير المكلفة المزودة ببطاقات الفيديو المدمجة غير قادرة على التأقلم. لذلك ، يجدر التفكير عشر مرات قبل أن تقرر عمل مثل هذه الفسيفساء على موقعك من عدد كبير من الأجزاء.


لا تكن فجلًا ، تحقق من أداء الحرف الخاصة بك ليس فقط في مزرعة التعدين الخاصة بك ، ولكن أيضًا على أجهزة الكمبيوتر المحمولة العادية. سيكون المستخدمون هم الأساس.


تقسيم المستوى وفقًا للرسم البياني للوظيفة


دعونا نرى خيارًا آخر لتقسيم طائرة إلى أجزاء. لم تعد تتطلب قوة حوسبة كبيرة. الفكرة الرئيسية هي أخذ بعض الوظائف الرياضية وبناء رسمها البياني. سيقسم الخط الناتج الطائرة إلى قسمين. إذا استخدمنا دالة y = f(x) ، نحصل على القسمة على شكل قطع. باستبدال X بـ Y ، يمكننا تغيير القسم الأفقي إلى عمودي. إذا كنت تأخذ الوظيفة في الإحداثيات القطبية ، فأنت بحاجة إلى ترجمة كل شيء إلى الديكارتي والعكس بالعكس ، ولكن جوهر الحسابات لن يتغير. في هذه الحالة ، لا تكون النتيجة قطعًا إلى جزأين ، بل قطع ثقب. لكننا سنرى الخيار الأول.


لكل Y ، سنحسب قيمة X لعمل قسم عمودي. يمكننا أن نتخذ موجة جيبية لهذه الأغراض ، على سبيل المثال ، لكنها مملة للغاية. من الأفضل أخذ بضع قطع دفعة واحدة وطيها.


نأخذ العديد من الجيوب الأنفية ، كل منها مرتبط بإحداثية على طول Y والوقت ، ونضيفها. سيطلق الفيزيائيون هذا التراكب الإضافي. من الواضح ، بضرب النتيجة بأكملها في رقم ما ، نقوم بتغيير السعة. أخرجه في ماكرو منفصل. إذا قمت بضرب الإحداثيات - معلمة الجيب ، فسيتغير التردد. لقد رأينا هذا بالفعل في مقال سابق. نزيل أيضًا معدّل التردد الشائع لجميع الجيوب الأنفية من الصيغة. لن يكون من غير الضروري اللعب بمرور الوقت ، ستعطي الإشارة السلبية تأثير تحريك الخط في الاتجاه المعاكس.


 float time = u_time * SPEED; float x = (sin(texture_coord.y * FREQUENCY) + sin(texture_coord.y * FREQUENCY * 2.1 + time) + sin(texture_coord.y * FREQUENCY * 1.72 + time * 1.121) + sin(texture_coord.y * FREQUENCY * 2.221 + time * 0.437) + sin(texture_coord.y * FREQUENCY * 3.1122 + time * 4.269)) * AMPLITUDE; 

بعد إجراء مثل هذه الإعدادات العالمية لوظيفتنا ، سنواجه مشكلة تكرار نفس الحركة على فترات قصيرة إلى حد ما. من أجل حل هذه المشكلة ، نحتاج إلى ضرب كل شيء في المعاملات التي يكون أصغر المضاعف المشترك فيها كبيرًا جدًا. يتم استخدام شيء مماثل أيضًا في مولد الأرقام العشوائية ، هل تتذكر؟ في هذه الحالة ، لم نفكر وأخذنا أرقامًا جاهزة من بعض الأمثلة من الإنترنت ، ولكن لا أحد يزعج من تجربة قيمنا.


يبقى فقط اختيار أحد النسيجين للنقاط فوق الرسم البياني للوظيفة والثاني للنقاط أسفله. بتعبير أدق إلى اليسار واليمين ، لجأنا جميعًا:


 if (texture_coord.x - 0.5 > x) { gl_FragColor = texture2D(u_textures[0], texture_coord); } else { gl_FragColor = texture2D(u_textures[1], texture_coord); } 

ما تلقيناه يشبه الموجات الصوتية. بتعبير أدق ، صورتهم على راسم التذبذب. في الواقع ، يمكننا بدلاً من إرسال الجيوب الأنفية لدينا نقل البيانات من نوع من الملفات الصوتية. لكن العمل مع الصوت هو موضوع لمقال منفصل.



أقنعة


يجب أن تؤدي الأمثلة السابقة إلى ملاحظة منطقية تمامًا: كل هذا يبدو وكأنه عمل الأقنعة في SVG (إذا لم تكن قد عملت معهم ، فراجع الأمثلة من المقالة أقنعة SVG وتأثيرات نجاح باهر ). إنه فقط أننا هنا نقوم بها بشكل مختلف قليلاً. والنتيجة هي نفسها - بعض المناطق مطلية بمادة واحدة ، وبعضها بأخرى. فقط التحولات السلسة لم تكن بعد. لذلك دعونا نفعل واحدة.


نزيل كل ما هو غير ضروري ونعيد إحداثيات الفأرة. قم بعمل تدرج شعاعي مع المركز في موقع المؤشر واستخدمه كقناع. في هذا المثال ، سيشبه سلوك التظليل بشكل وثيق منطق الأقنعة في SVG مما كان عليه في الأمثلة السابقة. نحن بحاجة إلى وظيفة mix وبعض وظيفة المسافة. سيقوم الأول بخلط قيم لون البكسل من كلا النسيجين ، مع الأخذ في الاعتبار كمعامل ثالث معامل (من 0 إلى 1) يحدد أي من القيم سيسود نتيجة لذلك. نأخذ معامل الجيب كدالة للمسافة - سيعطي فقط تغييرًا سلسًا في القيمة بين 0 و 1.


 gl_FragColor = mix( texture2D(u_textures[0], texture_coord), texture2D(u_textures[1], texture_coord), abs(sin(length(texture_coord - u_mouse_position / u_canvas_size)))); 

هذا كل شئ. دعونا نلقي نظرة على النتيجة:



الميزة الرئيسية على SVG واضحة:


على عكس SVG ، يمكننا هنا بسهولة إنشاء تدرجات متجانسة لمختلف الوظائف الرياضية ، وعدم جمعها من العديد من التدرجات الخطية.

إذا كانت لديك مهمة أبسط لا تتطلب مثل هذه التحولات السلسة أو الأشكال المعقدة التي يتم حسابها في العملية ، فمن المرجح أن يكون تنفيذها أسهل بدون استخدام أجهزة تظليل. نعم ، ومن المرجح أن يكون الأداء على الأجهزة الضعيفة أفضل. اختر أداة بناءً على مهامك.


للأغراض التعليمية ، دعنا نرى مثالاً آخر. أولاً ، قم بعمل دائرة ستبقى فيها المادة كما هي:


 gl_FragColor = texture2D(u_textures[0], texture_coord); float dist = distance(texture_coord, u_mouse_position / u_canvas_size); if (dist < 0.3) { return; } 

واملأ الباقي بخطوط قطرية:


 float value = sin((texture_coord.y - texture_coord.x) * 200.0); if (value > 0.0) { gl_FragColor.rgb *= dist; } else { gl_FragColor.rgb *= dist / 10.0; } 

القبول هو نفسه - نضرب معلمة الجيب لزيادة تردد الخطوط ؛ تقسيم القيم التي تم الحصول عليها إلى قسمين ؛ لكل نصفين ، نقوم بتحويل لون البكسل بطريقتنا الخاصة. من المفيد أن نتذكر أن رسم الخطوط القطرية يرتبط عادةً بإضافة إحداثيات في X و Y. لاحظ أننا نستخدم أيضًا المسافة إلى مؤشر الماوس عند تغيير الألوان ، وبالتالي إنشاء نوع من الظل. بنفس الطريقة ، يمكنك استخدامه مع التحولات الهندسية ، وسنرى ذلك قريبًا على مثال البيكسيلاشن. في غضون ذلك ، ألق نظرة على نتيجة هذا التظليل:



بسيطة وجميلة.


ونعم ، إذا كنت مرتبكًا بعض الشيء ، يمكنك صنع مواد ليس من الصور ، ولكن من إطارات من مقاطع الفيديو (هناك العديد من الأمثلة على الشبكة ، يمكنك اكتشافها بسهولة) ، وتطبيق جميع تأثيراتنا عليها. تستخدم العديد من مواقع الدليل مثل Awwwards هذه التأثيرات مع الفيديو.

يجدر تذكر فكرة أخرى:


لا أحد يزعج باستخدام أحد القوام كقناع. يمكننا التقاط صورة واستخدام قيم ألوان وحدات البكسل الخاصة بها في تحولاتنا ، سواء كانت تغيرات في ألوان أخرى ، أو تحولات في الجوانب أو أي شيء آخر يتبادر إلى ذهنك.

ولكن نعود إلى تقسيم الطائرة إلى أجزاء.


التقطيع


هذا التأثير واضح إلى حد ما ، ولكن في نفس الوقت من الشائع جدًا أن يكون من الخطأ المرور. قسّم طائرتنا إلى مربعات ، بالطريقة نفسها التي كانت عليها في المثال باستخدام مولد الضجيج ، وبعد ذلك قمنا بتعيين نفس اللون لجميع وحدات البكسل داخل كل مربع. يتم الحصول عليها عن طريق خلط القيم من زوايا مربع ، قمنا بالفعل بشيء مماثل. لهذا الغرض ، لا نحتاج إلى صيغ معقدة ، لذا قم فقط بجمع كل القيم وقسمتها على 4 - عدد زوايا المربع.


 float block_size = abs(sin(u_time)) / 20.0; vec2 block_position = floor(texture_coord / block_size) * block_size; gl_FragColor = ( texture2D(u_textures[0], block_position) + texture2D(u_textures[0], block_position + vec2(1.0, 0.0) * block_size) + texture2D(u_textures[0], block_position + vec2(0.0, 1.0) * block_size) + texture2D(u_textures[0], block_position + vec2(1.0, 1.0) * block_size) ) / 4.0; 

لقد ربطنا مرة أخرى إحدى المعلمات بمرور الوقت من خلال وحدة الجيب لمعرفة ما يحدث عندما تتغير بصريًا.



موجات بكسل


, .


 float block_size = abs(sin( length(texture_coord - u_mouse_position / u_canvas_size) * 2.0 - u_time)) / 100.0 + 0.001; 

, 0 1; , , , . , .



"" , , -. . " ", , . . — . .


الملخص


, , , , . -. - - . . . , , , .




PS: , WebGL ( ) ? , , . ?

Source: https://habr.com/ru/post/ar421821/


All Articles