Buat animasi perpindahan bola menggunakan material Three.js khusus

 – Beragampengetahuan
11 mins read

Buat animasi perpindahan bola menggunakan material Three.js khusus – Beragampengetahuan

Dalam tutorial ini, kita akan mengeksplorasi kemungkinan membuat material khusus menggunakan shader berdasarkan material bawaan Three.js dan React Three Fiber.

Contents

Ringkasan

Kita akan belajar:

  • Cara menghubungkan ke materi Three.js bawaan untuk memodifikasinya sesuai keinginan kita
  • Cara memindahkan simpul jaring untuk mendapatkan bentuk yang menakjubkan
  • Bagaimana membuat lampu menghormati bentuk baru ini
  • Sebagai bonus, kita akan mempelajari cara memperbaiki bayangan setelah perpindahan geometri

Tutorial ini membutuhkan:

  • pengetahuan dasar reaksitermasuk komponen, status, dan props.
  • pemahaman dasar Tiga.jstermasuk konsep dasar rendering 3D, geometri, dan material.

siap-siap? Ayo mulai!

Langkah 1: Pengaturan Dasar

Pertama, kita perlu menyiapkan proyek menggunakan R3F dan membuat adegan dasar dengan bola.

import React, { Suspense } from 'react';
import { OrbitControls } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';

const Experiment = () => {
    return (
        <>
            <mesh>
                <icosahedronGeometry args={[1.3, 200]} />
                <meshPhysicalMaterial
                    roughness={0.56}
		            metalness={0.76}
		            clearcoat={0}
		            ior={2.81}
		            iridescence={0.96}
                />
            </mesh>
            <ambientLight />
            <directionalLight intensity={5} position={[-2, 2, 3.5]} />
        </>
    );
};

const Experience = () => {
    return (
        <div className="canvas-wrapper">
            <Canvas
                camera={{
                    position: [0, 0, 5],
                    fov: 45,
                    near: 0.1,
                    far: 1000,
                }}
                gl={{ alpha: false }}
            >
                <Experiment />
                <OrbitControls />
            </Canvas>
        </div>
    );
};

export default Experience;

Dalam kode ini:

  • Kami menyiapkan komponen React dasar Experiment Buat bola menggunakan icosahedronGeometry dan bahan fisik dengan sifat tertentu.
  • Kami menambahkan pencahayaan sekitar dan terarah pada pemandangan.
  • ini Experience Pengaturan komponen Canvas Dilengkapi dengan kamera dan kontrol trek untuk berinteraksi.

Tidak ada yang mewah di sini, kami hanya membuat kanvas dasar, mengatur pencahayaan, dan menambahkan bola ke pemandangan. Ini akan menjadi dasar untuk modifikasi kami selanjutnya.

Langkah 2: Refaktorisasi untuk digunakan three-custom-shader-material

MeshPhysicalMaterial Ini adalah materi bawaan tercanggih di Three.js. Bagaimana cara kita memodifikasi shader suatu material tanpa kehilangan fungsionalitas yang sudah ada?

Secara tradisional ini dilakukan dengan menggunakan bahan onBeforeCompile metode. Metode ini mengambil shader saat ini sebagai string, yang dapat kita ganti dengan kode kita. Ini terlihat seperti ini:

material.onBeforeCompile = (shader) => {
    shader.uniforms = {
        ...shader.uniforms,
        ...uniforms,
    };

    shader.vertexShader = shader.vertexShader
        .replace(
            '#include <common>',
            `
            #include <common>
            // your code
            `,
        )
        .replace(
            '#include <project_vertex>',
            `
            #include <project_vertex>
            // your code
            `,
        );
};

Namun, pendekatan ini bisa jadi merepotkan dan tidak dapat dibaca. Sebagai gantinya kita akan menggunakan perpustakaan yang luar biasa THREE-CustomShaderMaterial.

import React, { Suspense, useRef } from 'react';
import { OrbitControls } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import CustomShaderMaterial from 'three-custom-shader-material';
import { MeshPhysicalMaterial } from 'three';

const Experiment = () => {
    const materialRef = useRef(null);

    return (
        <>
            <mesh>
                <icosahedronGeometry args={[1.3, 200]} />
                <CustomShaderMaterial
                    ref={materialRef}
                    silent
                    baseMaterial={MeshPhysicalMaterial}
                    roughness={0.56}
		            metalness={0.76}
		            clearcoat={0}
		            ior={2.81}
		            iridescence={0.96}
                />
            </mesh>
            <ambientLight />
            <directionalLight intensity={5} position={[-2, 2, 3.5]} />
        </>
    );
};

...

Hasilnya sama, tapi sekarang kita bisa menggunakan perpustakaan untuk menulis ulang bagian shader. buat kosong vertex.glsl Dan fragment.glsl dokumen:

// vertex.glsl

void main {
	
}
// fragment.glsl

void main {
	
}

catatan: Kami tidak memberikan nilai pada variabel gl_Position Dan gl_FragColor secara langsung. Sebagai gantinya, kami menetapkan nilai ke variabel yang diharapkan perpustakaan (di bagian selanjutnya dalam tutorial). Daftar lengkap variabel-variabel ini dapat ditemukan di sini.

