Brush Rosetta

This Rosetta demo is intended to give readers a starting point for porting the Brush VR app to three.js and WebGL2, highlighting some of the lower-level aspects of managing shaders. code let color, depth, brush, escorzo = true, fallback = [], points = [], record let basicShader function preload() { loadJSON('/cloud_500.json', json => fallback = json.map(entry => ({ worldPosition: createVector(entry.x, entry.y, entry.z), color: entry.color })) ) } function setup() { createCanvas(600, 400, WEBGL) colorMode(RGB, 1) document.oncontextmenu = () => false points = [...fallback] const o = parsePosition(Tree.ORIGIN, { from: Tree.WORLD, to: Tree.SCREEN }) depth = createSlider(0, 1, o.z, 0.001) depth.position(10, 10) depth.style('width', '580px') color = createColorPicker('#C7C08D') color.position(width - 70, 40) brush = sphereBrush basicShader = parseShader(`#version 300 es precision highp float; uniform vec4 uMaterialColor; out vec4 fragColor; void main() { fragColor = uMaterialColor; }`, Tree.pMatrix | Tree.vMatrix | Tree.mMatrix) } function draw() { (mouseY >= 30) && orbitControl() shader(basicShader) record && update() background('#222226') axes({ size: 50, bits: Tree.X | Tree.Y | Tree.Z }) for (const point of points) { push() translate(point.worldPosition) brush(point) pop() } } function keyPressed() { key === 'c' && (points = []) key === 'f' && focus() key === 'r' && (record = !record) key === 's' && saveCloud() } function update() { points.push({ worldPosition: parsePosition([mouseX, mouseY, depth.value()], { from: Tree.SCREEN, to: Tree.WORLD }), color: color.color(), }) } function focus() { const center = [0, 0, 0] const position = parsePosition() const up = parseDirection(Tree.j) camera(...position.array(), ...center, ...up.array()) const o = parsePosition(Tree.ORIGIN, { from: Tree.WORLD, to: Tree.SCREEN }) depth.value(o.z) } function sphereBrush(point) { push() noStroke() fill(point.color) sphere(1) pop() } function saveCloud() { const data = points.map(point => ({ x: point.worldPosition.x, y: point.worldPosition.y, z: point.worldPosition.z, color: [red(point.color) / 255, green(point.color) / 255, blue(point.color) / 255, alpha(point.color) / 255] })) saveJSON(data, 'custom_cloud.json') } Shader Declaration A key difference in this version is the use of parseShader, which defines a basic shader that handles color through the uMaterialColor uniform: ...

November 19, 2024 · 3 min · Theme PaperMod

3D Brush Painting in VR

The following 3D brush painting algorithm integrates depth control for VR experiences by leveraging p5.treegl’s parsePosition function. This allows users to paint dynamically in 3D space, using mouse input and a depth slider to place brush strokes with precision. code 'use strict' let color, depth, brush, escorzo = true, fallback = [], points = [], record function preload() { loadJSON('cloud_500.json', json => fallback = json.map(entry => ({ worldPosition: createVector(entry.x, entry.y, entry.z), color: entry.color })) ) } function setup() { createCanvas(600, 400, WEBGL) colorMode(RGB, 1) document.oncontextmenu = () => false points = [...fallback] const o = parsePosition(Tree.ORIGIN, { from: Tree.WORLD, to: Tree.SCREEN }) depth = createSlider(0, 1, o.z, 0.001) depth.position(10, 10) depth.style('width', '580px') color = createColorPicker('#C7C08D') color.position(width - 70, 40) brush = sphereBrush } function draw() { (mouseY >= 30) && orbitControl() record && update() background('#222226') axes({ size: 50, bits: Tree.X | Tree.Y | Tree.Z }) for (const point of points) { push() translate(point.worldPosition) brush(point) pop() } } function keyPressed() { key === 'c' && (points = []) key === 'f' && focus() key === 'r' && (record = !record) key === 's' && saveCloud() } function sphereBrush(point) { push() noStroke() fill(point.color) sphere(1) pop() } function update() { points.push({ worldPosition: parsePosition([mouseX, mouseY, depth.value()], { from: Tree.SCREEN, to: Tree.WORLD }), color: color.color(), }) } function focus() { const center = [0, 0, 0] const position = parsePosition() const up = parseDirection(Tree.j) camera(...position.array(), ...center, ...up.array()) const o = parsePosition(Tree.ORIGIN, { from: Tree.WORLD, to: Tree.SCREEN }) depth.value(o.z) } function saveCloud() { const data = points.map(point => ({ x: point.worldPosition.x, y: point.worldPosition.y, z: point.worldPosition.z, color: [red(point.color) / 255, green(point.color) / 255, blue(point.color) / 255, alpha(point.color) / 255] })) saveJSON(data, 'custom_cloud.json') } Key Elements of the 3D Brush Algorithm The primary functionality revolves around transforming user input from screen space to world space, enabling dynamic point placement. Using the parsePosition function, we map 2D screen coordinates and depth values into the 3D world, allowing the brush to paint accurately in VR space. ...

October 4, 2024 · 3 min · Theme PaperMod