Cara menulis efek tampilan berbasis shader menggunakan React Three Fiber dan GLSL – Beragampengetahuan
Setelah melihat berbagai tampilan gambar yang dibuat oleh beberapa rekan saya di X, saya memutuskan untuk mencobanya dan membuatnya sendiri. Idenya adalah untuk mempraktikkan teknik R3F dan shader sambil membuat sesuatu yang dapat dengan mudah digunakan kembali di proyek lain.
Catatan: Anda dapat menemukan kode untuk semua langkah sebagai fork di repositori Github berikut.
Memulai Proyek
Proyek dasarnya adalah aplikasi ViteJS React sederhana dengan R3F Canvas dan paket-paket berikut diinstal:
three // ThreeJS & R3F packages
@react-three/fiber
@react-three/drei
motion // Previously FramerMotion
leva // To add tweaks to our shader
vite-plugin-glsl // For vite to work with .glsl files
Sekarang kita sudah siap, kita bisa mulai menulis shader pertama kita.
Buat shader gambar sederhana
Pertama, kita akan membuat vertex.glsl & fragment.glsl Dalam dua file terpisah tampilannya seperti ini:
// vertex.glsl
varying vec2 vUv;
void main()
{
// FINAL POSITION
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
// VARYINGS
vUv = uv;
}
Dan milik kita fragment.glsl Ini terlihat seperti ini:
uniform sampler2D uTexture;
varying vec2 vUv;
void main()
{
// Apply texture
vec3 textureColor = texture2D(uTexture, vUv).rgb;
// FINAL COLOR
gl_FragColor = vec4(textureColor, 1.0);
}
Di sini kita meneruskan UV dari mesh ke shader fragmen dan masuk texture2D Fungsi menerapkan tekstur gambar ke fragmen kita.
Sekarang setelah file shader dibuat, kita dapat membuat komponen utama:
import { shaderMaterial, useAspect, useTexture } from "@react-three/drei";
import { extend } from "@react-three/fiber";
import { useRef } from "react";
import * as THREE from "three";
import imageRevealFragmentShader from "../shaders/imageReveal/fragment.glsl";
import imageRevealVertexShader from "../shaders/imageReveal/vertex.glsl";
const ImageRevealMaterial = shaderMaterial(
{
uTexture: new THREE.Texture(),
},
imageRevealVertexShader,
imageRevealFragmentShader,
(self) => {
self.transparent = true;
}
);
extend({ ImageRevealMaterial });
const RevealImage = ({ imageTexture }) => {
const materialRef = useRef();
// LOADING TEXTURE & HANDLING ASPECT RATIO
const texture = useTexture(imageTexture, (loadedTexture) => {
if (materialRef.current) {
materialRef.current.uTexture = loadedTexture;
}
});
const { width, height } = texture.image;
const scale = useAspect(width, height, 0.25);
return (
<mesh scale={scale}>
<planeGeometry args={[1, 1, 32, 32]} />
<imageRevealMaterial attach="material" ref={materialRef} />
</mesh>
);
};
export default RevealImage;
Disini kita membuat bahan dasar menggunakan shaderMaterial Berasal dari React Three Drei dan kemudian mengembangkannya menggunakan R3F untuk digunakan di komponen kami.
Kami kemudian memuat gambar yang diteruskan sebagai penyangga dan menangani skalanya berkat useAspect Kait dari React-Three/Drei.
Kita harus mendapatkan sesuatu seperti ini:

Tambahkan efek dasar
(Disebutkan secara khusus kepada Bruno Simon karena telah menjadi inspirasinya).
Sekarang kita perlu menambahkan efek noise radial yang akan kita gunakan untuk menampilkan gambar kita, untuk melakukan ini kita akan menggunakan fungsi noise Perlin dan memadukannya dengan gradien radial seperti ini:
// fragment.glsl
uniform sampler2D uTexture;
uniform float uTime;
varying vec2 vUv;
#include ../includes/perlin3dNoise.glsl
void main()
{
// Displace the UV
vec2 displacedUv = vUv + cnoise(vec3(vUv * 5.0, uTime * 0.1));
// Perlin noise
float strength = cnoise(vec3(displacedUv * 5.0, uTime * 0.2 ));
// Radial gradient
float radialGradient = distance(vUv, vec2(0.5)) * 12.5 - 7.0;
strength += radialGradient;
// Clamp the value from 0 to 1 & invert it
strength = clamp(strength, 0.0, 1.0);
strength = 1.0 - strength;
// Apply texture
vec3 textureColor = texture2D(uTexture, vUv).rgb;
// FINAL COLOR
// gl_FragColor = vec4(textureColor, 1.0);
gl_FragColor = vec4(vec3(strength), 1.0);
}
Anda dapat menemukan fungsi kebisingan Perlin di sini atau di repositori kode di sini.
ini uTime Digunakan untuk mengubah bentuk kebisingan pada waktunya agar terasa lebih hidup.
Sekarang kita hanya perlu sedikit memodifikasi komponen kita untuk meluangkan waktu pada materi kita:
const ImageRevealMaterial = shaderMaterial(
{
uTexture: new THREE.Texture(),
uTime: 0,
},
...
);
// Inside of the component
useFrame(({ clock }) => {
if (materialRef.current) {
materialRef.current.uTime = clock.elapsedTime;
}
});
ini useFrame Hook R3F berjalan pada setiap frame dan memberi kita jam yang dapat kita gunakan untuk mendapatkan waktu yang telah berlalu sejak adegan tersebut dirender.
Inilah yang kami dapatkan sekarang:



