I have a THREE.Line object that is used to orchestrate a custom grid. Now the problem I am having is when using a raycast to intersectObjects when the users mouse might goes over this grid, the intercept only succeeds if the users mouse goes "directly" over any of the grid lines.
Please see the attached photo:
So ultimately what I am am trying to accomplish is, how can the fill in the space between the lines with an "invisible" face or even shape so that when the users mouse goes over this grid, I can trigger an action. Here is currently what I am doing:
var response = this.buildGridGeometry(modelStep,modelSize,gridtype);
var geometry = response['geometry'];
geometry.computeBoundingBox();
// # Setup the material
var material = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } );
// # Draw each individual line
var line = new THREE.Line( geometry, material, THREE.LinePieces );
Any suggestions?
The easiest solution would be to create a custom Geometry with the same shape as the grid, then make it's initial position, rotation and scale match with the grid (if it isn't already). After that you can add the custom Geometry as a child to the grid so that it moves correspondingly. Last step is to make the custom Geometry mesh invisible by setting its property .visible = false;.
Now you just need to use the raycasters intersectsObject() on the custom Geometry mesh instead, and when it does intersect, you know that the grid is intersected aswell.
Related
I am using Three.js to model a home. The idea is that I can show the home to an architect and discuss it further. The project is supposed to be walkable, so I put together it all on this site. If you visit the site to view the code, use arrow keys to move horizontally relative to the grass, use W/S to move up/down, and A/D to yaw view.
https://bsdillon.github.io/cs200_Spring2020/ThreeJs/solarhouse.html
I was able to make panels (cubes actually) and put textures on them. Everything looks great except that the walls look different depending on the direction of view. See the image below. I added a red line and yellow triangle to help make the geometry more obvious. The image on the left shows the external view of a panel with a clapboard exterior and an open doorway on the left. The image on the right shows the same panel viewed from inside the structure. You can see that from inside the door is still on the left (it should appear on the right) and the stick frame interior also appears to be reversed.
I never noticed this before, but it appears to be the standard for the way I set up these panels. Apparently I'm doing something wrong. The question is how can I show the texture so that the views will be consistent.
var materialArray = new Object();//I set up an object I set up to store materials
function makeMaterial2(filename, repeatX, repeatY)//a method for creating materials from an image file
{
var m = new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( filename ), transparent: true, opacity: 0.9, color: 0xffffff });
return m;
}
//two examples of the material creation I implemented
materialArray["rDoor"] = makeMaterial2("doorRight.png",false,false);
materialArray["crDoor"] = makeMaterial2("doorRightClapboard.png",false,false);
function drawPanel2(x,y,z,x2,y2,z2,str)//this funciton creates a cube with the material in question
{
var cubegeometry = new THREE.BoxGeometry(Math.abs(x-x2),Math.abs(y-y2),Math.abs(z-z2));
var cube = new THREE.Mesh(cubegeometry, materialArray[str]);
cube.position.set((x+x2)/2,(y+y2)/2,(z+z2)/2);
return cube;
}
//adding the panels to the scene with the materials
scene.add(drawPanel2(-10,level,front,0,level+height,front,"rDoor"));
scene.add(drawPanel2(-10,level,front+margin,0,level+height,front+margin,"crDoor"));
You are using a simple BoxGeometry that has standard UV texture coodinates. A texture mapped on this geometry will look the same from all sides (see box example). However, you want that the open space of the door is at the same position. You could do one of the following to achieve that:
Apply different materials and textures to different sides of the box. The 6 sides of BoxGeometry are already indexed for multi material usage.
a) You'll have to flip a texture in an image editing software and save it separately. Load these multiple textures.
b) Clone the texture and set texture.wrapS = THREE.RepeatWrapping; texture.repeat.x = - 1; to flip it (How to flip a Three.js texture horizontally).
Create an array of 6 materials each with its according texture and pass it to the Mesh.
Change the UV texture coordinates of BoxGeometry, such that one side will show the texture flipped.
Your boxes are flat. 4 sides aren't visible. Instead of a BoxGeometry, you could also create a PlaneGeometry. Set material.side: THREE.DoubleSide such that the plane will be visible from both sides. Following this approach, you'll have to rework your drawPanel2 method, because you can't just flatten one side of the geometry, but you have to rotate the plane according the intended orientation of the panel.
This is the basic example from the Threejs documentation
function initSphere(){
const geometry = new THREE.SphereGeometry( 150, 14, 14 );
const material = new THREE.MeshBasicMaterial( {color: 0xFF0000, vertexColors: 0xFFFFFF} );
const sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );
}
It creates a red sphere which is what I wanted, but I can't really see the sphere and it's edges because it just looks like a red circle. I was thinking that changing the edges to a white color would help make the effect I want, but I can't seem to know how to ask this question to solve it.
Can anyone help?
First, your shape is "just a red circle" because you are using MeshBasicMaterial. This material is simply a color and does not include any kind of shading or highlights--it doesn't even need a light source! Every bit of the shape will be rendered as 0xff0000.
If you want shading/highlights, You will need to use a more complex material like MeshPhongMaterial, MeshLambertMaterial, or MeshStandardMaterial. Because these are shaded, you will need to include a light in your scene.
Secondly, the vertexColors property does not change the color of the "edges." Instead, it is a Boolean that indicates whether vertex colors are used to color the Mesh.
If you want to show edges, you could try using EdgesGeometry to define a secondary shape.
I want to add a section in one corner of my main view in which I display a small version of the main view.
I am programming a webpage using JavaScript with three.js. I have a main view window in which I display some geometries. There are rotatable and movable using OrbitControls.
In the corner of my main view I want an separate section, in which I can display a small cube, which rotates the same way my main-view rotates. But I shall not zoom in or out if I zoom the main view.
var orientationGeometry = new THREE.Mesh( geometry, material );
camera.add( orientationGeometry );
// in animate function:
orientationCube.rotation.x = controls.getPolarAngle();
orientationCube.rotation.y = controls.getAzimuthalAngle();
This rotates the small cube correctly and by adding this cube as a child to the camera it stays fixed on the screen. But when I zoom in or out this small Box as well zooms away from the camera.
Is there a way to make an extra section like shown in the attached image?
To render a second viewport on the screen you can enable WebGLRenderer.ScissorTest, set the desired scissor, scale the viewport accordingly and render the scene again. Don't forget to clearDepth() or nothing will get rendered.
Now, in order to get a different camera behavior, you need to add a second camera to the scene and update it depending on your needs. If you want it to rotate and move just like the full-screen camera, you need to update those parameters.
In order to keep a fixed zoom state, you can get the normalized position of the camera and multiply by the set distance you want to use.
function animate() {
// render full scene.
// ...
// setup scissor viewport.
renderer.clearDepth(); // important!
renderer.setScissorTest( true );
renderer.setScissor( 20, 20, insetWidth, insetHeight );
renderer.setViewport( 20, 20, insetWidth, insetHeight );
// update second camera.
camera2.position.copy( camera.position );
camera2.position.normalize().multiplyScalar( distance );
camera2.quaternion.copy( camera.quaternion );
// render small scene.
renderer.render( scene, camera2 );
renderer.setScissorTest( false );
}
Here's a working example https://jsfiddle.net/qwb39spx/
I am looking to produce an effect very similar to the following example:
https://threejs.org/examples/?q=trails#webgl_trails
However, I would like to make the old trail fade into the background over time -- rather than just persist into an increasingly messy screen.
It seems that the following code will allow you to draw over previous frames without clearing them:
renderer = new THREE.WebGLRenderer( { preserveDrawingBuffer: true } );
renderer.autoClearColor = false;
But I am unsure how to make each frame fade into the background as new frames a drawn on top. It seems like painting over the screen with a somewhat transparent color would work, but I'm not sure how to approach that.
There's now an example using the postprocessing EffectComposer and AfterImagePass.
https://threejs.org/examples/webgl_postprocessing_afterimage.html
You could easily take the example you showed above, and create a very faint black plane that sits directly in front of the camera:
// Make highly-transparent plane
var fadeMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: 0.01
});
var fadePlane = new THREE.PlaneBufferGeometry(1, 1);
var fadeMesh = new THREE.Mesh(fadePlane, fadeMaterial);
// Create Object3D to hold camera and transparent plane
var camGroup = new THREE.Object3D();
var camera = new THREE.PerspectiveCamera();
camGroup.add(camera);
camGroup.add(fadeMesh);
// Put plane in front of camera
fadeMesh.position.z = -0.1;
// Make plane render before particles
fadeMesh.renderOrder = -1;
// Add camGroup to scene
scene.add(camGroup);
This will make sure that every time the scene is rendered, it'll slowly fade the previously-rendered particles to black. You'll need to play with the opacity of fadeMaterial and position of fadeMesh to get the desired effect.
renderOrder = -1; makes sure that you render the plane first (fading out previous particles), and then render the particles, to avoid covering the newest ones.
I have a really simple scene which has one .dae mesh in it, and a 7000*7000 plane underneath the mesh. I'd like it to be lit by a high SpotLight, so the mesh throws a shadow on the ground. But, something seems to be broken! No matter how high I put the SpotLight, it never lights up the plane! Also, it lights the mesh up only a little, while it is in a small square (perimeter).
You can see the situation here:
As soon as I move the mesh (a monster) around, it wont be lit anymore.
This is how I instantiate the light:
// create a spotlight
self.spotLight = new THREE.SpotLight();
// set its position
self.spotLight.position.y = 1000; //I recon it needs to be relatively high so it lights up everything
self.spotLight.position.x = 0; //(0, 0) are the coordinates where the mesh is spawned, and are the center of the plane
self.spotLight.position.z = 0;
self.spotLight.castShadow = true;
This is how the plane is made:
//The plane.
self.plane = new THREE.Mesh(new THREE.PlaneGeometry(self.groundSize, self.groundSize), new THREE.MeshLambertMaterial({color: 0x5C8A00}));
self.plane.receiveShadow = true;
self.plane.position.x = 0;
self.plane.position.y = -26;
self.plane.position.z = 0;
Also, here's another picture, this time, I've added a lot of PointLights:
You can see how the shadow still disappears!
Now, what am I doing wrong here? AFAIK, light should disperse equally in all directions! And also, there is another problem, I seem to be unable to add multiple SpotLights on the scene! Everything slows down completely if I do so - is this intended? Maybe it's because I enabled shadows on all of them...
#Neil, the same thing happens in your code as well!
I have created a jsfiddle showing a plane with Lambert material and a rotating cube that is casting a shadow, maybe you can see what is different to yours.
edit
Try playing about with some of the params, I can stop the clipping on my demo with:
spotLight.shadowCameraFov = 70;
update demo and moving demo