Langkah 3: Pindahkan bentuk di blok vertex shader

Tugas kita adalah memodifikasi posisi titik menurut pola tertentu, memindahkannya ke arah normal dari pusat. Kami menggunakan shader fragmen untuk memvisualisasikan pola ini. Karena hasilnya perlu digunakan di vertex shader, kami menulis kode di sana dan meneruskan hasilnya melalui varying untuk visualisasi.

Apa yang ingin kami lakukan:

  • Buat animasi pola meningkat tanpa batas
  • Warp menggunakan fungsi noise untuk memberikan tampilan organik dan acak
  • Pecahan yang membuat pola ini
  • Dorong simpul ke luar menggunakan normal

Mari kita mulai dengan menampilkan pola di shader fragmen:

// vertex.glsl

uniform float uTime;

varying float vPattern;

//	Classic Perlin 3D Noise 
//	by Stefan Gustavson (
//
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}

float cnoise(vec3 P){
  vec3 Pi0 = floor(P); // Integer part for indexing
  vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
  Pi0 = mod(Pi0, 289.0);
  Pi1 = mod(Pi1, 289.0);
  vec3 Pf0 = fract(P); // Fractional part for interpolation
  vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
  vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
  vec4 iy = vec4(Pi0.yy, Pi1.yy);
  vec4 iz0 = Pi0.zzzz;
  vec4 iz1 = Pi1.zzzz;

  vec4 ixy = permute(permute(ix) + iy);
  vec4 ixy0 = permute(ixy + iz0);
  vec4 ixy1 = permute(ixy + iz1);

  vec4 gx0 = ixy0 / 7.0;
  vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
  gx0 = fract(gx0);
  vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
  vec4 sz0 = step(gz0, vec4(0.0));
  gx0 -= sz0 * (step(0.0, gx0) - 0.5);
  gy0 -= sz0 * (step(0.0, gy0) - 0.5);

  vec4 gx1 = ixy1 / 7.0;
  vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
  gx1 = fract(gx1);
  vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
  vec4 sz1 = step(gz1, vec4(0.0));
  gx1 -= sz1 * (step(0.0, gx1) - 0.5);
  gy1 -= sz1 * (step(0.0, gy1) - 0.5);

  vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
  vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
  vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
  vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
  vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
  vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
  vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
  vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);

  vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
  g000 *= norm0.x;
  g010 *= norm0.y;
  g100 *= norm0.z;
  g110 *= norm0.w;
  vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
  g001 *= norm1.x;
  g011 *= norm1.y;
  g101 *= norm1.z;
  g111 *= norm1.w;

  float n000 = dot(g000, Pf0);
  float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
  float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
  float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
  float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
  float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
  float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
  float n111 = dot(g111, Pf1);

  vec3 fade_xyz = fade(Pf0);
  vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
  vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
  float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 
  return 2.2 * n_xyz;
}

// It's like mod() function, but "smooth" (thank you, captain!), with no immediate jump to 0
float smoothMod(float axis, float amp, float rad) {
    float top = cos(PI * (axis / amp)) * sin(PI * (axis / amp));
    float bottom = pow(sin(PI * (axis / amp)), 2.0) + pow(rad, 2.0);
    float at = atan(top / bottom);
    return amp * (1.0 / 2.0) - (1.0 / PI) * at;
}

// We gonna use this function multiple times
float getDisplacement(vec3 position) {
		// gonna be uniforms later on
		float uFractAmount = 4.;
		float uDisplacementStrength = 0.57;
		float uSpeed = 1.1;

    vec3 pos = position;
    pos.y -= uTime * 0.05 * uSpeed; // pattern changes in time, going up
    pos += cnoise(pos * 1.65) * uNoiseStrength; // position distortion with noise

    return smoothMod(pos.y * uFractAmount, 1., 1.5) * uDisplacementStrength;
}

void main() {
    float pattern = getDisplacement(position);
    vPattern = pattern; // pass the result to the fragment shader
}
// fragment.glsl

varying float vPattern;

void main() {
    vec3 color = vec3(vPattern);

    csm_FragColor = vec4(color, 1.); // Using `csm_FragColor` removes all the shading. Use this only for debugging.
}

Semakin terang warnanya, semakin besar perpindahan posisi puncak titik tersebut.

Selanjutnya, kita perlu merefleksikan pola kita di vertex shader. Kita perlu mengubah arah pendirian kita. Vektor normal yang normal pada permukaan sempurna untuk tujuan ini. Dengan menggunakan normal, kita mendorong posisi titik ke luar:

// vertex.glsl

...

void main() {
    float pattern = getDisplacement(position);
    vPattern = pattern;
  
    csm_Position += normal * pattern; // move position according to normal
}

Mari tambahkan warna:

// Experience.jsx

...

const uniforms = {
    uTime: { value: 0 },
	uColor: { value: new Color('#af00ff') },
};
    
...
// fragment.glsl

varying float vPattern;

uniform vec3 uColor;

