How to get smooth shading with bufferGeometry in three js? - javascript

I am making a cube sphere with LOD, and I ran into a little problem the normal that I was generating is by this code block.
// VectorVertices is an array of Vector3
let VectorNormals = new Array(this.VectorVertices.length);
for (let i = 0; i < VectorNormals.length; i++) {
VectorNormals[i] = new THREE.Vector3();
}
for (let i = 0; i < this.Triangles.length; i += 3) {
let vertexIndexA = this.Triangles[i];
let vertexIndexB = this.Triangles[i + 1];
let vertexIndexC = this.Triangles[i + 2];
let pointA = this.VectorVertices[vertexIndexA];
let pointB = this.VectorVertices[vertexIndexB];
let pointC = this.VectorVertices[vertexIndexC];
pointB.sub(pointA);
pointC.sub(pointA);
let vertexNormal = new THREE.Vector3().crossVectors(pointB, pointC).normalize();
VectorNormals[vertexIndexA].add(vertexNormal);
VectorNormals[vertexIndexB].add(vertexNormal);
VectorNormals[vertexIndexC].add(vertexNormal);
}
for (let i = 0; i < VectorNormals.length; i++) {
VectorNormals[i].normalize();
this.Normals.push(VectorNormals[i].x, VectorNormals[i].y, VectorNormals[i].z);
}
The this.Normals is then set to a bufferGeometry. I am creating the mesh with MeshPhongMaterial.
The normals between the neighbouring faces weren't calculated properly, and I don't what's going wrong. I apologize for my grammar. Thanks!
EDIT: Showing my image problem This is the result I am getting

You're trying to make a smooth sphere, but you are assigning all of the normals of a triangle to the value of the face normal, which is what you're calculating by subtracting and crossing the vertex values. (Note: You may need to be careful regarding the direction of the crossed vector! Ensure it's pointing in the same direction as your original vertex vectors!)
To make a smooth surface, the normals of adjoining vetices need to be the same. So if you have two triangles:
A -- C
| / |
| / |
B -- D
Then to make the transition from ABC to DCB a smooth one, the normals at B and C must be the same. In the case of a sphere, they should also be the average of all surrounding face normals, which ensures a smooth transition in all directions.
Actually for a sphere, if the vertices all originate from the geometric origin, then all you have to do is normalize the vertex value, and that is the normal for that vertex.

Related

THREE.js Detecting adjacent faces from a raycaster intersection point

I have a Mesh created with a BufferGeometry.
I also have the coordinates of where my mouse intersects the Mesh, using the Raycaster.
I am trying to detect faces within(and touching) a radius from the intersection point.
Once I detect the "tangent" faces, I then want to color the faces. Because I am working with a BufferGeometry, I am manipulating the buffer attributes on my geometry.
Here is my code:
let vertexA;
let vertexB;
let vertexC;
let intersection;
const radius = 3;
const color = new THREE.Color('red');
const positionsAttr = mesh.geometry.attributes.position;
const colorAttr = mesh.geometry.attributes.color;
// on every mouseMove event, do below:
vertexA = new THREE.Vector3();
vertexB = new THREE.Vector3();
vertexC = new THREE.Vector3();
intersection = raycaster.intersectObject(mesh).point;
// function to detect tangent edge
function isEdgeTouched(v1, v2, point, radius) {
const line = new THREE.Line3();
const closestPoint = new THREE.Vector3();
line.set(v1, v2);
line.closestPointToPoint(point, true, closestPoint);
return point.distanceTo(closestPoint) < radius;
}
// function to color a face
function colorFace(faceIndex) {
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.setXYZ(faceIndex * 3 + 0, color.r, color.g, color.b);
colorAttr.needsUpdate = true;
}
// iterate over each face, color it if tangent
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)
|| isEdgeTouched(vertexA, vertexB, point, radius)) {
colorFace(i);
}
While this code works, it seems to be very poor in performance especially when I am working with a geometry with many many faces. When I checked the performance monitor on Chrome DevTools, I notices that both the isEdgeTouched and colorFace functions take up too much time on each iteration for a face.
Is there a way to improve this algorithm, or is there a better algorithm to use to detect adjacent faces?
Edit
I got some help from the THREE.js slack channel, and modified the algorithm to use Three's Sphere. I am now no longer doing "edge" detection, but instead checking whether a face is within the Sphere
Updated code below:
const sphere = new THREE.Sphere(intersection, radius);
// now checking if each vertex of a face is within sphere
// if all are, then color the face at index i
for (let i=0; i < (positionsAttr.count) /3); i++) {
vertexA.fromBufferAttribute(positionsAttr, i * 3 + 0);
vertexB.fromBufferAttribute(positionsAttr, i * 3 + 1);
vertexC.fromBufferAttribute(positionsAttr, i * 3 + 2);
if (sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)
&& sphere.containsPoint(vertexA)) {
colorFace(i);
}
When I tested this in my app, I noticed that the performance has definitely improved from the previous version. However, I am still wondering if I could improve this further.
This seem to be a classic Nearest Neighbors problem.
You can narrow the search by finding the nearest triangles to a given point very fast by building a Bounding Volume Hierarchy (BVH) for the mesh, such as the AABB-tree.
BVH:
https://en.m.wikipedia.org/wiki/Bounding_volume_hierarchy
AABB-Tree:
https://www.azurefromthetrenches.com/introductory-guide-to-aabb-tree-collision-detection/
Then you can query against the BVH a range query using a sphere or a box of a given radius. That amounts to traverse the BVH using a sphere/box "query" which is used to discard quickly and very early the Bounding Volume Nodes that does not clip the sphere/box "query". At the end the real distance or intersection test is made only with triangles whose BV intersect the sphere/box "query", typically a very small fraction of the triangles.
The complexity of the query against the BVH is O(log n) in contrast with your approach which is O(n).