Anda mungkin bisa melihatnya datang, tapi kita akan menggunakannya pada saluran Alpha dan kemudian mengurangi atau menambah radius gradien radial untuk menampilkan/menyembunyikan gambar.
Anda dapat mencobanya sendiri dengan menambahkan gambar ke saluran RGB warna akhir di fragment shader, dan menambahkan intensitas ke saluran Alpha. Anda harus mendapatkan sesuatu seperti ini:
Sekarang, bagaimana kita menganimasikan radius efeknya.
Efek animasi
Caranya, sebenarnya sangat mudah, kita tinggal menambahkan seragam baru saja uProgress Dalam shader fragmen kita, ini akan berubah dari 0 menjadi 1 dan menggunakannya untuk memengaruhi radius:
// fragment.glsl
uniform float uProgress;
...
// Radial gradient
float radialGradient = distance(vUv, vec2(0.5)) * 12.5 - 7.0 * uProgress;
...
// Opacity animation
float opacityProgress = smoothstep(0.0, 0.7, uProgress);
// FINAL COLOR
gl_FragColor = vec4(textureColor, strength * opacityProgress);
Kami juga menggunakan perkembangan untuk menambahkan sedikit animasi opacity di awal efek untuk menyembunyikan gambar kami sepenuhnya di awal.
Sekarang kita dapat meneruskan seragam baru ke material kita dan menggunakan Leva untuk mengontrol kemajuan efeknya:
const ImageRevealMaterial = shaderMaterial(
{
uTexture: new THREE.Texture(),
uTime: 0,
uProgress: 0,
},
...
);
...
// LEVA TO CONTROL REVEAL PROGRESS
const { revealProgress } = useControls({
revealProgress: { value: 0, min: 0, max: 1 },
});
// UPDATING UNIFORMS
useFrame(({ clock }) => {
if (materialRef.current) {
materialRef.current.uTime = clock.elapsedTime;
materialRef.current.uProgress = revealProgress;
}
});
Sekarang Anda harus memiliki sesuatu seperti ini:
Ada banyak cara berbeda yang bisa kita lakukan untuk maju. Untuk mempermudahnya, kami akan membuat tombol di aplikasi yang akan revealProgress Penggunaan prop komponen kami motion/react (sebelumnya Framer Motion):
// App.jsx
// REVEAL PROGRESS ANIMATION
const [isRevealed, setIsRevealed] = useState(false);
const revealProgress = useMotionValue(0);
const handleReveal = () => {
animate(revealProgress, isRevealed ? 0 : 1, {
duration: 1.5,
ease: "easeInOut",
});
setIsRevealed(!isRevealed);
};
...
<Canvas>
<RevealImage
imageTexture="./img/texture.webp"
revealProgress={revealProgress}
/>
</Canvas>
<button
onClick={handleReveal}
className="yourstyle"
>
SHOW/HIDE
</button>
Kami menggunakan MotionValue dari Motion/React dan menyebarkannya ke props komponen kami.
Maka kita hanya perlu melakukannya useFrame Kait seperti ini:
// UPDATING UNIFORMS
useFrame(({ clock }) => {
if (materialRef.current) {
materialRef.current.uTime = clock.elapsedTime;
materialRef.current.uProgress = revealProgress.get();
}
});
Anda harus mendapatkan sesuatu seperti ini:
Tambahkan perpindahan
Hal lain yang ingin saya lakukan untuk menambahkan lebih banyak “kehidupan” pada efeknya adalah memindahkan simpul, menciptakan gelombang yang sinkron dengan kemajuan efeknya. Ini sebenarnya sangat sederhana, karena kita hanya perlu sedikit memodifikasi vertex shader kita:
uniform float uProgress;
varying vec2 vUv;
void main()
{
vec3 newPosition = position;
// Calculate the distance to the center of our plane
float distanceToCenter = distance(vec2(0.5), uv);
// Wave effect
float wave = (1.0 - uProgress) * sin(distanceToCenter * 20.0 - uProgress * 5.0);
// Apply the wave effect to the position Z
newPosition.z += wave;
// FINAL POSITION
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
// VARYINGS
vUv = uv;
}
Di sini, intensitas dan posisi gelombang bergantung uProgress seragam.
Kita harus mendapatkan sesuatu seperti ini:
Ini cukup halus, tapi detail inilah yang membuat perbedaan menurut saya.
Melangkah lebih jauh
Itu di sini! Anda siap untuk pamer! Saya harap Anda bersenang-senang saat membuat efek ini. Sekarang Anda dapat mencoba segala macam hal dengannya untuk menjadikannya lebih baik dan melatih keterampilan shader Anda. Misalnya, Anda dapat mencoba menggunakan Leva untuk menambahkan lebih banyak penyesuaian guna mempersonalisasikannya sesuai keinginan Anda. Anda juga dapat mencoba menganimasikan pengguliran, membuat pesawat berputar, dan banyak lagi.
Saya memberikan contoh kecil tentang apa yang dapat Anda lakukan dengannya, yang dapat Anda temukan di akun Twitter saya.
Terima kasih telah membaca! 🙂
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Cara #menulis #efek #tampilan #berbasis #shader #menggunakan #React #Fiber #dan #GLSL