ask:
option 1: Build a shader effect that uses custom vertex attributes.
Use three.js and WebGL. You can either use a BufferGeometry and populate all the attribute data for it, or you can use a built-in geometry type, and add an additional BufferAttribute to it. Use whatever makes the most sense for your effect.
option 2: Build a post-processing effect that uses a custom shader pass.
Use three.js and WebGL. You can stack many effects in your EffectComposer but at least one of them has to use a custom shader! Use a GUI to control parameters and toggle render passes.
You can combine both options if you’d like!
i didn’t have time this week because of the haptics class this weekend.
made a random geometry generator.
/* eslint-disable no-undef, no-unused-vars */
// three-js template by elie.
import * as THREE from "https://unpkg.com/three@0.154.0/build/three.module.js";
//this could just be three.js file.
import GUI from "https://cdn.jsdelivr.net/npm/lil-gui@0.18.1/dist/lil-gui.esm.min.js";
// Create renderer.
const canvas = document.querySelector("#canvas");
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// Create scene.
const scene = new THREE.Scene();
scene.background = new THREE.Color(255, 255, 255); //rendered as rgb.
// Create camera.
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
camera.position.z = 3;
scene.add(camera);
// load shaders, but as text files. the browser automatically converts them to javascript modules.
async function loadShader(url) {
const res = await fetch(url);
return await res.text();
}
//--
const aspect = window.innerWidth / window.innerHeight;
//load geometry, material, mesh + add to scene.
//generate random positions:
//helper for generating random integers.
function rando_int(min = 0, max = 1) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const amount = rando_int(100, 1000);
// console.log(amount);
const positions = make_positions(rando_int(10, 100), 1);
function make_positions(n = 10, range = 2) {
// n: number of points, range: half-width/height of area (so -range to +range).
const positions = [];
for (let i = 0; i < n; i++) {
const x = (Math.random() * 2 - 1) * range;
const y = (Math.random() * 2 - 1) * range;
const z = (Math.random() * 2 - 1) * range;
positions.push(x, y, z);
}
return positions;
}
const vertice_nums = positions.length / 3; //each position has three attributes; so.
function make_uvs() {
//pass uvs as if nothing has changed.
const uvs = [];
for (let i = 0; i < vertice_nums; i++) {
const u = i / (vertice_nums - 1);
const v = i / (vertice_nums - 1);
uvs.push(u, v);
}
return uvs;
}
const uvs = make_uvs();
//shuffle the index for the order in which they should be drawn.
const indices = make_shuffled_indices(vertice_nums);
function make_shuffled_indices(v) {
const indices = [];
for (let i = 0; i < vertice_nums; i++) {
indices.push(i);
}
for (let i = vertice_nums - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[indices[i], indices[j]] = [indices[j], indices[i]];
}
return indices;
}
// Create geometry.
const planeGeo = new THREE.BufferGeometry();
planeGeo.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
planeGeo.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
planeGeo.setIndex(indices);
// Create material.
const frag_shader = await loadShader("./frag.frag");
const vert_shader = await loadShader("./vert.vert");
const planeMat = new THREE.RawShaderMaterial({
vertexShader: vert_shader,
fragmentShader: frag_shader,
uniforms: {
u_resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
u_time: { value: 0.0 },
},
});
// Create and add mesh to scene.
const planeMesh = new THREE.Mesh(planeGeo, planeMat);
scene.add(planeMesh);
const clock = new THREE.Clock(); // tracks elapsed time
//animation loop:
const tick = () => {
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
//for resizing:
window.addEventListener("resize", () => {
const w = window.innerWidth;
const h = window.innerHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize(w, h);
// // Update geometry on resize
// const newAspect = w / h;
// rectGeo.dispose();
// boxMesh.geometry = new THREE.PlaneGeometry(2 * newAspect, 2);
// boxMat.uniforms.u_resolution.value.set(w, h);
});