Spline offset in three.js

I have to make 3d text from font glyphs. Yes, I know that I can use TextGeometry, but I need to draw this manually because I need to do offset on font splines.
At this moment I have splines with their points and I can draw letters.
From points I know: previousPoint, currentPoint and nextPoint and I need to compute bisector between previous and next points and I have no idea to do that.
Or if is another way to move spline points outer of initial position to do offset.
My idea:
Thank you!
EDIT:
With yours answers I obtained correct values for each splines from font, but only at 'o' and '0' I have a problem.
This method draw a weird tangent in bottom of the letter and I don't know to resolve this problem..
here is the result
Anybody know how to resolve this?
EDIT 2:
Finally I finished my project. And this is the final product ( .stl exporter )
final offset
Thank you for yours answers!
There are the result from: x = (prev_x + next_x) / 2 and y = (prev_y + next_y) / 2
wrong result
desired result
Here is my code where let points is all the points from the path:
getPathPoints(path) {
let points = path.getPoints();
console.log(points)
for (let i = 0; i < points.length; i++) {
let A = points.extended(i - 1); // previousPoint => where extends is a custom array prototype
let B = points.extended(i); // currentPoint
let C = points.extended(i + 1); // nextPoint
let x = (A.x + C.x) / 2;
let y = (A.y + C.y) / 2;
let bisector = new THREE.Vector2(x,y);
console.log(bisector);
}
}
What splines describe your glyphs?
I know that TTF fonts use quadratic Bezier curves. For Bezier direction vector in starting and ending points has direction onto control point. So difference
S = ControlPoint[1] - ControlPoint[0]
represents direction in the starting point, difference
E = ControlPoint[1] - ControlPoint[2]
represents direction in the ending point.
Normalize these vectors for two neighbour curves and add them - now you have bisector vector.
Bisector = E(i).Normalized + S(i+1).Normalized

THREE.js Color and position attributes of bufferGeometry use different vertices, and are hard to compare

I have a plane with a detail of 400 by 400.
When defining the y positions of all of the vertices, I do this.
var position = floorGeometry.attributes.position;
for ( var i = 0; i <= complexity + 1; i ++ ) {
for (var i2 = 0; i2 <= complexity; i2 ++) {
vertex.fromBufferAttribute( position, i * complexity + i2);
var x = vertex.x;
var y = vertex.z;
vertex.y = noise(x,y)
position.setXYZ(i * complexity + i2, vertex.x, vertex.y, vertex.z );
}
}
Complexity represents the detail of the plane.
As you can see... I use geometry.attributes.position to access the vertices, but it is important to note that this stores all of the "sqaure" coordinates
But when it comes to the color attribute... it actually uses the points (and expects an array) of each and every vertex of the tris that make up the plane in a specific order...
What I am doing is making an array of colors (3 elements per vertex representing rgb) and then trying to add it as an attribute to the geometry, and I am trying to make vertices of different heights different colors. For example
count = floorGeometry.attributes.position.count;
var colors = [];
for ( var i = 0; i < count; i ++ ) {
vertex.fromBufferAttribute( position, Math.floor(i)); //<---- NOTE
if (vertex.y > 500) {
colors.push(1,0,0);
}
else colors.push(0,1,0);
}
At the point in the code with the comment "NOTE" I dont know what i am doing here in terms of turing an index from that square system to the color attributes tri based vertex system.
Any ideas? Should I try to access the vertices of the tri based system instead? Is there a mathematical way to do this correctly?
The simple solution is to not use:
vertex.fromBufferAttribute( position, index );
because that uses the square system I discussed in my question, instead use:
geometry.attributes.position.getY(i);
or .getX(i) or .getZ(i) because these use the vertices of the tris!

How to get the angle of a 3d plane using the position of the 4 corners