void main() {
    vec3 color = vPattern * uColor;

    csm_DiffuseColor = vec4(color, 1.); // Restore shading using `csm_DiffuseColor`
}

kelihatan bagus! Tapi ada masalah. Bayangannya sepertinya tidak benar; ada sesuatu yang salah. Masalahnya adalah kita mengubah tampilan geometrinya, namun normalnya tetap berperilaku seperti bola. Kita perlu menghitung ulang.

Langkah Empat: Perbaiki Bayangan

Apa yang harus kita lakukan? Ada yang disebut teknologi “tetangga”. Kita perlu mencari dua buah vektor yang berhadapan dengan vektor-vektor yang berdekatan dan saling tegak lurus. Ini disebut garis singgung dan bitangen.

biarkan kami console.log ini geometry.attributes:

Tidak ada properti Tangent secara default. Kabar baiknya adalah Three.js dapat menghitung garis singgung untuk kita, lalu kita dapat menghitung bitangennya. ada satu computeTangent() Metode untuk semua geometri yang diwarisi dari BufferGeometryjadi mari kita gunakan. Catatan singkat: Menurut dokumentasi, penghitungan hanya mendukung geometri yang diindeks dan jika posisi, normal, dan uv ditentukan. Kami memiliki yang terakhir, namun geometri kami belum diindeks. Kita perlu menyelesaikan masalah ini. Three.js menyediakan serangkaian utilitas, salah satunya adalah mergeVertices Fungsi yang mengubah geometri yang tidak diindeks menjadi geometri yang diindeks. Harap diperhatikan: Menggabungkan simpul memerlukan biaya komputasi yang mahal dan mungkin memerlukan waktu.

Mari kita gunakan lalu panggil “computeTangents()”:

...
import { MeshPhysicalMaterial, Color, IcosahedronGeometry } from 'three';
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils';

...

// Refactor to use regular Three.js code instead of R3F primitive for convenience
const geometry = useMemo(() => {
    const geometry = mergeVertices(new IcosahedronGeometry(1.3, 200));
    geometry.computeTangents();

    return geometry;
}, []);

...

return (
    <>
        <mesh geometry={geometry}>
            <CustomShaderMaterial
                ref={materialRef}
                silent
                baseMaterial={MeshPhysicalMaterial}
                roughness={0.56}
		        metalness={0.76}
		        clearcoat={0}
		        ior={2.81}
		        iridescence={0.96}
            />
        </mesh>
        ...
    <>
);

Sekarang catat sifat geometrinya:

Hebat, kita punya garis singgung! Sekarang kita siap menghitung normal baru.

// vertex.glsl

attribute vec4 tangent; // extract tangent

uniform float uTime;

varying float vPattern;

...

void main() {
    float pattern = getDisplacement(position); // base displacement pattern
    vPattern = pattern; // pass the result to the fragment shader
    
    vec3 biTangent = cross(normal, tangent.xyz);
    float shift = 0.01; // approximate distance to the neighbour
    vec3 posA = position + tangent.xyz * shift; // position of neighbour A
    vec3 posB = position + biTangent * shift; // position of neighbour B

    csm_Position += normal * pattern;
    posA += normal * getDisplacement(posA); // applying displacement to positionA
    posB += normal * getDisplacement(posB); // applying displacement to positionB
		
    // To calculate direction between two vectors,
	// we should subtract the destination from the origin,
	// then normalize it
	// Make sure to use `csm_Position` instead of plain `position`

    vec3 toA = normalize(posA - csm_Position);
    vec3 toB = normalize(posB - csm_Position);

    csm_Normal = normalize(cross(toA, toB)); // recalculated normal
}

Dilakukan! Anda bisa mendapatkan hasil akhirnya di github.

Hadiah: Perbaiki Bayangan

Dengan mengaktifkan bayangan, Anda akan melihat bahwa bayangan yang dihasilkan oleh jaring tidak memperhitungkan perpindahan geometri. Itu masih bentuk dasar bola.

Kita perlu menggunakan material kedalaman khusus yang menghormati perubahan geometri kita. Mari kita selesaikan masalah ini:

<mesh geometry={geometry}>
	<CustomShaderMaterial
		ref={materialRef}
		silent
		baseMaterial={MeshPhysicalMaterial}
		vertexShader={vertexShader}
		fragmentShader={fragmentShader}
		uniforms={uniforms}
		roughness={0.56}
		metalness={0.76}
		clearcoat={0}
		ior={2.81}
		iridescence={0.96}
	/>
	{/* Custom depth material, reusing vertex shader and uniforms */}
	<CustomShaderMaterial
	    ref={depthMaterialRef}
	    baseMaterial={MeshDepthMaterial}
	    vertexShader={vertexShader}
	    uniforms={uniforms}
	    silent
	    depthPacking={RGBADepthPacking}
	    attach="customDepthMaterial"
	/>
</mesh>

Itu dia. Saya telah menambahkan kontrol sehingga Anda dapat menikmati demo ini. menikmati!

Ulasan situs web inspiratif #62

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

#Buat #animasi #perpindahan #bola #menggunakan #material #Three.js #khusus

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *