How can I print text along an arc using Three.js? - javascript

I'm trying to print text along an arc using Three.js. With help from this post, I was able to get the math for the rotation. But, as you'll see, the text is all jumbled on top of itself. How can I get it to correctly space itself out? Codepen is here.
var container, camera, scene, renderer;
function init() {
container = document.getElementById( 'canvas' );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 1, 100000);
camera.position.z = 500;
scene.add(camera);
var loader = new THREE.FontLoader();
loader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/fonts/helvetiker_bold.typeface.json', function ( font ) {
var theText = "This is some text."
var numRadsPerChar = 2*Math.PI/theText.length;
for (var i = 0; i < theText.length; i++){
var char = theText[i]
var geometry = new THREE.TextBufferGeometry( char, {
font: font,
size: 60,
height: 60,
curveSegments: 20
});
var materials = [
new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, overdraw: 0.5 } ),
new THREE.MeshBasicMaterial( { color: 0x000000, overdraw: 0.5 } )
];
var mesh = new THREE.Mesh( geometry, materials );
mesh.rotation.z = (i * numRadsPerChar);
//mesh.position.x = i * 20;
group = new THREE.Group();
group.add( mesh );
scene.add( group );
};
} );
renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
animate();
} init();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}

Had some help figuring this out, but here's the portion that needed to be edited to work:
mesh.rotation.z = (i * numRadsPerChar);
Needed to be:
mesh.position.x = 100 * Math.sin(i * numRadsPerChar);
mesh.position.y = 100 * Math.cos(i * numRadsPerChar);
mesh.rotation.z = Math.PI/2 -(i * numRadsPerChar);
Basically, I needed to add a constant (Math.PI/2) for the rotation. For the x and y positions, I had to take the sin and cosine, respectively, to get the letters to be placed properly around the arc. Here's a working codepen.

Another way to do this would be to draw your text on a canvas and use the canvas in a texture with texture.wrapS and .wrapT set to THREE.RepeatWrapping.. then put that texture on a material.. then make a path with the extruded path geometry and set it's material. The you can move the text by setting the texture.offset.x or y each frame in your render loop.

Related

WebGL: How can you draw a pentagon?

