WEBGL : Vertex Shader dans page html

Particles WebGL2 — cours en 10 étapes (présentation)

1) Structure HTML minimale

Une page fond noir avec un unique <canvas> plein écran : c’est la surface de rendu.

<style>
  html,body{margin:0;height:100%;background:#000;overflow:hidden}
  canvas{position:fixed;inset:0;width:100%;height:100%;display:block}
</style>
<canvas id="gl"></canvas>

2) Canvas + DPR (haute résolution)

On utilise le devicePixelRatio (DPR) pour un rendu net et on ajuste le viewport au resize.

const canvas = document.getElementById('gl');
const gl = canvas.getContext('webgl2', {alpha:false});
let DPR=1, W=0, H=0;
function resize(){
  DPR = Math.max(1, Math.min(3, window.devicePixelRatio||1));
  W = Math.floor(innerWidth * DPR);
  H = Math.floor(innerHeight * DPR);
  canvas.width = W; canvas.height = H;
  gl.viewport(0,0,W,H);
}
addEventListener('resize', resize, {passive:true});
resize();

3) Contexte WebGL2 + shaders minimaux

Smoke test : on compile un vertex/fragment tout simple et on dessine 1 point au centre.

// Vertex (NDC au centre)
const vs = `#version 300 es
precision highp float;
void main(){
  gl_PointSize = 8.0;
  gl_Position = vec4(0.0,0.0,0.0,1.0);
}`;
const fs = `#version 300 es
precision mediump float;
out vec4 outColor;
void main(){ outColor = vec4(1.0); }`;
// (compile + link) → gl.useProgram(program); gl.drawArrays(gl.POINTS, 0, 1);

4) Grille via gl_VertexID (sans VBO)

Sans buffer : chaque sommet déduit sa position dans une grille across×across en NDC.

uniform int uAcross;
void main(){
  int id = gl_VertexID;
  int x = int(mod(float(id), float(uAcross)));
  int y = id / uAcross;
  float u = float(x)/float(uAcross-1);
  float v = float(y)/float(uAcross-1);
  vec2 p = vec2(u*2.0-1.0, v*2.0-1.0);
  gl_PointSize = 6.0;
  gl_Position = vec4(p,0.0,1.0);
}

5) Taille selon DPR + uBaseSize

Taille en pixels “device” avec uDPR et uBaseSize. Légèrement plus gros en bas (mix sur v).

uniform float uDPR, uBaseSize; // px
float base = mix(1.0, 1.6, 1.0 - v); // plus gros en bas
gl_PointSize = uBaseSize * base * uDPR;

6) Ondes sinusoïdales

Deux offsets sin/cos dépendants du temps cassent la rigidité et animent la grille.

uniform float uTime;
float offx = sin(uTime*0.8 + float(y)*0.21) * 0.08;
float offy = cos(uTime*0.6 + float(x)*0.17) * 0.10;
p += vec2(offx, offy);

7) Value-noise → champ de flux

Un value-noise 3D (x,y,t) fournit un angle et donc une direction de flux à ajouter.

float valueNoise(vec3 p){ /* trilinear value-noise */ }
uniform float uFlowScale, uFlowStr;
float ang = valueNoise(vec3(p*uFlowScale*2.0, uTime*0.15)) * 6.28318530718;
vec2 flow = vec2(cos(ang), sin(ang)) * uFlowStr;
p += flow;

8) Montée + traînées (voile noir)

La fumée monte et reboucle verticalement. Les traînées viennent d’un voile noir alpha rendu avant les points.

uniform float uRise;
p.y += (uTime * uRise);
if (p.y > 1.2) p.y -= 2.4;

// Pass "voile" (fragment)
uniform float uFade; // 0.06–0.1
out vec4 outColor;
outColor = vec4(0.0,0.0,0.0, uFade);

9) Halo doux + filaire (F) + couleurs HSV

Remplace le disque plein par un halo radial (gl_PointCoord). Option anneau + teintes HSV animées.

in vec4 vColor; in float vAlpha; uniform bool uWireframe;
vec2 uv = gl_PointCoord - 0.5; float r = length(uv)*2.0;
float soft = smoothstep(1.0, 0.0, r);
float ring = smoothstep(1.0, 0.90, r) * smoothstep(0.75, 0.9, r);
float a = (uWireframe ? ring : soft) * vAlpha;
if (a < 0.01) discard;
outColor = vec4(vColor.rgb, a);

10) Souffle souris + burst (B) + additif

Poussée radiale autour de la souris (NDC). Burst = impulsion courte. Blend additif pour l’effet glow.

uniform vec2 uMouseNDC; uniform float uMousePush, uBurst;
vec2 d = p - uMouseNDC; float r = length(d);
float push = smoothstep(0.7, 0.0, r) * uMousePush;
if (r > 0.0001) p += normalize(d) * 0.35 * push;
// dessin : gl.blendFunc(gl.SRC_ALPHA, gl.ONE); // additif


Code final complet:

Commentaires

Posts les plus consultés de ce blog

Mario Kart 2D

Animation javascript dans page web