xxxxxxxxxx
110
const vs = `#version 300 es
precision highp float;
in vec3 aPosition;
// main view params
uniform mat4 uProjectionMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uModelMatrix; // model matrix is bound with bindMatrices()
// frustum params
uniform mat4 uViewFrustumMatrix;
uniform mat4 uEyeFrustumMatrix;
uniform float d; // morph param in [0, 1]
uniform float ndc;
uniform float n; // near plane
void main() {
vec4 wPosition4 = uModelMatrix * vec4(aPosition, 1.0); // model to world
vec4 fPosition4 = uViewFrustumMatrix * wPosition4; // world to frustum
vec2 xy = -(fPosition4.xy / fPosition4.z) * (1.0 + ndc) * n;
fPosition4.xy = mix(fPosition4.xy, xy, d);
wPosition4 = uEyeFrustumMatrix * fPosition4; // frustum to world
gl_Position = uProjectionMatrix * uViewMatrix * wPosition4; // world to eye
} // & eye to clip`;
const fs = `#version 300 es
precision highp float;
uniform vec4 uMaterialColor;
out vec4 fragColor;
void main() {
fragColor = uMaterialColor;
}`;
let mainCam, frustumCam
let e, v, p
const n = 81, ndc = 0.5, fovy = 0.75, z = 158
let morph
let d, animate
let cajas
function setup() {
createCanvas(600, 400, WEBGL)
strokeWeight(0.5)
stroke('magenta')
morph = createShader(vs, fs) // Create shader with vertex and fragment shaders
bindMatrices(Tree.mMatrix) // Bind model matrix for transformations
colorMode(RGB, 1)
cajas = [] // Array to store box geometries
for (let i = 0; i < 40; i++) {
cajas.push({
position: createVector(random(-50, 50), random(-50, 50), random(-25, 75)),
size: random(15) + 5,
color: color(random(), random(), random())
})
}
d = createSlider(0, 1, 0, 0.01) // Slider to control the morph parameter
d.style('width', '100px')
d.position(width - 110, 10)
animate = createCheckbox('animate', true) // Checkbox to toggle animation
animate.position(width - 110, 35)
mainCam = createCamera(); // Main camera setup
mainCam.camera(-250.56, -257.35, 181.54,
-249.9, -256.75, 181.2,
-0.57, 0.745, 0.34)
frustumCam = createCamera() // Camera to visualize the frustum
setCamera(mainCam) // Set the main camera as the active camera
const f = 7 / 30
ortho(-width * f, width * f, -height * f, height * f, 1, 10000)
p = new p5.Matrix() // Projection matrix
}
function draw() {
background(0.6)
if (animate.checked()) {
d.value(map(sin(frameCount / 60), -1, 1, 0, 1)) // Animate the morph parameter
}
resetShader() // Reset the shader for drawing non-shader geometry
strokeWeight(0.5)
push()
rotateX(HALF_PI) // Rotate the grid for better visualization
stroke('black')
grid({ subdivisions: 20, size: 240, style: Tree.SOLID }) // Draw a grid
pop()
shader(morph) // Apply the custom shader
update() // Update camera and shader uniforms
cajas.forEach(caja => { // Render each box with the shader effect
push()
fill(caja.color)
translate(caja.position)
box(caja.size)
pop()
})
fill(color(1, 0, 1, 0.3))
viewFrustum({
eMatrix: e, pMatrix: p, bits: Tree.NEAR | Tree.FAR,
viewer: () => axes({
size: 50, bits: Tree.X | Tree._Y | Tree._Z
})
}) // Render the view frustum using its projection (pMatrix) and eye (eMatrix)
}
function update() {
frustumCam.camera(0, 0, z, Tree.k, Tree.j) // Update frustum camera
v = frustumCam.vMatrix() // Get frustum view (world to frustum transform)
e = frustumCam.eMatrix() // Get frustum eye (frustum to world transform)
const f = n * (1 + 2 * tan(fovy / 2) * (1 + ndc)) // Calculate far plane
p.perspective(fovy, 1, n, f) // Set perspective projection
morph.setUniform('d', d.value()) // Emit morph parameter
morph.setUniform('n', n) // Emit near plane
morph.setUniform('ndc', ndc) // Emit NDC parameter
morph.setUniform('uViewFrustumMatrix', v.mat4) // Emit frustum view matrix
morph.setUniform('uEyeFrustumMatrix', e.mat4) // Emit frustum eye matrix
}