I am practicing WebGL and attempting to draw a pentagon, but I am not sure how to approach this. Any help would be much appreciated.
It's easy to do this with three.js. You can use CircleGeometry and reduce the number of segments to five to give you a pentagon.
var geometry = new THREE.CircleGeometry( 10, 5 )
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } )
var circle = new THREE.Mesh( geometry, material )
scene.add( circle )
More information here: https://threejs.org/docs/#api/en/geometries/CircleGeometry
Within a working example:
var width = window.innerWidth
var height = window.innerHeight
var renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setClearColor(0x8e8ed7)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)
var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(35, width / height, 0.1, 3000)
camera.position.set(0, 0, 100)
var controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.minDistance = 40
controls.maxDistance = 1300
var material = new THREE.MeshPhongMaterial({color: 0xFF0000, specular: 0x111111, shininess: 75})
scene.add(camera, new THREE.AmbientLight(0xffffff, 0.4))
var light = new THREE.PointLight(0xffffff, 0.8)
camera.add(light)
light.position.y += 60
light.position.x += 70
requestAnimationFrame(function animate(){
requestAnimationFrame(animate)
renderer.render(scene, camera)
})
var geometry = new THREE.CircleGeometry( 10, 5 )
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } )
var circle = new THREE.Mesh( geometry, material )
scene.add( circle )
html, body {
width: 100%;
height: 100%;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
if you want to use only webgl, first of all you need to create vertices, for this you can use a circle, select five points on a circle at equal distances from each other and lying on a circle, here is the formula for each coordinate
let vertices =[];
for(let i=0; i<5; i++){
let x = radius * Math.cos(360/5 * i);
let y = radius * Math.sin(360/5 * i);
vertices.push(x);vertices.push(y);
}
substituting your required radius instead of the radius, you will get a set of coordinates of your pentagon, and then drop them into the buffer of your program and draw, tried to show as briefly as possible

Three.js rotate a sphere that's followed by a camera

I'm working on a toy Three.js scene in which I want to follow a sphere with a camera [demo]. Right now, though, I can't figure out how to make the sphere "roll" without also rotating the camera.
Here's the code I use to update the sphere's position each frame:
function moveSphere() {
var delta = clock.getDelta(); // seconds
var moveDistance = 200 * delta; // 200 pixels per second
var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec
// move forwards/backwards/left/right
if ( pressed['W'] ) {
sphere.translateZ( -moveDistance );
}
if ( pressed['S'] )
sphere.translateZ( moveDistance );
if ( pressed['Q'] )
sphere.translateX( -moveDistance );
if ( pressed['E'] )
sphere.translateX( moveDistance );
// rotate left/right/up/down
var rotation_matrix = new THREE.Matrix4().identity();
if ( pressed['A'] )
sphere.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
if ( pressed['D'] )
sphere.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
if ( pressed['R'] )
sphere.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
if ( pressed['F'] )
sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
}
And the code to follow the sphere each tick of time:
function moveCamera() {
var relativeCameraOffset = new THREE.Vector3(0,50,200);
var cameraOffset = relativeCameraOffset.applyMatrix4(sphere.matrixWorld);
camera.position.x = cameraOffset.x;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(sphere.position);
}
Is there an easy way to make the ball roll without making the camera spiral all over the place? Inside of the if (pressed['W']) block, I tried various permutations of sphere.rotateOnAxis(new THREE.Vector3(0,0,1), rotateAngle); but haven't found a natural way to make the ball roll. I would be very grateful for any advice others can offer on this!
Your issue is this line:
var cameraOffset = relativeCameraOffset.applyMatrix4(sphere.matrixWorld);
This takes the offset you specify and applies not only the sphere position, but it's rotation as well. For example, if your sphere is rotated 180 degrees on the Y-axis, the resulting vector is (0, 50, -200) + (sphere position).
You need to extract the translation component from the sphere matrix, and apply it to the offset. The code below uses an intermediate matrix to store the position of the sphere.
/**
* Follow the sphere
**/
var sphereTranslation = new THREE.Matrix4(); // only make it once to reduce overhead
function moveCamera() {
var relativeCameraOffset = new THREE.Vector3(0,50,200);
sphereTranslation.copyPosition(sphere.matrixWorld); // get sphere position only
var cameraOffset = relativeCameraOffset.applyMatrix4(sphereTranslation);
camera.position.x = cameraOffset.x;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(sphere.position);
}
The key here was to create a sphere, then add that sphere to a group, so that I could translate and rotate the group (which controls the ball's position) while also rotating the sphere inside the group (which allows the ball to "roll"). Separating these entities out allows the camera to follow the sphere group just as before while allowing the ball to rotate independently of the sphere group's movement [updated demo]:
/**
* Generate a scene object with a background color
**/
function getScene() {
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111);
return scene;
}
/**
* Generate the camera to be used in the scene. Camera args:
* [0] field of view: identifies the portion of the scene
* visible at any time (in degrees)
* [1] aspect ratio: identifies the aspect ratio of the
* scene in width/height
* [2] near clipping plane: objects closer than the near
* clipping plane are culled from the scene
* [3] far clipping plane: objects farther than the far
* clipping plane are culled from the scene
**/
function getCamera() {
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 10000);
camera.position.set(0,150,400);
camera.lookAt(scene.position);
return camera;
}
/**
* Generate the light to be used in the scene. Light args:
* [0]: Hexadecimal color of the light
* [1]: Numeric value of the light's strength/intensity
* [2]: The distance from the light where the intensity is 0
* #param {obj} scene: the current scene object
**/
function getLight(scene) {
var lights = [];
lights[0] = new THREE.PointLight( 0xffffff, 0.6, 0 );
lights[0].position.set( 100, 200, 100 );
scene.add( lights[0] );
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
return light;
}
/**
* Generate the renderer to be used in the scene
**/
function getRenderer() {
// Create the canvas with a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
// Add support for retina displays
renderer.setPixelRatio(window.devicePixelRatio);
// Specify the size of the canvas
renderer.setSize(window.innerWidth, window.innerHeight);
// Add the canvas to the DOM
document.body.appendChild(renderer.domElement);
return renderer;
}
/**
* Generate the controls to be used in the scene
* #param {obj} camera: the three.js camera for the scene
* #param {obj} renderer: the three.js renderer for the scene
**/
function getControls(camera, renderer) {
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.zoomSpeed = 0.4;
controls.panSpeed = 0.4;
return controls;
}
/**
* Get grass
**/
function getPlane(scene, loader) {
var texture = loader.load('grass.jpg');
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 10, 10 );
var material = new THREE.MeshBasicMaterial({
map: texture, side: THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var plane = new THREE.Mesh(geometry, material);
plane.position.y = -0.5;
plane.rotation.x = Math.PI / 2;
scene.add(plane);
return plane;
}
/**
* Add background
**/
function getBackground(scene, loader) {
var imagePrefix = 'sky-parts/';
var directions = ['right', 'left', 'top', 'bottom', 'front', 'back'];
var imageSuffix = '.bmp';
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000 );
var materialArray = [];
for (var i = 0; i < 6; i++)
materialArray.push( new THREE.MeshBasicMaterial({
map: loader.load(imagePrefix + directions[i] + imageSuffix),
side: THREE.BackSide
}));
var sky = new THREE.Mesh( geometry, materialArray );
scene.add(sky);
}
/**
* Add a character
**/
function getSphere(scene) {
var geometry = new THREE.SphereGeometry( 30, 12, 9 );
var material = new THREE.MeshPhongMaterial({
color: 0xd0901d,
emissive: 0xaf752a,
side: THREE.DoubleSide,
flatShading: true
});
var sphere = new THREE.Mesh( geometry, material );
// create a group for translations and rotations
var sphereGroup = new THREE.Group();
sphereGroup.add(sphere)
sphereGroup.position.set(0, 24, 100);
scene.add(sphereGroup);
return [sphere, sphereGroup];
}
/**
* Store all currently pressed keys
**/
function addListeners() {
window.addEventListener('keydown', function(e) {
pressed[e.key.toUpperCase()] = true;
})
window.addEventListener('keyup', function(e) {
pressed[e.key.toUpperCase()] = false;
})
}
/**
* Update the sphere's position
**/
function moveSphere() {
var delta = clock.getDelta(); // seconds
var moveDistance = 200 * delta; // 200 pixels per second
var rotateAngle = Math.PI / 2 * delta; // pi/2 radians (90 deg) per sec
// move forwards/backwards/left/right
if ( pressed['W'] ) {
sphere.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle)
sphereGroup.translateZ( -moveDistance );
}
if ( pressed['S'] )
sphereGroup.translateZ( moveDistance );
if ( pressed['Q'] )
sphereGroup.translateX( -moveDistance );
if ( pressed['E'] )
sphereGroup.translateX( moveDistance );
// rotate left/right/up/down
var rotation_matrix = new THREE.Matrix4().identity();
if ( pressed['A'] )
sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), rotateAngle);
if ( pressed['D'] )
sphereGroup.rotateOnAxis(new THREE.Vector3(0,1,0), -rotateAngle);
if ( pressed['R'] )
sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), rotateAngle);
if ( pressed['F'] )
sphereGroup.rotateOnAxis(new THREE.Vector3(1,0,0), -rotateAngle);
}
/**
* Follow the sphere
**/
function moveCamera() {
var relativeCameraOffset = new THREE.Vector3(0,50,200);
var cameraOffset = relativeCameraOffset.applyMatrix4(sphereGroup.matrixWorld);
camera.position.x = cameraOffset.x;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(sphereGroup.position);
}
// Render loop
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
moveSphere();
moveCamera();
};
// state
var pressed = {};
var clock = new THREE.Clock();
// globals
var scene = getScene();
var camera = getCamera();
var light = getLight(scene);
var renderer = getRenderer();
// add meshes
var loader = new THREE.TextureLoader();
var floor = getPlane(scene, loader);
var background = getBackground(scene, loader);
var sphereData = getSphere(scene);
var sphere = sphereData[0];
var sphereGroup = sphereData[1];
addListeners();
render();
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>