About 6 months ago i started making a 3d graphics engine.
Its already looking very good. I already implemented rotation, translation, scaling, Z-buffer(painter's algoritm),... Im now working on a specular shader. For that i need some way to get the angle of he individual faces
My question is, how do i get the angle of a plane by only knowing the position of the four corners?
Here is what i got so far:
function faceAngle(verts,faces){
var arr = [];
for(var i=0;i<faces.length;i++){
var posA = verts[faces[i][0]];//the four corners
var posB = verts[faces[i][1]];// A B
var posC = verts[faces[i][2]];// -----
var posD = verts[faces[i][3]];// | |
// | |
var ar = []; // -----
ar.push(/*some Maths*/);//x // D C
ar.push(/*some Maths*/);//y
ar.push(/*some Maths*/);//z
arr.push(ar);
}
return arr;
}
Orientation of plane in the space is defined by normal vector. To get this vector, calculate cross product of two edges (belonging to the plane). So you need only three non-collinear points in the plane.
n = (posB - posA) x (posC - posA) //cross product of two vectors
Note that components of normalized (unit) normal vector are direction cosines

Detect if a set of points in an array that are the vertices of a complex polygon were defined in a clockwise or counterclockwise order?

EDIT: I updated the program with the answer and it works great!
I am making a program (feel free to try it out) that lets users draw polygons which it then triangulates. They can click to add vertices and hit enter to triangulate. Anyways, the algorithm works fine as long as I tell it if the points were drawn in a clockwise or counterclockwise fashion (right now I have it set only to work with clockwise polygons). I have been trying to figure this out for days, but have no idea how to determine whether the points are clockwise or counterclockwise. Try drawing shapes with the program mentioned earlier to get a better idea, you can experience what I am talking about better than I can try to explain it.
Here is how the points are defined:
function Point(x, y) {
this.x = x;
this.y = y;
}
var vertices = [];
// Called on click
function addPoint(mouseX, mouseY) {
vertices.push(new Point(mouseX, mouseY));
}
Here is an image of a clockwise polygon:
Here is an image of a counterclockwise polygon:
If you could help me figure out how to determine the "clockwise-ness" of the points, I would be very grateful!
Compute the polygon area using the shoelace formula, but without the absolute value sign. If the result is positive, the points are ordered counterclockwise, and if negative - clockwise.
function polygonArea() {
var area = 0;
for (var i = 0; i < vertices.length; i++) {
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
}
return area / 2;
}
var clockwise = polygonArea() > 0;
In case someone is using three.js the ShapeUtils comes with an inbuilt isClockWise method which internally uses the area method to determine the sign of the calculated area.
isClockWise: function ( pts ) {
return ShapeUtils.area( pts ) < 0;
}
The ShapeUtils.isClockWise Method can be found here.
area: function ( contour ) {
var n = contour.length;
var a = 0.0;
for ( var p = n - 1, q = 0; q < n; p = q ++ ) {
a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
}
return a * 0.5;
},
The ShapeUtils.area Method can be found here.
A general idea would be to take a look at the convex hull of your polygone and guess the orientation from there. However, I think that you do not need to build the whole hull to find the orientation, but just one segment belonging to it.
So:
Find two points of your polygones so that all the other points are on one side of this line.
If all the points are on the left (just check one of the points), it's counterclockwise. If they are on the right, it's clockwise.
Example:
On the top figure: 4-5 let the figure on the right, 5-11 let the figure on the right, ...
On the bottom figure: 6-7 let the figure on the left, 7-14 let the figure on the left, ...
Warning: While "walking" on your polygon, do not restart the numeration, otherwise it will be wrong. On the top figure, 4-(n-1) let the figure on the left!
Your intuitive definition of clockwisedness is not well defined. For example, If I draw a horseshoe:
/---a-b--\
/ _d_c_ \
/ / \ \
| | | |
| | | |
\ \ / /
\ \ / /
-- --
If 0 = a < b < b < d and I look at a and b I would conclude from your description that the shape has been drawn clockwise, but if 0 = c < d < a < b I would conclude that the shape has been drawn anticlockwise. Since both of these scenarios involve the same direction in which the points were drawn, just from different starting points, I can only conclude that your definition is lacking.
The horseshoe I drew isn't the best; the idea is that it is almost a circle with just a small hole at the bottom, to allow the other side to be drawn in the opposite direction.
If you are interested in defining things more strictly, then I suggest something along the following lines:
Considering any finite simple polygon as separating the plane into two distinct areas (one finite and one infinite), we can always consider the finite area to be the interior of the polygon. In such a scenario we define a vertex ordering to be clockwise iff the order of the points runs with the exterior along its right-hand side. This is called curve orientation.
Once you have this more solid definition, implementation can be as simple as counting the winding number. Take the midpoint of any ordered pair, say 0 and 1, take a line segment to the right of the ordered pair (at any angle, say perpendicular), and count how many intersections it has with other line segments: The curve is clockwise iff the number is odd.
This is simple to implement, linear in time O(n), and adds constant space O(1).
This a function function that specialized for OpenLayers. As You Can See The Condition Of Clockwise Polygon Is area<0 This Reference Confirm It.
function IsClockwise(feature)
{
if(feature.geometry==null)return -1;
var vertices=feature.geometry.getVertices();
var area=0;
for (var i = 0; i < (vertices.length); i++)
{
j = (i + 1) % vertices.length;
area += vertices[i].x * vertices[j].y;
area -= vertices[j].x * vertices[i].y;
// console.log(area);
}
return (area < 0);
}

Categories

Resources