Reputation: 37
I have a large number of randomly placed circles held in a single Buffer Geometry. I'm trying to scale each one based on audio input from their centers, but they keep scaling w.r.t the center of the scene. Instead of getting larger radii and staying still they get larger but also move further away from the center.
uniform float scale;
varying vec3 color;
void main()
{
mat4 sPos = mat4(vec4(scale,0.0,0.0,0.0),
vec4(0.0,scale,0.0,0.0),
vec4(0.0,0.0,1.0,0.0),
vec4(0.0,0.0,0.0,1.0));
vec3 pos = position * normal;
vec4 modelViewPosition = modelViewMatrix * sPos * vec4(pos, 1.0) ;
gl_Position = projectionMatrix * modelViewPosition;
}
This is what I'm trying. Is there a matrix transform that can move the origin to the center of each circle without messing up other aspects of the scene?
Upvotes: 0
Views: 1546
Reputation:
Add the normal * scale
uniform float scale;
varying vec3 color;
void main()
{
vec3 pos = position + normal * scale;
vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
gl_Position = projectionMatrix * modelViewPosition;
}
Example:
const vs = `
uniform float scale;
void main()
{
vec3 pos = position + normal * scale;
vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
gl_Position = projectionMatrix * modelViewPosition;
}
`;
const fs = `
void main() {
gl_FragColor = vec4(1,0,0,1);
}
`;
const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;
const scene = new THREE.Scene();
const uniforms = {
scale: { value: 1 },
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: fs,
});
const geometry = new THREE.BufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const totalPoints = numPointsPerCircle * numCircles;
const positions = new Float32Array(totalPoints * 3);
const normals = new Float32Array(totalPoints * 3);
const radius = 10;
let off = 0;
for (let c = 0; c < numCircles; ++c) {
const cx = rand(-100, 100);
const cy = rand(-100, 100);
for (let p = 0; p < numEdgePointsPerCircle; ++p) {
const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
positions[off + 0] = cx;
positions[off + 1] = cy;
positions[off + 2] = 0;
positions[off + 3] = cx + Math.cos(a0) * radius;
positions[off + 4] = cy + Math.sin(a0) * radius;
positions[off + 5] = 0;
positions[off + 6] = cx + Math.cos(a1) * radius;
positions[off + 7] = cy + Math.sin(a1) * radius;
positions[off + 8] = 0;
normals[off + 0] = 0;
normals[off + 1] = 0;
normals[off + 2] = 0;
normals[off + 3] = Math.cos(a0);
normals[off + 4] = Math.sin(a0);
normals[off + 5] = 0;
normals[off + 6] = Math.cos(a1);
normals[off + 7] = Math.sin(a1);
normals[off + 8] = 0;
off += 9;
}
}
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
const mesh = new THREE.Mesh( geometry, shaderMaterial );
scene.add(mesh);
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("canvas"),
});
function rand(min, max) {
return min + Math.random() * (max - min);
}
function resize(renderer, camera, force) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (force || canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false); // pass false so three doesn't mess up the css
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001; // seconds
resize(renderer, camera);
uniforms.scale.value = Math.sin(time) * 5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>
Example using just center point
const vs = `
uniform float scale;
void main()
{
vec3 pos = position + normal * scale;
vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
gl_Position = projectionMatrix * modelViewPosition;
}
`;
const fs = `
void main() {
gl_FragColor = vec4(1,0,0,1);
}
`;
const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;
const scene = new THREE.Scene();
const uniforms = {
scale: { value: 1 },
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: fs,
});
const geometry = new THREE.BufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const totalPoints = numPointsPerCircle * numCircles;
const positions = new Float32Array(totalPoints * 3);
const normals = new Float32Array(totalPoints * 3);
const radius = 10;
let off = 0;
for (let c = 0; c < numCircles; ++c) {
const cx = rand(-100, 100);
const cy = rand(-100, 100);
for (let p = 0; p < numEdgePointsPerCircle; ++p) {
const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
positions[off + 0] = cx;
positions[off + 1] = cy;
positions[off + 2] = 0;
positions[off + 3] = cx;
positions[off + 4] = cy;
positions[off + 5] = 0;
positions[off + 6] = cx;
positions[off + 7] = cy;
positions[off + 8] = 0;
normals[off + 0] = 0;
normals[off + 1] = 0;
normals[off + 2] = 0;
normals[off + 3] = Math.cos(a0);
normals[off + 4] = Math.sin(a0);
normals[off + 5] = 0;
normals[off + 6] = Math.cos(a1);
normals[off + 7] = Math.sin(a1);
normals[off + 8] = 0;
off += 9;
}
}
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('normal', new THREE.BufferAttribute(normals, 3));
const mesh = new THREE.Mesh( geometry, shaderMaterial );
scene.add(mesh);
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("canvas"),
});
function rand(min, max) {
return min + Math.random() * (max - min);
}
function resize(renderer, camera, force) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (force || canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false); // pass false so three doesn't mess up the css
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001; // seconds
resize(renderer, camera);
uniforms.scale.value = 10 + Math.sin(time) * 5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>
Example using instancing
const vs = `
attribute vec3 center;
uniform float scale;
void main()
{
vec3 pos = center + position * scale;
vec4 modelViewPosition = modelViewMatrix * vec4(pos, 1.0) ;
gl_Position = projectionMatrix * modelViewPosition;
}
`;
const fs = `
void main() {
gl_FragColor = vec4(1,0,0,1);
}
`;
const camera = new THREE.PerspectiveCamera(40, 1, 1, 1000);
camera.position.z = 400;
const scene = new THREE.Scene();
const uniforms = {
scale: { value: 1 },
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: fs,
});
// Just need one circle and 1 center point per circle
const geometry = new THREE.InstancedBufferGeometry();
const numCircles = 20;
const numEdgePointsPerCircle = 36;
const numPointsPerCircle = numEdgePointsPerCircle * 3;
const centers = new Float32Array(numCircles * 3);
const positions = new Float32Array(numPointsPerCircle * 3);
const radius = 10;
let off = 0;
for (let c = 0; c < numCircles; ++c) {
const cx = rand(-100, 100);
const cy = rand(-100, 100);
centers[c * 2 + 0] = cx;
centers[c * 2 + 1] = cy;
}
for (let p = 0; p < numEdgePointsPerCircle; ++p) {
const a0 = (p + 0) * Math.PI * 2 / numEdgePointsPerCircle;
const a1 = (p + 1) * Math.PI * 2 / numEdgePointsPerCircle;
positions[off + 0] = 0;
positions[off + 1] = 0;
positions[off + 2] = 0;
positions[off + 3] = Math.cos(a0);
positions[off + 4] = Math.sin(a0);
positions[off + 5] = 0;
positions[off + 6] = Math.cos(a1);
positions[off + 7] = Math.sin(a1);
positions[off + 8] = 0;
off += 9;
}
geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.addAttribute('center', new THREE.InstancedBufferAttribute(centers, 3));
const mesh = new THREE.Mesh( geometry, shaderMaterial );
scene.add(mesh);
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector("canvas"),
});
function rand(min, max) {
return min + Math.random() * (max - min);
}
function resize(renderer, camera, force) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (force || canvas.width !== width || canvas.height !== height) {
renderer.setSize(width, height, false); // pass false so three doesn't mess up the css
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001; // seconds
resize(renderer, camera);
uniforms.scale.value = 10 + Math.sin(time) * 5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
resize(renderer, camera, true);
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
<canvas></canvas>
Upvotes: 2