Incremental rendering of particals in THREE.js

Newbie to 3D world. Trying to render particles as they are created so that these 2000 particles should render as they are created. But from below code it renders only once all particles are created.
Tried couple of variations but no luck. Any help is appreciated.
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 255;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild( renderer.domElement );
var particles = new THREE.Geometry;
for (var p = 0; p < 2000; p++) {
var particle = new THREE.Vector3(Math.random() * 500 - 250, Math.random() * 500 - 250, Math.random() * 500 - 250);
particles.vertices.push(particle);
}
var particleMaterial = new THREE.ParticleBasicMaterial({ color: 0xeeeeee, size: 2 });
var particleSystem = new THREE.ParticleSystem(particles, particleMaterial);
scene.add(particleSystem);
function render() {
requestAnimationFrame(render);
particleSystem.rotation.y += 0.01;
renderer.render(scene, camera);
}
render();
</script>
You can render a subset of you particles (now called THREE.Points) by using BufferGeometry and setting the draw range.
var geometry = new THREE.BufferGeometry();
geometry.setDrawRange( startIndex, count );
You can reset the draw range inside your animation loop.
See this answer for information on how to populate the geometry and a live demo.
three.js r.78

How does position in Threejs work exactly?

I'm trying to make something responsive in Threejs which will be full screen.
I am having a hard time understanding the concept of positioning of objects due to this.
I will also want to add animations so need the positions to be dynamic.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var materialRed = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var materialBlue = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var circle = new THREE.Mesh( new THREE.CircleGeometry( 12, 128 ), materialRed );
scene.add(circle);
var square = [];
square.push( new THREE.Mesh( new THREE.PlaneGeometry( 10, 10, 1), materialBlue ) );
square.push( new THREE.Mesh( new THREE.PlaneGeometry( 10, 10, 1), materialBlue ) );
scene.add(square[0]);
scene.add(square[1]);
square[0].position.x = 100;
square[1].position.x = -100;
camera.position.z = 100;
var render = function() {
requestAnimationFrame(render);
circle.position.x += 0.1;
circle.position.y += 0.05;
renderer.render( scene, camera );
};
render();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r77/three.min.js"></script>
var circle = new THREE.Mesh( new THREE.CircleGeometry( 12, 128 ), materialRed );
scene.add(circle);
circle.position.x = 100;
I need to understand how positioning will change with change in aspect ration and camera frustums.
EDIT
For eg-
I need to move the sphere in an elliptical orbit around the screen. I need to calculate the equation of the orbit which will get me position of the sphere at every instance. I don't want the sphere to move out of frame and for that I'm ready to change the length of axes or size of sphere.

