Rohes WebGL, ohne Bibliothek
🎛️WebGL-Labor
Eine kleine Szene, komplett selbst gebaut: zwei Shader-Programme, sechs Puffer, eine Textur und ein
requestAnimationFrame-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×38varying 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 UV6uniform vec3 uBaseColor; // linear7uniform vec3 uLightDir; // zur Lichtquelle, normiert8uniform 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;1617vec3 toSRGB(vec3 c) { return pow(clamp(c, 0.0, 1.0), vec3(1.0 / 2.2)); }1819void 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; }2829 float nDotL = max(dot(N, L), 0.0);30 vec3 ambient = 0.08 * base;31 vec3 color;32 if (uShading == 1) { // Lambert33 color = ambient + base * nDotL * uLightIntensity;34 } else if (uShading == 2 || uShading == 3) { // Phong / Blinn-Phong35 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, GGX42 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}