I'm just starting to learn three.js and have been trying to set up a simple textured cube box in an isometric view. When I add edge lines to my cube it turns out to be quite grainy on a few of the edges.
I've tried moving the camera and aspect ratio a bit but to no avail.
var aspect = 100/100
var scene = new THREE.Scene();
var d = 100;
camera = new THREE.OrthographicCamera( - d * aspect, d * aspect, d, - d, 1, 500 );
camera.position.set( 80, 80, 80 ); // all components equal
camera.lookAt( scene.position ); // or the origin
var renderer = new THREE.WebGLRenderer( {alpha: true });
renderer.setSize( 100, 80 );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 65, 65, 65 );
var edges = new THREE.EdgesGeometry( geometry )
var line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {color: 0x000000} ) );
var material = new THREE.MeshBasicMaterial( { color: 0xf0f0f5 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
scene.add( line );
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
The above gives me a nice grey cube with black edges but the edges appear grainy in my browser.
Picture below:
This is a Z-fighting issue, which can be solved by a polgon offset .polygonOffset.
The issue is caused because the lines and the surface have to same coordinates and depth.
Set the .polygonOffset, .polygonOffsetFactor and .polygonOffsetUnits property of that THREE.Material, which is set to the solid geometry.
This causes that the surfaces are slightly pushed to the back and the lines cover the surfaces:
e.g.
var material = new THREE.MeshBasicMaterial({
color: 0xf0f0f5,
polygonOffset: true,
polygonOffsetFactor: 1.0,
polygonOffsetUnits: -4.0
} );
var aspect = 100/100
var scene = new THREE.Scene();
var d = 100;
camera = new THREE.OrthographicCamera( - d * aspect, d * aspect, d, - d, 1, 500 );
camera.position.set( 80, 80, 80 ); // all components equal
camera.lookAt( scene.position ); // or the origin
var renderer = new THREE.WebGLRenderer( {alpha: true });
renderer.setSize( 100, 80 );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 65, 65, 65 );
var edges = new THREE.EdgesGeometry( geometry )
var line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {color: 0x000000} ) );
var material = new THREE.MeshBasicMaterial({
color: 0xf0f0f5,
polygonOffset: true,
polygonOffsetFactor: 1.0,
polygonOffsetUnits: -4.0
} );
var cube = new THREE.Mesh( geometry, material );
scene.add( line );
scene.add( cube );
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
Related
I'm trying to animate the movement of a sphere along a predefined set of vertices.
I'm able to animate from one point to another using the below code
function animate() {
requestAnimationFrame(animate)
sphere.position.lerp(new THREE.Vector3(100, 2, 0), 0.05)
render()
}
function render() {
renderer.render(scene, camera)
}
animate()
this works for a single animation from initial position to (100, 2, 0). How do i keep on adding vertices, so that it will be animated in sequence.
You can animate a mesh along a path with plain three.js by defining a curve based on your sequence of points and then using this curve for animation.
let camera, scene, renderer, clock;
let mesh, path;
const duration = 10;
let time = 0;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.set( 3, 2, 3 );
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
clock = new THREE.Clock();
const controls = new THREE.OrbitControls( camera, renderer.domElement );
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
scene.add( new THREE.AxesHelper( 2 ) );
// points and path
const points = [
new THREE.Vector3( 0, 0, 0 ),
new THREE.Vector3( 1, 2, - 2 ),
new THREE.Vector3( 2, 2, - 0.5 ),
new THREE.Vector3( 2, 1, - 2 )
];
path = new THREE.CatmullRomCurve3( points, true );
// visualize the path
const lineGeometry = new THREE.BufferGeometry().setFromPoints( path.getPoints( 32 ) );
const lineMaterial = new THREE.LineBasicMaterial();
const line = new THREE.Line( lineGeometry, lineMaterial );
scene.add( line );
}
function animate() {
requestAnimationFrame( animate );
time += clock.getDelta();
const t = Math.min( time / duration, 1 );
if ( t === 1 ) time = 0;
path.getPointAt( t, mesh.position ); // animating
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.137.5/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.137.5/examples/js/controls/OrbitControls.js"></script>
I create two scenes and two renderers, one is for simple 3D mesh sphere and another is for svg circle. SVG scene sits on top of Mesh scene, like an overlay. Sphere and circle are placed on the same position in 3D space. I use OrbitControls to look around. When program loads I see sphere and the circle in the same place, but when I look around I see another svg sphere on the opposite side. Why do I see two svg icons in 3D space and how to get rid of the second one? I know for sure it is one svg node, not two, but somehow orbit controls see it behind like in the mirror.
Here is demo link: http://mystic-peanut.com/mystic_sites/svgdouble/svg_sandbox.html
Here is code snippet:
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = -50;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xff0000 );
meshScene = new THREE.Scene();
meshScene.background = new THREE.Color( 0x000000 );
// 3d sphere mesh added to mesh scene
var sphereGeometry = new THREE.SphereGeometry( 51, 60, 40 )
var mesh = new THREE.Mesh( sphereGeometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
mesh.name = 'pointer sphere';
mesh.position.set( 0, 0, 1500 );
meshScene.add( mesh );
// svg circle added to svg scene
node = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' );
node.setAttribute( 'stroke', 'black' );
node.setAttribute( 'fill', 'blue' );
node.setAttribute( 'r', '10' );
node.style.cursor = 'pointer';
var object = new THREE.SVGObject( node );
object.position.x = 0;
object.position.y = 0;
object.position.z = 1500;
scene.add( object );
meshRenderer = new THREE.WebGLRenderer( { antialias: true } );
meshRenderer.setPixelRatio( window.devicePixelRatio );
meshRenderer.setSize(window.innerWidth, window.innerHeight);
meshRenderer.domElement.style.position = 'absolute';
meshRenderer.domElement.style.top = '0px';
threeJSCanvas.appendChild( meshRenderer.domElement );
renderer = new THREE.SVGRenderer( {alpha: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setQuality( 'low' );
renderer.domElement.style.position = 'absolute';
renderer.domElement.style.top = '0px';
renderer.domElement.style.zIndex = 2;
renderer.setClearColor( 0x000000, 0);
threeJSCanvas.appendChild( renderer.domElement );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop)
controls.minDistance = 10;
controls.maxDistance = 100;
controls.maxPolarAngle = 2*Math.PI / 3;
controls.minPolarAngle = Math.PI / 3;
This issue was fixed within this ticket:
github.com/mrdoob/three.js/issues/15476
Description of the problem
I have a simple box mesh with a morphTarget and a slider which adjusts the morphTargetInfluence. The morph affects the mesh as it should but it also shrinks any other mesh in the scene.
I've put together a jsfiddle (modified from the threejs morph target) example below to demo the issue. In this demo moving the slider correctly morphs the corner of the red cube but incorrectly shrinks the size of the blue cube. The cube meshes are entirely unrelated so I don't see how the morph could affect the blue cube.
https://jsfiddle.net/djmm7vv2/
// JS code from jsfiddle
var container, stats;
var camera, scene, renderer;
var geometry, objects;
var mesh, mesh2;
init();
animate();
function init() {
// Create cameras, lights, scene, ect.
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 15000 );
camera.position.z = 500;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x222222 );
scene.fog = new THREE.Fog( 0x000000, 1, 15000 );
var light1 = new THREE.PointLight( 'white', 1);
light1.position.set( 100, 100, 100 );
var light2 = new THREE.PointLight( 'white', 1);
light2.position.set( -100, 100, 100 );
scene.add( light1 );
scene.add( light2 );
// Create first mesh (red cube)
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
var material = new THREE.MeshLambertMaterial( { color: 'red', morphTargets: true } );
// Create 1 blend shape
for ( var i = 0; i < 1; i ++ ) {
var vertices = [];
for ( var v = 0; v < geometry.vertices.length; v ++ ) {
vertices.push( geometry.vertices[ v ].clone() );
if ( v === i ) {
vertices[ vertices.length - 1 ].x *= 2;
vertices[ vertices.length - 1 ].y *= 2;
vertices[ vertices.length - 1 ].z *= 2;
}
}
geometry.morphTargets.push( { name: "target" + i, vertices: vertices } );
}
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// Add second mesh (blue cube)
var blue = new THREE.MeshLambertMaterial( { color: 'blue', morphTargets: true } );
var box2 = new THREE.BoxGeometry( 100, 100, 100 );
mesh2 = new THREE.Mesh( box2, blue );
scene.add( mesh2 );
mesh2.position.x = 150
// Create simple GUI slider to change morph influence on mesh (red cube)
var params = {
influence1: 0,
};
var gui = new dat.GUI();
var folder = gui.addFolder( 'Morph Targets' );
folder.add( params, 'influence1', 0, 1 ).step( 0.01 ).onChange( function( value ) { mesh.morphTargetInfluences[ 0 ] = value; } );
folder.open();
// Render it all
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
mesh.rotation.y += 0.01;
renderer.render( scene, camera );
}
...And here's the answer https://github.com/mrdoob/three.js/issues/12691#issuecomment-345416590. My blue mesh material had morphTargets set to true. Changing it to false corrected the issue.
var blue = new THREE.MeshLambertMaterial( { color: 'blue', morphTargets: false } );
If anyone knows why this is the case I would love to know more. The documentation on MeshLambertMaterials is sparse. Maybe this has to do with keeping material textures from freaking out when the morph is applied?
Three.js Version: 82
I'm looking at this example here: https://threejs.org/examples/#webgl_helpers
Notice that there are yellow boxes around the models. I was using version 79 and was using THREE.EdgesHelper to outline my 3D objects I made in Blender, but THREE.EdgesHelper was replaced by THREE.EdgesGeometry. I wanted to see an example of this, and the only one I could find is linked above.
I dove into the line and BoxHelper objects, but I didn't notice anything that would allow me to get rid of those yellow boxes.
JSFiddle: https://jsfiddle.net/4nbjvmpe/
Here is the code from the JSFiddle:
HTML
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
CSS
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
JavaScript
var mesh, renderer, scene, camera, controls;
init();
animate();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new THREE.OrbitControls( camera );
// ambient
scene.add( new THREE.AmbientLight( 0x222222 ) );
// light
var light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 20, 20, 0 );
scene.add( light );
// geometry
var geometry = new THREE.SphereGeometry( 5, 12, 8 );
// material
var material = new THREE.MeshPhongMaterial( {
color: 0x00ffff,
shading: THREE.FlatShading,
transparent: true,
opacity: 0.7,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// CUSTOM
// This is where I create the outline mesh.
var group = new THREE.Group();
scene.add( group );
group.updateMatrixWorld(true);
var edges = new THREE.EdgesGeometry(geometry);
var line = new THREE.LineSegments(edges);
group.add(line);
line.material.depthTest = true;
line.material.opacity = 0.25;
line.material.transparent = false;
this.outlineMesh = new THREE.BoxHelper(line);
scene.add(this.outlineMesh);
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
Simply comment this.outlineMesh = new THREE.BoxHelper(line); and scene.add(this.outlineMesh);. you will get want you want.
I need to hide the edges that are displayed in a box primitive using ThreeJS. These only are drawn when I put a texture on the faces.
I've tried with many options like wireframe=false, but the edges are still drawn.
This is the code:
var container, stats;
var camera, scene, renderer;
var canvasWidth = 500;
var canvasHeight = 500;
var windowHalfX = 100;
var windowHalfY = 100;
container = document.createElement( 'div' );
document.body.appendChild( container );
// Camera
camera = new THREE.OrthographicCamera( canvasWidth / - 2, canvasWidth / 2, canvasHeight / 2, canvasHeight / - 2, - 500, 5000 );
// Scene
scene = new THREE.Scene();
camera.position.x = 200;
camera.position.y = 200;
camera.position.z = 200;
camera.lookAt( scene.position );
// Renderer
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( "#fff" );
renderer.setSize( canvasWidth, canvasHeight );
container.appendChild( renderer.domElement );
var size = 100;
geometry = new THREE.BoxGeometry( size, size, size );
material = new THREE.MeshBasicMaterial({
color: "#0000ff",
side: THREE.DoubleSide,
wireframe: false
});
// Comment this line to paint a single color cube
material = new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture("http://upload.wikimedia.org/wikipedia/commons/8/81/Color_icon_black.png") });
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
var draw = function() {
requestAnimationFrame( draw );
renderer.render( scene, camera );
}
draw();
And a link to the example:
http://jsfiddle.net/gyss/qg4x9/
Cheers!
I've solved my problem changing this line
renderer = new THREE.CanvasRenderer();
for this other
renderer = new THREE.WebGLRenderer();
Other possible solution, as WestLangley commented above is to use this line with CanvasRenderer
material.overdraw = 0.5; // or some number between 0 and 1
When using renderer = new THREE.CanvasRenderer();
to remove the edges, a parameter (overdraw:true) needs to be added to the material definition like this:
var material = new THREE.MeshBasicMaterial( { map: texture, overdraw: true } );
Then you can use add the material to the 3D object:
mesh = new THREE.MeshBasicMaterial( geometry, material );