Particles in three.js

Can someone kindly explain me why there are no particles visible in the following code?
Followed a tutorial and everything seems ok.
(function() {
var camera, scene, renderer;
init();
animate();
function init() {
scene = new THREE.Scene();
var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(2,2,10);
camera.lookAt(scene.position);
console.log(scene.position);
var geometry = new THREE.CubeGeometry(1,1,1);
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
var pGeometry = new THREE.Geometry();
for (var p = 0; p < 2000; p++) {
var particle = new THREE.Vector3(Math.random() * 50 - 25, Math.random() * 50 - 25, Math.random() * 50 - 25);
pGeometry.vertices.push(particle);
}
var pMaterial = new THREE.ParticleBasicMaterial({ color: 0xfff, size: 1 });
var pSystem = new THREE.ParticleSystem(pGeometry, pMaterial);
scene.add(pSystem);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
})();
fiddle
CanvasRenderer does not support ParticleSystem. You need to use WebGLRenderer.
If you need to use CanvasRenderer, then see http://threejs./examples/canvas_interactive_particles.html for how to use Sprites instead.
three.js r.64
Also, as a side note, as of the time of this answer, IE11's WebGL implementation has an issue with points and sizing them. Particles in Three.js shaders use points to draw the particles and so in IE11, you will see very small squares and if you're not paying attention, you might not see anything at all ;)
I do know that in the next version of IE (updates coming) that this is fixed and allows you not only to change the point size, but use bitmaps with them.

Categories

Resources