# WebGL and 3D ## WebGL Mode Setup ```javascript function setup() { createCanvas(1920, 1080, WEBGL); // Origin is CENTER, not top-left // Y-axis points UP (opposite of 2D mode) // Z-axis points toward viewer } ``` ### Coordinate Conversion (WEBGL to P2D-like) ```javascript function draw() { translate(-width/2, -height/2); // shift origin to top-left // Now coordinates work like P2D } ``` ## 3D Primitives ```javascript box(w, h, d); // rectangular prism sphere(radius, detailX, detailY); cylinder(radius, height, detailX, detailY); cone(radius, height, detailX, detailY); torus(radius, tubeRadius, detailX, detailY); plane(width, height); // flat rectangle ellipsoid(rx, ry, rz); // stretched sphere ``` ### 3D Transforms ```javascript push(); translate(x, y, z); rotateX(angleX); rotateY(angleY); rotateZ(angleZ); scale(s); box(100); pop(); ``` ## Camera ### Default Camera ```javascript camera( eyeX, eyeY, eyeZ, // camera position centerX, centerY, centerZ, // look-at target upX, upY, upZ // up direction ); // Default: camera(0, 0, (height/2)/tan(PI/6), 0, 0, 0, 0, 1, 0) ``` ### Orbit Control ```javascript function draw() { orbitControl(); // mouse drag to rotate, scroll to zoom box(200); } ``` ### createCamera ```javascript let cam; function setup() { createCanvas(800, 800, WEBGL); cam = createCamera(); cam.setPosition(300, -200, 500); cam.lookAt(0, 0, 0); } // Camera methods cam.setPosition(x, y, z); cam.lookAt(x, y, z); cam.move(dx, dy, dz); // relative to camera orientation cam.pan(angle); // horizontal rotation cam.tilt(angle); // vertical rotation cam.roll(angle); // z-axis rotation cam.slerp(otherCam, t); // smooth interpolation between cameras ``` ### Perspective and Orthographic ```javascript // Perspective (default) perspective(fov, aspect, near, far); // fov: field of view in radians (PI/3 default) // aspect: width/height // near/far: clipping planes // Orthographic (no depth foreshortening) ortho(-width/2, width/2, -height/2, height/2, 0, 2000); ``` ## Lighting ```javascript // Ambient (uniform, no direction) ambientLight(50, 50, 50); // dim fill light // Directional (parallel rays, like sun) directionalLight(255, 255, 255, 0, -1, 0); // color + direction // Point (radiates from position) pointLight(255, 200, 150, 200, -300, 400); // color + position // Spot (cone from position toward target) spotLight(255, 255, 255, // color 0, -300, 300, // position 0, 1, -1, // direction PI / 4, 5); // angle, concentration // Image-based lighting imageLight(myHDRI); // No lights (flat shading) noLights(); // Quick default lighting lights(); ``` ### Three-Point Lighting Setup ```javascript function setupLighting() { ambientLight(30, 30, 40); // dim blue fill // Key light (main, warm) directionalLight(255, 240, 220, -1, -1, -1); // Fill light (softer, cooler, opposite side) directionalLight(80, 100, 140, 1, -0.5, -1); // Rim light (behind subject, for edge definition) pointLight(200, 200, 255, 0, -200, -400); } ``` ## Materials ```javascript // Normal material (debug — colors from surface normals) normalMaterial(); // Ambient (responds only to ambientLight) ambientMaterial(200, 100, 100); // Emissive (self-lit, no shadows) emissiveMaterial(255, 0, 100); // Specular (shiny reflections) specularMaterial(255); shininess(50); // 1-200 (higher = tighter highlight) metalness(100); // 0-200 (metallic reflection) // Fill works too (no lighting response) fill(255, 0, 0); ``` ### Texture ```javascript let img; function preload() { img = loadImage('texture.jpg'); } function draw() { texture(img); textureMode(NORMAL); // UV coords 0-1 // textureMode(IMAGE); // UV coords in pixels textureWrap(REPEAT); // or CLAMP, MIRROR box(200); } ``` ## Custom Geometry ### buildGeometry ```javascript let myShape; function setup() { createCanvas(800, 800, WEBGL); myShape = buildGeometry(() => { for (let i = 0; i < 50; i++) { push(); translate(random(-200, 200), random(-200, 200), random(-200, 200)); sphere(10); pop(); } }); } function draw() { model(myShape); // renders once-built geometry efficiently } ``` ### beginGeometry / endGeometry ```javascript beginGeometry(); // draw shapes here box(50); translate(100, 0, 0); sphere(30); let geo = endGeometry(); model(geo); // reuse ``` ### Manual Geometry (p5.Geometry) ```javascript let geo = new p5.Geometry(detailX, detailY, function() { for (let i = 0; i <= detailX; i++) { for (let j = 0; j <= detailY; j++) { let u = i / detailX; let v = j / detailY; let x = cos(u * TWO_PI) * (100 + 30 * cos(v * TWO_PI)); let y = sin(u * TWO_PI) * (100 + 30 * cos(v * TWO_PI)); let z = 30 * sin(v * TWO_PI); this.vertices.push(createVector(x, y, z)); this.uvs.push(u, v); } } this.computeFaces(); this.computeNormals(); }); ``` ## GLSL Shaders ### createShader (Vertex + Fragment) ```javascript let myShader; function setup() { createCanvas(800, 800, WEBGL); let vert = ` precision mediump float; attribute vec3 aPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; void main() { vTexCoord = aTexCoord; vec4 pos = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); gl_Position = pos; } `; let frag = ` precision mediump float; varying vec2 vTexCoord; uniform float uTime; uniform vec2 uResolution; void main() { vec2 uv = vTexCoord; vec3 col = 0.5 + 0.5 * cos(uTime + uv.xyx + vec3(0, 2, 4)); gl_FragColor = vec4(col, 1.0); } `; myShader = createShader(vert, frag); } function draw() { shader(myShader); myShader.setUniform('uTime', millis() / 1000.0); myShader.setUniform('uResolution', [width, height]); rect(0, 0, width, height); resetShader(); } ``` ### createFilterShader (Post-Processing) Simpler — only needs a fragment shader. Automatically gets the canvas as a texture. ```javascript let blurShader; function setup() { createCanvas(800, 800, WEBGL); blurShader = createFilterShader(` precision mediump float; varying vec2 vTexCoord; uniform sampler2D tex0; uniform vec2 texelSize; void main() { vec4 sum = vec4(0.0); for (int x = -2; x <= 2; x++) { for (int y = -2; y <= 2; y++) { sum += texture2D(tex0, vTexCoord + vec2(float(x), float(y)) * texelSize); } } gl_FragColor = sum / 25.0; } `); } function draw() { // Draw scene normally background(0); fill(255, 0, 0); sphere(100); // Apply post-processing filter filter(blurShader); } ``` ### Common Shader Uniforms ```javascript myShader.setUniform('uTime', millis() / 1000.0); myShader.setUniform('uResolution', [width, height]); myShader.setUniform('uMouse', [mouseX / width, mouseY / height]); myShader.setUniform('uTexture', myGraphics); // pass p5.Graphics as texture myShader.setUniform('uValue', 0.5); // float myShader.setUniform('uColor', [1.0, 0.0, 0.5, 1.0]); // vec4 ``` ### Shader Recipes **Chromatic Aberration:** ```glsl vec4 r = texture2D(tex0, vTexCoord + vec2(0.005, 0.0)); vec4 g = texture2D(tex0, vTexCoord); vec4 b = texture2D(tex0, vTexCoord - vec2(0.005, 0.0)); gl_FragColor = vec4(r.r, g.g, b.b, 1.0); ``` **Vignette:** ```glsl float d = distance(vTexCoord, vec2(0.5)); float v = smoothstep(0.7, 0.4, d); gl_FragColor = texture2D(tex0, vTexCoord) * v; ``` **Scanlines:** ```glsl float scanline = sin(vTexCoord.y * uResolution.y * 3.14159) * 0.04; vec4 col = texture2D(tex0, vTexCoord); gl_FragColor = col - scanline; ``` ## Framebuffers ```javascript let fbo; function setup() { createCanvas(800, 800, WEBGL); fbo = createFramebuffer(); } function draw() { // Render to framebuffer fbo.begin(); clear(); rotateY(frameCount * 0.01); box(200); fbo.end(); // Use framebuffer as texture texture(fbo.color); plane(width, height); } ``` ### Multi-Pass Rendering ```javascript let sceneBuffer, blurBuffer; function setup() { createCanvas(800, 800, WEBGL); sceneBuffer = createFramebuffer(); blurBuffer = createFramebuffer(); } function draw() { // Pass 1: render scene sceneBuffer.begin(); clear(); lights(); rotateY(frameCount * 0.01); box(200); sceneBuffer.end(); // Pass 2: blur blurBuffer.begin(); shader(blurShader); blurShader.setUniform('uTexture', sceneBuffer.color); rect(0, 0, width, height); resetShader(); blurBuffer.end(); // Final: composite texture(blurBuffer.color); plane(width, height); } ```