I added a WireframeHelper to my scene like this:
var wfh = new THREE.WireframeHelper(mesh, 0x000000);
wfh.material.linewidth = 2;
scene.add(wfh);
How do I change the wireframeHelpers position?
I tried:
wfh.position.x += 10;
and
wfh.matrixAutoUpdate = true
wfh.applyMatrix(new THREE.Matrix4().makeTranslation(10, 0, 0));
The second one moves my wireframeHelper but not to the position it should move.
The WireframeHelper will move, automatically, when the mesh is moved. Move the mesh, instead.
If you want to see only the helper, you can set mesh.visible = false, but the mesh must be included in the scene graph.
three.js r.65
Related
I try to make a very simple program with a clickable 3d object in Threejs. My code is based on
https://threejs.org/examples/#webgl_interactive_cubes
It works when I click on the object (although the resulting array contains the object twice, I assume because the ray intersects it when entering it and when exiting it).
But when I click in an area just surrounding the object raycaster.intersectObjects returns the object although it should return an empty array.
What am I doing wrong? Why is the object also intersected when I click next to it and not on it? And is an object always included twice in the intersect array because of the ray entering and exiting it?
A working example of the code is here:
https://codepen.io/ettir_deul/pen/PoGLNZx
(open the console to see the intersect array after you clicked on the screen)
And the code looks like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="libs/three.min.js.r116.1"></script>
</head>
<body>
<div id="threejsCanvas"></div>
<script>
let scene, center, camera, renderer, raycaster;
const mouse = new THREE.Vector2();
const threejsCanvas = document.getElementById("threejsCanvas");
init3d();
function init3d(){
scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAEE);
center = new THREE.Vector3(0, 0, 0);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(center.x, center.y, center.z+1);
camera.lookAt(center);
buttonMesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 0.2), new THREE.MeshBasicMaterial({map : null, color: 0x008844, wireframe: false, side: THREE.DoubleSide}));
buttonMesh.position.set(center.x, center.y, center.z);
scene.add(buttonMesh);
buttonMesh.objId = "buttonMesh";
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
threejsCanvas.appendChild(renderer.domElement);
renderer.render(scene, camera);
raycaster = new THREE.Raycaster();
document.addEventListener('click', onMouseClick, false);
}
function onMouseClick(event){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
console.log(intersects);
}
</script>
</body>
</html>
the reason for getting the object twice is the
side: THREE.DoubleSide
in the material. You can use THREE.FrontSide
The reason for picking the object when the mouse is close is the canvas and window dimensions not being equal.
If you add :
body{
padding: 0;
margin: 0;
}
in the CSS it is fixed.
Here is a codepen that works.
Two results - is OK. You got 2 faces of mesh. Each one intersects ray. Check result intersection properties: face, faceIndex, point.
Raycaster is not precise and pretty slow. You can use its 'params' to change precision (see https://threejs.org/docs/#api/en/core/Raycaster). I suggest using GPUPicker. It is precise and super fast. Check here:
https://github.com/brianxu/GPUPicker
Edit: Yes you can change material to avoid 2 intersections. But it is often it is not acceptable for surfaces.
Edit: Yes precision settings affects only points and line picking. But GPUPicker can pick exact rendering result of points (shader effects, symbols with transparency). But standard raycaster - can't.
I imported a model and found that shadow only show in a small area(green area in the picture). What can I do to let all objects show their shadow.
Here is my code.
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
light.castShadow = true;
light.shadow.camera.near = 0.01; // same as the camera
light.shadow.camera.far = 1000; // same as the camera
light.shadow.camera.fov = 50; // same as the camera
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
scene.add( light );
Thanks!!
EDIT:
I add gui to change light.shadow.camera.top / light.shadow.camera.bottom / light.shadow.camera.left / light.shadow.camera.right, but nothing happens.
var gui = new dat.GUI();
gui.add( light.shadow.camera, 'top' ).min( 1 ).max( 100000 ).onChange( function ( value ) {
light.shadow.camera.bottom = -value;
light.shadow.camera.left = value;
light.shadow.camera.right = -value;
});
That's happening because directional lights use an OrthographicCamera to draw a shadowmap to cast shadows. If there are objects outside the view of this camera, it won't be able to calculate their shadows, and will have the effect you're seeing outside the green box. If you want to extend the area that this camera covers, you can modify the .left .right .top .bottom properties of this shadow camera to cover your entire scene. I'm using a box of 100 units in the example below;
var side = 100;
light.shadow.camera.top = side;
light.shadow.camera.bottom = -side;
light.shadow.camera.left = side;
light.shadow.camera.right = -side;
... but you can change the dimensions to whatever you need. Keep in mind that .fov does nothing in your example code because ortho cameras don't use the field-of-view property.
All right, it would be great if you could show me a live example of your code as I have fixed this before for a project in boxelizer.com
The issue can be fixed by changing the light.shadow.camera.left, right, bottom and top properties as suggested before, however we might not be able to see what the effective area of our shadow might be and hence we might be really close to fixing it but not at all. My suggestion is using a helper momentarily just to see the effective shadow area of your light with:
var shadowHelper = new THREE.CameraHelper( light.shadow.camera );
scene.add( shadowHelper );
You are also welcome to see all the code I used in the link I referenced to.
Here's a function I use to set the shadowMap dimensions and coverage area:
//sz is the size in world units that the shadow should cover. (area)
// mapSize is the width,height of the texture to be used for shadowmap (resolution)
var setShadowSize=(light1, sz, mapSz)=>{
light1.shadow.camera.left = sz;
light1.shadow.camera.bottom = sz;
light1.shadow.camera.right = -sz;
light1.shadow.camera.top = -sz;
if(mapSz){
light1.shadow.mapSize.set(mapSz,mapSz)
}
}
setShadowSize(myLight,15.0,1024);
I try to include, into a main window (representing sphere), a subwindow representing a zoomed view of this sphere.
For this moment, I can display a right-bottom subwindow containing the three axes of main scene. These axes are rotating the same way I make rotate the sphere with mouse.
Now, I would like to display, into this subwindow, a zoomed view of the sphere instead of having the subwindow with 3D axes.
From Can multiple WebGLRenderers render the same scene?, we can't use a second time the THREE.WebGLRenderer() for the subwindow.
From How to render the same scene with different cameras in three.js? ,a solution may be to use setViewport function but I don't know if I can display the zoom of sphere in this subwindow.
I tried to do in render() function :
function render() {
controls.update();
requestAnimationFrame(render);
zoomCamera.position.copy(camera.position);
zoomCamera.position.sub(controls.target);
zoomCamera.position.setLength(500);
zoomCamera.lookAt(zoomScene.position );
// Add Viewport for zoomScene
renderer.setViewport(0, 0, width, height);
renderer.render(scene, camera);
zoomRenderer.setViewport(0, 0, 200 , 200);
zoomRenderer.render(zoomScene, zoomCamera);
}
From your advices, is it possible technically to have 2 object in the same time (one on main window and the other on right-bottom subwindow) ?
Can I solve my issue with setViewport ?
And if someone could give documentation on setViewport, this would be great.
Thanks in advance.
UPDATE :
#WestLangley , thanks, I did your modifications (with a unique scene) but the content of inset does not appear.
I think that issue comes from the fact that I don't know how to make the link between the container of inset and the drawing of this inset.
For example, into the link above, I tried :
...
// Add sphere to main scene
scene.add(sphere);
camera.position.z = 10;
var controls = new THREE.TrackballControls(camera);
// If I include these two lines below, nothing appears
// zoomContainer = document.getElementById('zoomContainer');
// zoomContainer.appendChild(renderer.domElement);
// Zoom camera
zoomCamera = new THREE.PerspectiveCamera(50, zoomWidth, zoomHeight, 1, 1000);
zoomCamera.position.z = 20;
zoomCamera.up = camera.up; // important!
// Call rendering function
render();
function render() {
controls.update();
requestAnimationFrame(render);
zoomCamera.position.copy(camera.position);
zoomCamera.position.sub(controls.target);
zoomCamera.position.setLength(camDistance);
zoomCamera.lookAt(scene.position);
// Add zoomCamera to main scene
scene.add(zoomCamera);
// render scene
renderer.clear();
renderer.setViewport(0, 0, width, height);
renderer.render(scene, camera);
// render inset
renderer.clearDepth(); // important!
renderer.setViewport(10, height - zoomHeight - 10, zoomWidth, zoomHeight);
renderer.render(scene, zoomCamera);
}
Given that I have only one renderer, I don't knwo how to assign the "inset" container to the renderer of Viewport (i.e with the good renderer.setViewport)
Would you have got a workaround ?
You want to render another view of your scene in an inset window.
Create one scene, and two cameras.
Make sure you set autoClear to false when you instantiate the renderer.
renderer.autoClear = false;
Then, in your render loop, use this pattern
// render scene
renderer.clear();
renderer.setViewport( 0, 0, window.innerWidth, window.innerHeight );
renderer.render( scene, camera );
// render inset
renderer.clearDepth(); // important!
renderer.setViewport( 10, window.innerHeight - insetHeight - 10, insetWidth, insetHeight );
renderer.render( scene, camera2 );
three.js r.75
I'm creating a parent/child hierarchy between three objects using the ".add()" method in Three.js, (RE: 71). Parenting a two objects poses no problems. However, When I attempt to create a three level hierarchy, the middle child jumps to an offset equivalent to the offset of it's parent from the origin.
This is the hierarchy:
Red Box (parent of Green Box) -> Green Box (parent of Blue Box) -> Blue Box
Below is an image of how the boxes should be positioned before and after the parenting:
This is an Image if how the boxes have been repositioned when parented:
The relevant code looks like this:
var testObject_G1 = new THREE.BoxGeometry(50, 100, 100);
var testObject_G2 = new THREE.BoxGeometry(100, 100, 50);
var testObject_G3 = new THREE.BoxGeometry(50, 100, 100);
var testObject_MR = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var testObject_MG = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var testObject_MB = new THREE.MeshBasicMaterial({ color: 0x0000ff });
var testObject_Mesh = new THREE.Mesh(testObject_G1, testObject_MR);
testObject_Mesh.position.x = -100;
var testObject_Mesh2 = new THREE.Mesh(testObject_G2, testObject_MG);
testObject_Mesh2.position.x = 0;
var testObject_Mesh3 = new THREE.Mesh(testObject_G3, testObject_MB);
testObject_Mesh3.position.x = 100;
testObject_Mesh2.add( testObject_Mesh3 );
testObject_Mesh.add( testObject_Mesh2 );
scene.add( testObject_Mesh );
There is a JS Fiddle here: jumping second child
Thank you in advance.
what you see is correct. In a hierarchy the child will inherit the parents transform (inherit is simplification; their matrices will be multiplied but you only have translation defined so the values will be added). So testMesh (red) will appear at x=-100, testMesh2 (green) will also appear at x=-100 (since its x transform is 0 and will inherit its parent transform) and testMesh3 (blue) will appear at x=0 (since -100+0+100=0).
Add this to your fiddle to see the relative transformations.
scene.add( new THREE.AxisHelper( 100 ));
I'm trying to create a rectangle and a pointtext element, where the rectangle will be the
text element's container.
Without text element, everything works fine. When text element inserted, rectangle is pushed away. Well, rectangle is displayed at the correct position, but the points where it receives the events are pushed away.
Please see http://jsbin.com/abejim/1
Rectangle's visibility should increase when hovered. Hovering does not affect, but when mouse moved to 580,280 and around , it's visibility increases.
Any suggestions?
That jsbin seems to be working fine for me in Firefox. The rectangle is displayed in the correct location and hovering over the rectangle makes it highlight. Possibly the paper.js code has updated since you asked the question.
It looks like you asked the same question on the paper.js mailing list. For future reference here, the response was:
pointtext takes relative coordinates and you r trying to give absolute coordinates.
try this one:
var size = new paper.Size(125, 75); //SM size to paper size
var rectangle = new paper.Rectangle({ x: 0, y: 0 }, size); //(top_left, bottom_right)
var cornerSize = new paper.Size(10, 10); //rounding of edges
var shape = new paper.Path.RoundRectangle(rectangle, cornerSize);
shape.strokeWidth = 3;
shape.strokeColor = '#525252';
shape.fillColor = '#FFFFFF';
shape.name = 'shape';
var stateNameTxt = new paper.PointText(10, 65);
stateNameTxt.content = state_name;
stateNameTxt.name = 'stateNameTxt';
var state = new paper.Group(); //create group
state.name = state_name;
state.opacity = 0.8;
state.addChild(shape); //add shape to grpup
state.addChild(stateNameTxt); //add pointtext to group