Rohes WebGL, ohne Bibliothek

🎛️WebGL-Labor

Eine kleine Szene, komplett selbst gebaut: zwei Shader-Programme, sechs Puffer, eine Textur und einrequestAnimationFrame-Loop – rund 150 Zeilen, die three.js sonst für uns erledigt. Die Matrizen kommen aus derselben Bibliothek wie im Matrizenrechner.
Geometrie
Material / Shading
Licht (gerichtet)
Kamera
Darstellung & Pipeline

Probier: Tiefentest und Culling aus beim Torus (hintere Flächen übermalen vordere) · Orthografisch einschalten und den Abstand ändern (die Größe bleibt gleich) · PBR mit metallic 1 und roughness 0,1 · Textur an und FOV 120°.

Vertex-Shader
1attribute vec3 aPosition;
2attribute vec3 aNormal;
3attribute vec2 aUv;
4uniform mat4 uModel;
5uniform mat4 uView;
6uniform mat4 uProjection;
7uniform mat3 uNormalMatrix; // (M⁻¹)ᵀ, oberes 3×3
8varying vec3 vNormal;
9varying vec3 vWorldPos;
10varying vec2 vUv;
11void main() {
12 vec4 world = uModel * vec4(aPosition, 1.0);
13 vWorldPos = world.xyz;
14 vNormal = uNormalMatrix * aNormal;
15 vUv = aUv;
16 gl_Position = uProjection * uView * world;
17}
Fragment-Shader
1precision highp float;
2varying vec3 vNormal;
3varying vec3 vWorldPos;
4varying vec2 vUv;
5uniform int uShading; // 0 unbeleuchtet, 1 Lambert, 2 Phong, 3 Blinn-Phong, 4 PBR, 5 Normalen, 6 UV
6uniform vec3 uBaseColor; // linear
7uniform vec3 uLightDir; // zur Lichtquelle, normiert
8uniform float uLightIntensity;
9uniform vec3 uCameraPos;
10uniform float uShininess;
11uniform float uRoughness;
12uniform float uMetallic;
13uniform bool uUseTexture;
14uniform sampler2D uTexture;
15const float PI = 3.14159265;
16
17vec3 toSRGB(vec3 c) { return pow(clamp(c, 0.0, 1.0), vec3(1.0 / 2.2)); }
18
19void main() {
20 vec3 N = normalize(vNormal);
21 vec3 L = normalize(uLightDir);
22 vec3 V = normalize(uCameraPos - vWorldPos);
23 vec3 base = uBaseColor;
24 if (uUseTexture) base *= pow(texture2D(uTexture, vUv).rgb, vec3(2.2));
25 if (uShading == 5) { gl_FragColor = vec4(N * 0.5 + 0.5, 1.0); return; }
26 if (uShading == 6) { gl_FragColor = vec4(vUv, 0.0, 1.0); return; }
27 if (uShading == 0) { gl_FragColor = vec4(toSRGB(base), 1.0); return; }
28
29 float nDotL = max(dot(N, L), 0.0);
30 vec3 ambient = 0.08 * base;
31 vec3 color;
32 if (uShading == 1) { // Lambert
33 color = ambient + base * nDotL * uLightIntensity;
34 } else if (uShading == 2 || uShading == 3) { // Phong / Blinn-Phong
35 float spec = 0.0;
36 if (nDotL > 0.0) {
37 if (uShading == 2) spec = pow(max(dot(reflect(-L, N), V), 0.0), uShininess);
38 else spec = pow(max(dot(N, normalize(L + V)), 0.0), uShininess);
39 }
40 color = ambient + (base * nDotL + vec3(0.6) * spec) * uLightIntensity;
41 } else { // PBR: Cook-Torrance, GGX
42 vec3 H = normalize(L + V);
43 float r = max(uRoughness, 0.04);
44 float a = r * r;
45 float nDotV = max(dot(N, V), 1e-4);
46 float nDotH = max(dot(N, H), 0.0);
47 float d = nDotH * nDotH * (a * a - 1.0) + 1.0;
48 float D = a * a / (PI * d * d);
49 float k = (r + 1.0) * (r + 1.0) / 8.0;
50 float G = (nDotV / (nDotV * (1.0 - k) + k)) * (max(nDotL, 1e-4) / (max(nDotL, 1e-4) * (1.0 - k) + k));
51 vec3 F0 = mix(vec3(0.04), base, uMetallic);
52 vec3 F = F0 + (1.0 - F0) * pow(1.0 - max(dot(V, H), 0.0), 5.0);
53 vec3 spec = D * G * F / (4.0 * nDotV * max(nDotL, 1e-4));
54 vec3 kd = (1.0 - F) * (1.0 - uMetallic);
55 color = ambient + (kd * base / PI + spec) * nDotL * PI * uLightIntensity;
56 }
57 gl_FragColor = vec4(toSRGB(color), 1.0);
58}