I'm not sure if this is an error or not, but once you call geometry.computeVertexNormals() in a THREE.BoxGeometry, and you calculate the vertex normals, I think the resulting vectors are wrong.
Take this as an example:
geometry = new THREE.BoxGeometry(50, 50, 50);
geometry.computeVertexNormals();
material = new THREE.MeshNormalMaterial();
as you see everything looks done properly, however if you rotate the X axis you see that the vectors are pointing in different directions.
mesh.rotation.x = 90 * Math.PI / 180;
Shouldn't we expect the vector to point exactly from the center of the 3 existing vectors before the geometry merge? Something like this:
If not, any explanation on why this happens?
Thanks in advance.
computeVertexNormals computes the average of the face normals of the faces connected to each vertex. For the box mesh, not all the vertices are connected to the same number of faces, so you have a different normal average. Drawing the wireframe makes this easier to see.
Related
I want to make portals with threejs by drawing an ellipse and then texture mapping a WebGlRenderTarget to its face. I have that function sort of working, but it tries to stretch the large rectangular buffer from the render target to the ellipse. What I want is to project the texture in its original dimensions onto the ellipse and just cut out anything that doesn't hit the ellipse like so:
Before Projection:
After projection:
How can this be done with threejs?
I've looked into texture coordinates, but don't understand how to use them, and even saw a projection light PR in threejs that might work?
Edit: I also watched a Sebastian Lague video on portals and saw he does this with “screen space coordinates”. Any advice on using those?
Thanks for your help!
Made a codepen available here:
https://codepen.io/cdeep/pen/JjyjOqY
UV mapping lets us specify which parts of the texture correspond to which vertices of the geometry. More details here: https://www.spiria.com/en/blog/desktop-software/understanding-uv-mapping-and-textures/
You could loop through the vertices and set the corresponding UV value.
const vertices = ellipseGeometry.attributes.position.array;
for(let i = 0; i < numPoints; i++) {
const [x, y] = [vertices[3*i], vertices[3*i + 1]];
uvPositions.push(0.5 + x * imageHeight / ((2 * yRadius) * imageWidth));
uvPositions.push(0.5 + y / (2 * yRadius));
}
ellipseGeometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvPositions, 2 ));
UV coordinates increase from (0, 0) to (1, 1) from bottom left to top right.
The above code works because the ellipse is on the x-y plane. Or else, you'll need to get the x,y values in the plane of the ellipse.
More info on texture mapping in three.js here:
https://discoverthreejs.com/book/first-steps/textures-intro/
Edit: Do note that the demo doesn't really look like a portal. For that, you'll need to move the texture based on the camera view which isn't that simple
I'm trying to make a linear regression plane visualization tool for a math project. Currently I have the math parts completed, but I am not sure how to graph the plane. I have a equation in the form of z=C+xD+yE, where C, D, and E are known constants. How do I graph the plane using these information? Thanks.
github page: https://saxocellphone.github.io/LAProject/
z=C+xD+yE
This equation gives full information about the plane. What else you need to graph (plot, draw?) it? Probably it depends on your graphic software possibilities.
Canonical form of given equation:
xD + yE - z + C = 0
Normal to the plane is (D, E, -1). Distance to the coordinate origin Abs(C)/Sqrt(D^2+E^2+1).
Plane intersects coordinate axes at values (-C/D), (-C/E), (C)
I see your problem is not with math, but with three,
as WestLangley pointed out in his comment you can play with rotations etc. or create a simple triangle which is the easiest way
since you have your equation for the plane create 3 points to form a triangle
// z=C+xD+yE
// i assume here that the plane is not aligned with any axis
// and does not pass through the origin, otherwise choose the points in another way
var point1 = new THREE.Vector3(-C/D,0,0);//x axis intersection
var point2 = new THREE.Vector3(0,-C/E,0);//y axis intersection
var point3 = new THREE.Vector3(0,0,C);//z axis intersection
now form a new geometry as in How to make a custom triangle in three.js
var geom = new THREE.Geometry();
geom.vertices.push(point1);// adding vertices to geometry
geom.vertices.push(point2);
geom.vertices.push(point3);
// telling geometry that vertices 0,1,2 form a face = triangle
geom.faces.push( new THREE.Face3( 0, 1, 2 ) );
create a simple material and add it to a scene
var material = new THREE.MeshBasicMaterial({
color: 0xff0000, // RGB hex color for material
side: THREE.DoubleSide // do not hide object when viewing from back
});
scene.add(new THREE.Mesh(geometry,material));
that should get you going, you can add another triangles, or make it larger with choosing points that are further apart
Relevant codepen:
http://codepen.io/OpherV/pen/yNebep
In my game I have a model of an alien tree.
For each face of this tree I generate a pyramid (CylinderGeometry with 4 faces), and position it at the center of the face. Then I wish for this pyramid to be perpendicular to the face, so that I'll get a tree with spikes.
I've tried achieving this with object.lookAt and point it at the face normal, but weird things happen. For example:
If I add
cylinderGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
The shape in the middle works as expected, but the rest is still distorted
What is the proper way to get my spiked tree?
Bonus question
How would I go about merging these spikes to the original geometry after proper creation and orientation so that I don't have so many separate objects?
You want to create cylinder and point it in a particular direction. One way to do that is to use the following pattern:
Create the geometry.
var cylinderGeometry = new THREE.CylinderGeometry( 1, 10, 25, 4, 1 );
Translate the geometry so the base sits at the origin.
cylinderGeometry.translate( 0, 12.5, 0 );
Rotate the geometry so the top points in the direction of the positive-Z axis.
cylinderGeometry.rotateX( Math.PI / 2 );
Create the mesh.
var cylinder = new THREE.Mesh( cylinderGeometry , characterSkinMaterial );
Objects in three.js are by default "looking" in the direction of their local positive-Z axis. (Except the camera, which looks down its negative-Z axis.)
Tell the cylinder to "look" in the direction you want. Since we have transformed the geometry, this will make the top of the cylinder point in the desired direction.
cylinder.lookAt( face.normal );
Now place the cylinder wherever you want it.
cylinder.position.copy( centerPoint );
obj.add( cylinder );
three.js r.91
I am relatively new to three.js and am trying to position and manipulate a plane object to have the effect of laying over the surface of a sphere object (or any for that matter), so that the plane takes the form of the object surface. The intention is to be able to move the plane on the surface later on.
I position the plane in front of the sphere and index through the plane's vertices casting a ray towards the sphere to detect the intersection with the sphere. I then try to change the z position of said vertices, but it does not achieve the desired result. Can anyone give me some guidance on how to get this working, or indeed suggest another method?
This is how I attempt to change the vertices (with an offset of 1 to be visible 'on' the sphere surface);
planeMesh.geometry.vertices[vertexIndex].z = collisionResults[0].distance - 1;
Making sure to set the following before rendering;
planeMesh.geometry.verticesNeedUpdate = true;
planeMesh.geometry.normalsNeedUpdate = true;
I have a fiddle that shows where I am, here I cast my rays in z and I do not get intersections (collisions) with the sphere, and cannot change the plane in the manner I wish.
http://jsfiddle.net/stokewoggle/vuezL/
You can rotate the camera around the scene with the left and right arrows (in chrome anyway) to see the shape of the plane. I have made the sphere see through as I find it useful to see the plane better.
EDIT: Updated fiddle and corrected description mistake.
Sorry for the delay, but it took me a couple of days to figure this one out. The reason why the collisions were not working was because (like we had suspected) the planeMesh vertices are in local space, which is essentially the same as starting in the center of the sphere and not what you're expecting. At first, I thought a quick-fix would be to apply the worldMatrix like stemkoski did on his github three.js collision example I linked to, but that didn't end up working either because the plane itself is defined in x and y coordinates, up and down, left and right - but no z information (depth) is made locally when you create a flat 2D planeMesh.
What ended up working is manually setting the z component of each vertex of the plane. You had originaly wanted the plane to be at z = 201, so I just moved that code inside the loop that goes through each vertex and I manually set each vertex to z = 201; Now, all the ray start-positions were correct (globally) and having a ray direction of (0,0,-1) resulted in correct collisions.
var localVertex = planeMesh.geometry.vertices[vertexIndex].clone();
localVertex.z = 201;
One more thing was in order to make the plane-wrap absolutely perfect in shape, instead of using (0,0,-1) as each ray direction, I manually calculated each ray direction by subtracting each vertex from the sphere's center position location and normalizing the resulting vector. Now, the collisionResult intersection point will be even better.
var directionVector = new THREE.Vector3();
directionVector.subVectors(sphereMesh.position, localVertex);
directionVector.normalize();
var ray = new THREE.Raycaster(localVertex, directionVector);
Here is a working example:
http://jsfiddle.net/FLyaY/1/
As you can see, the planeMesh fits snugly on the sphere, kind of like a patch or a band-aid. :)
Hope this helps. Thanks for posting the question on three.js's github page - I wouldn't have seen it here. At first I thought it was a bug in THREE.Raycaster but in the end it was just user (mine) error. I learned a lot about collision code from working on this problem and I will be using it later down the line in my own 3D game projects. You can check out one of my games at: https://github.com/erichlof/SpacePong3D
Best of luck to you!
-Erich
Your ray start position is not good. Probably due to vertex coordinates being local to the plane. You start the raycast from inside the sphere so it never hits anything.
I changed the ray start position like this as a test and get 726 collisions:
var rayStart = new THREE.Vector3(0, 0, 500);
var ray = new THREE.Raycaster(rayStart, new THREE.Vector3(0, 0, -1));
Forked jsfiddle: http://jsfiddle.net/H5YSL/
I think you need to transform the vertex coordinates to world coordinates to get the position correctly. That should be easy to figure out from docs and examples.
Well, the title pretty much states it. I want to be able to draw a curve on a surface in Web GL. So for example, I'd like to draw a parabola on the surface of a sphere.
x = cos(theta)sin(phi); y = sin(theta)sin(phi); z = cos(phi).
If you make theta = theta(t) and phi = phi(t), you can draw curves on the surface.
I guess lines on shapes is what I need. Anyone know if that's possible in Web GL?
A parabola is the set of point of the plane that have the same distance from a line and a point (called focus). The point here is what do you mean by "line" on a sphere. Remember that a parabola extends to infinity, bu you can't do that on a sphere, unless you define some particular metric on it.
Anyway, you gave use a parametrization of the sphere, in terms on theta and phi. That's good. If you want to define a curve on the surface, you should have a bind between theta and phi, for example
phi = theta ^ 2
would draw something that could be defined as a "parabola" in some way, i.e. the projection on the sphere, given by the parametrization, of its representation on a plane.
Are you looking for help with how to do this in WebGL? In this case, take a look at this example
http://dl.dropbox.com/u/17612367/OpenGL%20to%20WebGL/example%202.3.1%20-%20line%20graph/code/index.html
you would basically load the positions into a vector and draw it using drawArrays with LINELOOP or something... See this cheatsheet for arguments or google the drawArrays function for more info:
http://www.nihilogic.dk/labs/webgl_cheat_sheet/WebGL_Cheat_Sheet.pdf
Good Luck!