⚡️ Limited Black Friday Deal
Get 50% off on the React Three Fiber Ultimate Course with the promo code ULTIMATE50
Buy Now
Fundamentals
Core
Master
Shaders
Water Shader
गर्मी आ रही है (कम से कम जब मैं यह पाठ लिख रहा हूँ), यह पूल पार्टी का समय है! 🩳
इस पाठ में, हम React Three Fiber और GLSL का उपयोग करके निम्नलिखित जल प्रभाव बनाएंगे:
फोम पूल के किनारों और बत्तख के चारों ओर अधिक घना है।
इस प्रभाव को बनाने के लिए, हम Lygia Shader library की खोज करेंगे ताकि Shaders के निर्माण को सरल बनाया जा सके और हम फोम प्रभाव बनाने के लिए render target technique का अभ्यास करेंगे।
Starter pack
इस पाठ के लिए प्रारंभिक पैक में निम्नलिखित संपत्तियाँ शामिल हैं:
- Swimming pool model Poly by Google CC-BY के माध्यम से Poly Pizza
- Duck model Pmndrs marketplace से
- Inter font Google Fonts से
बाकी में सरल प्रकाश और कैमरा सेटअप शामिल है।
पूल में एक धूप का दिन 🏊
वॉटर शेडर
वॉटर सिर्फ एक साधारण plane है जिसमें <meshBasicMaterial />
लगाया गया है। हम इस material को एक custom shader से बदलेंगे।
आइए एक नया फाइल WaterMaterial.jsx
बनाते हैं जिसमें shader material के लिए boilerplate होगा:
import { shaderMaterial } from "@react-three/drei"; import { Color } from "three"; export const WaterMaterial = shaderMaterial( { uColor: new Color("skyblue"), uOpacity: 0.8, }, /*glsl*/ ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, /*glsl*/ ` varying vec2 vUv; uniform vec3 uColor; uniform float uOpacity; void main() { gl_FragColor = vec4(uColor, uOpacity); #include <tonemapping_fragment> #include <encodings_fragment> }` );
हमारे material में दो uniforms हैं: uColor
और uOpacity
.
हमारे custom material का declaratively उपयोग करने के लिए, main.jsx
फाइल में @react-three/fiber
से extend
function का उपयोग करें:
// ... import { extend } from "@react-three/fiber"; import { WaterMaterial } from "./components/WaterMaterial.jsx"; extend({ WaterMaterial }); // ...
हमें
extend
कॉल को उस फाइल से करना होगा जो उस कंपोनेंट से पहले इम्पोर्ट की जाती है जो custom material का उपयोग करती है। इस तरह, हम अपने कंपोनेंट्स मेंWaterMaterial
को declaratively उपयोग कर पाएंगे।इसलिए हम इसे
main.jsx
फाइल में करते हैं, न किWaterMaterial.jsx
फाइल में।
अब Water.jsx
में, हम <meshBasicMaterial />
को हमारे custom material से बदल सकते हैं और properties को संबंधित uniforms के साथ समायोजित कर सकते हैं:
import { useControls } from "leva"; import { Color } from "three"; export const Water = ({ ...props }) => { const { waterColor, waterOpacity } = useControls({ waterOpacity: { value: 0.8, min: 0, max: 1 }, waterColor: "#00c3ff", }); return ( <mesh {...props}> <planeGeometry args={[15, 32, 22, 22]} /> <waterMaterial uColor={new Color(waterColor)} transparent uOpacity={waterOpacity} /> </mesh> ); };
हमने सफलतापूर्वक basic material को हमारे custom shader material से बदल दिया है।
Lygia Shader Library
एनिमेटेड फोम इफ़ेक्ट बनाने के लिए, हम Lygia Shader library का उपयोग करेंगे। यह लाइब्रेरी shaders के निर्माण को सरल बनाती है, उपयोगिताओं और फ़ंक्शंस के सेट के साथ shaders को अधिक घोषणात्मक तरीके से बनाने की अनुमति देती है।
जो सेक्शन हमारे लिए रुचिकर होगा वह है generative। इसमें कई उपयोगी फ़ंक्शंस होते हैं जो जैसे noise, curl, fbm जैसे generative इफ़ेक्ट्स बनाने में सहायक होते हैं।
जनरेटिव सेक्शन के भीतर, उपलब्ध फ़ंक्शंस की सूची पाई जा सकती है।
किसी एक फ़ंक्शन को खोलकर, आप देख सकते हैं कि इसे अपने shader में उपयोग करने के लिए कोड स्निपेट और इफ़ेक्ट का प्रीव्यू कैसे दिखता है।
pnoise फ़ंक्शन पेज
यह वही इफ़ेक्ट है जिसका हम उपयोग करना चाहते हैं। उदाहरण में आप देख सकते हैं कि pnoise
फ़ंक्शन का उपयोग करने के लिए, उन्होंने pnoise
shader फ़ाइल शामिल की है। हम भी ऐसा ही करेंगे।
Resolve Lygia
हमारे प्रोजेक्ट में Lygia Shader library का उपयोग करने के लिए, हमारे पास दो विकल्प हैं:
- लाइब्रेरी की सामग्री को हमारे प्रोजेक्ट में कॉपी करें और जिन फ़ाइलों की आवश्यकता है उन्हें इंपोर्ट करें। (हमने shaders introduction lesson में GLSL फ़ाइलों को कैसे इंपोर्ट करें यह देखा था)
resolve-lygia
नामक लाइब्रेरी का उपयोग करें जो वेब से Lygia Shader library को resolve करेगी और स्वचालित रूप से lygia से संबंधित#include
निर्देशों को फ़ाइलों की सामग्री के साथ बदल देगी।
आपके प्रोजेक्ट पर निर्भर करते हुए, कितने इफ़ेक्ट्स का उपयोग करना है, और यदि आप अन्य shader लाइब्रेरीज़ का उपयोग कर रहे हैं, तो आप किसी एक समाधान को प्राथमिकता दे सकते हैं।
इस पाठ में, हम resolve-lygia
लाइब्रेरी का उपयोग करेंगे। इसे इंस्टॉल करने के लिए, निम्नलिखित कमांड चलाएं:
yarn add resolve-lygia
फिर, इसका उपयोग करने के लिए हमें बस अपने fragment और/या vertex shader कोड को resolveLygia
फ़ंक्शन के साथ लपेटना होगा:
// ... import { resolveLygia } from "resolve-lygia"; export const WaterMaterial = shaderMaterial( { uColor: new Color("skyblue"), uOpacity: 0.8, }, /*glsl*/ ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, resolveLygia(/*glsl*/ ` varying vec2 vUv; uniform vec3 uColor; uniform float uOpacity; void main() { gl_FragColor = vec4(uColor, uOpacity); #include <tonemapping_fragment> #include <encodings_fragment> }`) );
इस पाठ में हमें केवल fragment shader के लिए resolveLygia
फ़ंक्शन का उपयोग करने की आवश्यकता होगी।
अब हम अपने shader में pnoise
फ़ंक्शन का उपयोग कर सकते हैं:
#include "lygia/generative/pnoise.glsl" varying vec2 vUv; uniform vec3 uColor; uniform float uOpacity; void main() { float noise = pnoise(vec3(vUv * 10.0, 1.0), vec3(100.0, 24.0, 112.0)); vec3 black = vec3(0.0); vec3 finalColor = mix(uColor, black, noise); gl_FragColor = vec4(finalColor, uOpacity); #include <tonemapping_fragment> #include <encodings_fragment> }
हमने vUv
को 10.0
से गुणा किया है ताकि noise अधिक स्पष्ट हो सके और हमने noise effect बनाने के लिए pnoise
फ़ंक्शन का उपयोग किया है। हमने noise के मान के आधार पर uColor
को ब्लैक के साथ मिलाया है ताकि अंतिम रंग बनाया जा सके।
हम देख सकते हैं कि noise effect पानी पर लागू हुआ है।
⚠️ Resolve Lygia में कभी-कभी डाउनटाइम समस्याएं होती हैं। यदि आपको कोई समस्या आती है, तो आप Glslify library का उपयोग कर सकते हैं या Lygia Shader library से जिन फ़ाइलों की आपको आवश्यकता है उन्हें कॉपी कर सकते हैं और glsl files का उपयोग कर सकते हैं जैसा हमने shaders introduction lesson में किया था।
फ़ोम इफेक्ट
शोर प्रभाव (noise effect) हमारे फ़ोम प्रभाव का आधार है। प्रभाव को परिष्कृत करने से पहले, आइए उन यूनिफार्म्स को बनाते हैं जिनकी हमें फ़ोम प्रभाव पर पूर्ण नियंत्रण के लिए आवश्यकता होगी।
WaterMaterial.jsx
:
import { shaderMaterial } from "@react-three/drei"; import { resolveLygia } from "resolve-lygia"; import { Color } from "three"; export const WaterMaterial = shaderMaterial( { uColor: new Color("skyblue"), uOpacity: 0.8, uTime: 0, uSpeed: 0.5, uRepeat: 20.0, uNoiseType: 0, uFoam: 0.4, uFoamTop: 0.7, }, /*glsl*/ ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`, resolveLygia(/*glsl*/ ` #include "lygia/generative/pnoise.glsl" varying vec2 vUv; uniform vec3 uColor; uniform float uOpacity; uniform float uTime; uniform float uSpeed; uniform float uRepeat; uniform int uNoiseType; uniform float uFoam; uniform float uFoamTop; void main() { float noise = pnoise(vec3(vUv * 10.0, 1.0), vec3(100.0, 24.0, 112.0)); vec3 black = vec3(0.0); vec3 finalColor = mix(uColor, black, noise); gl_FragColor = vec4(finalColor, uOpacity); #include <tonemapping_fragment> #include <encodings_fragment> }`) );
हमने निम्नलिखित यूनिफार्म्स जोड़ीं:
uTime
: फ़ोम प्रभाव को एनिमेट करने के लिएuSpeed
: प्रभाव एनिमेशन की गति को नियंत्रित करने के लिएuRepeat
: शोर प्रभाव को स्केल करने के लिएuNoiseType
: विभिन्न शोर कार्यों के बीच स्विच करने के लिएuFoam
: जब फ़ोम प्रभाव शुरू होता है उसे नियंत्रित करने के लिएuFoamTop
: वह सीमा जिस पर फ़ोम गाढ़ा हो जाता है उसे नियंत्रित करने के लिए
अब हमें इन यूनिफार्म्स को सामग्री पर लागू करने की आवश्यकता है।
Water.jsx
:
import { useFrame } from "@react-three/fiber"; import { useControls } from "leva"; import { useRef } from "react"; import { Color } from "three"; export const Water = ({ ...props }) => { const waterMaterialRef = useRef(); const { waterColor, waterOpacity, speed, noiseType, foam, foamTop, repeat } = useControls({ waterOpacity: { value: 0.8, min: 0, max: 1 }, waterColor: "#00c3ff", speed: { value: 0.5, min: 0, max: 5 }, repeat: { value: 30, min: 1, max: 100, }, foam: { value: 0.4, min: 0, max: 1, }, foamTop: { value: 0.7, min: 0, max: 1, }, noiseType: { value: 0, options: { Perlin: 0, Voronoi: 1, }, }, }); useFrame(({ clock }) => { if (waterMaterialRef.current) { waterMaterialRef.current.uniforms.uTime.value = clock.getElapsedTime(); } }); return ( <mesh {...props}> <planeGeometry args={[15, 32, 22, 22]} /> <waterMaterial ref={waterMaterialRef} uColor={new Color(waterColor)} transparent uOpacity={waterOpacity} uNoiseType={noiseType} uSpeed={speed} uRepeat={repeat} uFoam={foam} uFoamTop={foamTop} /> </mesh> ); };
अब हम शेडर में फ़ोम लॉजिक बना सकते हैं।
हम uTime
को uSpeed
से गुणा करके अपने समायोजित समय की गणना करते हैं:
float adjustedTime = uTime * uSpeed;
फिर हम pnoise
फ़ंक्शन का उपयोग करके शोर प्रभाव उत्पन्न करते हैं:
float noise = pnoise(vec3(vUv * uRepeat, adjustedTime * 0.5), vec3(100.0, 24.0, 112.0));
हम smoothstep
फ़ंक्शन का उपयोग करके फ़ोम प्रभाव लागू करते हैं:
noise = smoothstep(uFoam, uFoamTop, noise);
फिर, हम फ़ोम का प्रतिनिधित्व करने के लिए चमकीले रंग बनाते हैं। हम एक intermediateColor
और एक topColor
बनाते हैं:
vec3 intermediateColor = uColor * 1.8; vec3 topColor = intermediateColor * 2.0;
हम शोर मान के आधार पर रंग को समायोजित करते हैं:
vec3 finalColor = uColor; finalColor = mix(uColor, intermediateColor, step(0.01, noise)); finalColor = mix(finalColor, topColor, step(1.0, noise));
जब शोर 0.01
और 1.0
के बीच होता है, तो रंग मध्यम रंग होगा। जब शोर 1.0
या उससे अधिक होता है, तो रंग शीर्ष रंग होगा।
यहाँ अंतिम शेडर कोड है:
#include "lygia/generative/pnoise.glsl" varying vec2 vUv; uniform vec3 uColor; uniform float uOpacity; uniform float uTime; uniform float uSpeed; uniform float uRepeat; uniform int uNoiseType; uniform float uFoam; uniform float uFoamTop; void main() { float adjustedTime = uTime * uSpeed; // NOISE GENERATION float noise = pnoise(vec3(vUv * uRepeat, adjustedTime * 0.5), vec3(100.0, 24.0, 112.0)); // FOAM noise = smoothstep(uFoam, uFoamTop, noise); // COLOR vec3 intermediateColor = uColor * 1.8; vec3 topColor = intermediateColor * 2.0; vec3 finalColor = uColor; finalColor = mix(uColor, intermediateColor, step(0.01, noise)); finalColor = mix(finalColor, topColor, step(1.0, noise)); gl_FragColor = vec4(finalColor, uOpacity); #include <tonemapping_fragment> #include <encodings_fragment> }
अब हमारे पास पानी पर एक अच्छा फ़ोम प्रभाव है।
End of lesson preview
To get access to the entire lesson, you need to purchase the course.