Draw a basic triangle with Three.js - javascript

I am triyng to learn three.js I want to dra a basic triangle however my codes do not work.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 70, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.z = 10;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.Geometry();
geometry.vertices= [new THREE.Vector3(2,1,0), new THREE.Vector3(1,3,0), new THREE.Vector3(3,4,0)];
geometry.faces = [new THREE.Face3(0,1,2)];
var mesh= new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ color: 0xffff00 }) );
scene.add(mesh);
renderer.render(scene, camera);
Where i am doing it wrong?

With Faces
var geom = new THREE.Geometry();
var v1 = new THREE.Vector3(0, 0, 0);
var v2 = new THREE.Vector3(30, 0, 0);
var v3 = new THREE.Vector3(30, 30, 0);
var triangle = new THREE.Triangle(v1, v2, v3);
var normal = triangle.normal();
geom.vertices.push(triangle.a);
geom.vertices.push(triangle.b);
geom.vertices.push(triangle.c);
geom.faces.push(new THREE.Face3(0, 1, 2, normal));
var mesh = new THREE.Mesh(geom, new THREE.MeshNormalMaterial());
scene.add(mesh);
Just Outline
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(-30, 2, 2));
geometry.vertices.push(new THREE.Vector3(2, 30, 2));
geometry.vertices.push(new THREE.Vector3(30, 2, 2));
geometry.vertices.push(new THREE.Vector3(-30, 2, 2));
var line = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({ color: 0x00ff00 })
);
scene.add(line);

It's funny how hard was to find an updated answer, since Geometry was deprecated in recent versions of Three.js the way I found to draw a plane triangle is with Shape primitive.
const shape = new THREE.Shape();
const x = 0;
const y = 0;
shape.moveTo(x - 2, y - 2);
shape.lineTo(x + 2, y - 2);
shape.lineTo(x, y + 2);
const TriangleGeometry = new THREE.ShapeGeometry(shape);
based on https://threejs.org/docs/index.html?q=ShapeGeometry#api/en/geometries/ShapeGeometry

You have to be careful with the order in which you add the vertices to the face. If the order follows a clockwise order, the normal vector of the surface will point down. And since your camera looks from above, you won't see the triangle. If the order is counterclockwise, the normal will point towards your camera.
It will work if you change:
geometry.faces = [new THREE.Face3(0,1,2)];
To:
geometry.faces = [new THREE.Face3(1,0,2)];
You can also use the argument side, which applies it to both sides of the face.new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide };

Related

How does three.js render video according to spherical UV?

I have a dash streaming video. According to its title, it is a 3*3. Now I can splice the complete video through the THREE,
// 3*3 PlaneGeometry
var geometry = new THREE.PlaneGeometry(400, 200, 3, 3);
const video1 = document.getElementById("videos1");
...................
...................
const texture1 = new THREE.VideoTexture(video3);
texture1.maxFilter = THREE.NearestFilter;
texture1.minFilter = THREE.NearestFilter;
...................
...................
var geometryfaces = geometry.faces;
for (let i = 0; i < geometryfaces.length; i++) {
const faces = geometryfaces[i];
materials[i] = new THREE.MeshBasicMaterial({
map: textures[i],
});
}
var uv = [
new THREE.Vector2(0, 0),
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 1),
new THREE.Vector2(1, 0),
];
// Set the texture coordinates
for (var m = 0; m < geometryfaces.length; m += 2) {
geometry.faces[m].materialIndex = faceId;
console.log(geometry.faces);
geometry.faces[m + 1].materialIndex = faceId;
geometry.faceVertexUvs[0][m] = [uv[2], uv[3], uv[1]];
geometry.faceVertexUvs[0][m + 1] = [uv[3], uv[0], uv[1]];
faceId++;
}
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(geometry);
var material = new THREE.MeshFaceMaterial(materials);
var mesh = new THREE.Mesh(bufferGeometry, material); //网格模型对象Mesh
This way I can get a full flat video, but this video is panoramic and I need to render it on the ball, and I don't know much about the UV of the ball。
I need help. Thank you
picture:enter image description here
picture2:enter image description here
Three.js SphereGeometry automatically creates the UV mapping for you. Since the image you're using is equirectangular, you can just map the image onto the inside of the sphere for the effect you want:
var camera, scene, renderer, vp, sphere;
vp = new THREE.Vector2(window.innerWidth, window.innerHeight);
// Init WebGL stuff
function init() {
// WebGL Renderer
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(vp.x, vp.y);
renderer.domElement.classList.add("canvasWebGL");
// append to DOM
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xe1e1e1);
// camera
camera = new THREE.PerspectiveCamera(55, vp.x / vp.y, 0.05, 100);
// Sphere radius is large enough to surround the camera
let sphereGeom = new THREE.SphereGeometry( 20, 128, 128 );
// Just use your equirect image as the texture
// And render the INSIDE of the sphere, not the outside
const tex = new THREE.TextureLoader().load("https://i.imgur.com/1VECsLy.jpg");
let sphereMat = new THREE.MeshBasicMaterial({
map: tex,
side: THREE.BackSide
});
sphere = new THREE.Mesh(sphereGeom, sphereMat);
scene.add(sphere);
}
function animate(s) {
sphere.rotation.set(
Math.cos(s / 3000), s / 3000, 0
);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
init();
animate(0);
<script src="https://cdn.jsdelivr.net/npm/three#0.123/build/three.js"></script>
Read more about sphere geometry here: https://threejs.org/docs/?q=material#api/en/geometries/SphereGeometry

Three.js – Create half a ring and animate it

I've created a Ring and would like to have only half of it. And after that animate it, that it builds itself up from 0 to half.
var geometry = new THREE.RingGeometry(10, 9, 32);
var material = new THREE.MeshBasicMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
How can I archive it? I'm new to three.js.
Use thetaStart and thetaLength to animate the half-ring.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.118.3/build/three.module.js";
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var grid = new THREE.GridHelper(10, 10);
grid.rotation.x = Math.PI * 0.5;
scene.add(grid);
var innerRadius = 1;
var outerRadius = 2;
// re-building geometry
var usualRingGeom = new THREE.RingBufferGeometry(innerRadius, outerRadius, 32, 1, 0, 1);
var usualRingMat = new THREE.MeshBasicMaterial({color: 0xffff00});
var usualRing = new THREE.Mesh(usualRingGeom, usualRingMat);
scene.add(usualRing);
var clock = new THREE.Clock();
renderer.setAnimationLoop(()=>{
let t = clock.getElapsedTime();
// re-building geometry
usualRingGeom = new THREE.RingBufferGeometry(innerRadius, outerRadius, 32, 1, 0, (Math.sin(t) * 0.5 + 0.5) * Math.PI);
usualRing.geometry.dispose();
usualRing.geometry = usualRingGeom;
renderer.render(scene, camera);
});
</script>
PS You also can achieve the same result without re-building a geometry. For that, you can bend a plane in js (changing vertices) or in shaders :)

Sphere texture does not cover the whole sphere

When the texture is applied on the sphere, ugly folds appear at poles and part of the texture is missing.
var geometry = new THREE.SphereGeometry(50, 50, 50, 0, Math.PI * 2, 0, Math.PI * 2);
var load = new THREE.TextureLoader().load("e.jpg");
load.anisotropy = 8;
var material = new THREE.MeshBasicMaterial({
map: load,
overdraw: true
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
How to do it correctly?
You created a sphere with twice the number of circles of latitude.
Try this :
var geometry = new THREE.SphereGeometry(50, 50, 50, 0, Math.PI * 2, 0, Math.PI);
var load = new THREE.TextureLoader().load("e.jpg");
load.anisotropy = 8;
var material = new THREE.MeshBasicMaterial({
map: load,
overdraw: true
});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
By the way, you can leave extra parameters to default by not passing those to the constructor :
var geometry = new THREE.SphereGeometry(50, 50, 50);
hi try to first load required image and on load use following code.
var earthTexture = new THREE.TextureLoader().load("e.jpg");
var earthGeometery = new THREE.SphereGeometry(50, 50, 50);
var earthMaterial = new THREE.MeshLambertMaterial({
map: earthTexture,
transparent: false
});
imgObj : is image object

Three js Add plane to the scene

I am trying to add a plane to the scene but if I check children's scene nothing is added.
var plane = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffff00, opacity: 0.25 } ) );
plane.visible = true;
this.scene.add( plane );
try this:
var geo = new THREE.PlaneBufferGeometry(2000, 2000, 8, 8);
var mat = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.DoubleSide });
var plane = new THREE.Mesh(geo, mat);
scene.add(plane);
if you want to use the plane as a floor, you have to rotate it.
plane.rotateX( - Math.PI / 2);

Three.js raycaster intersection

I wrote the code below to get intersection point with a 3d shape. It works well but if there are two intersection point with shape, it returns me only the farest intersection while I need the nearest intersection with the shape. How can I do to get the nearest intersection?
/*here I create a cube*/
var geometry0 = new THREE.Geometry()
geometry0.vertices = [new THREE.Vector3(0.5, -0.5, 0.5), new THREE.Vector3(-0.5, -0.5, 0.5), new THREE.Vector3(-0.5, -0.5, -0.5), new THREE.Vector3(0.5, -0.5, -0.5), new THREE.Vector3(0.5, 0.5, 0.5), new THREE.Vector3(-0.5, 0.5, 0.5), new THREE.Vector3(-0.5, 0.5, -0.5), new THREE.Vector3(0.5, 0.5, -0.5)];
geometry0.faces = [new THREE.Face3(3, 2, 1), new THREE.Face3(3, 1, 0), new THREE.Face3(4, 5, 6), new THREE.Face3(4, 6, 7), new THREE.Face3(0, 1, 5), new THREE.Face3(0, 5, 4), new THREE.Face3(1, 2, 6), new THREE.Face3(1, 6, 5), new THREE.Face3(2, 3, 7), new THREE.Face3(2, 7, 6), new THREE.Face3(3, 0, 4), new THREE.Face3(3, 4, 7)];
geometry0.computeFaceNormals();
geometry0.computeVertexNormals();
var material0 = new THREE.MeshBasicMaterial({color: 0x39d2dbe7fff39d2, transparent: true, opacity: 0});
mesh0 = new THREE.Mesh(geometry0, material0);
egh0 = new THREE.EdgesHelper(mesh0, 0x000);
egh0.material.linewidth = 2;
scene.add(egh0);
objects.push(mesh0);
projector = new THREE.Projector();
console.log(objects);
mouse2D = new THREE.Vector3(0, 10000, 0.5);//controllare i valori
/* here I create the ray */
document.addEventListener('click', onDocumentMouseClick, false);
function onDocumentMouseClick(event) {
event.preventDefault();
mouse2D.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse2D.y = -(event.clientY / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3( mouse2D.x, mouse2D.y, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if (intersects.length > 0) {
console.log("ok");}
If I check intersects[0].point I can only see the farest point face of cube intersected and not the first (for example,if you look at a cube in front of you and make a ray,this ray before intersect the first face and after intersect second face behind the first.)
The goal of this code is to create an event only when you click on vertices.
So after this I wrote code to calculate the euclidean distance from point of click and all vertices
and return the vertice nearest to the point of click.
If you have other idea to fire an event only when you click on vertices is welcome.
Yes, the basic idea of ray casting is that we project a ray perpendicular to the plane we find out the list of objects that the ray has intersected.
So all you have to do to access the first element is adding the following piece of code.
var intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
var firstIntersectedObject = intersects[0];
// this will give you the first intersected Object if there are multiple.
}
Here is one of my other SO post in which I have explained things in a bit more detailed way, you can refer it to better understand how raycasting functions.
Try to make through this example. Look at messages in the console.
<script src="js/controls/EventsControls.js"></script>
EventsControls = new EventsControls( camera, renderer.domElement );
EventsControls.draggable = false;
EventsControls.onclick = function() {
console.log( this.focused.name );
console.log( 'this.focusedPoint: (' + this.focusedPoint.x + ', ' +
this.focusedPoint.y + ', ' + this.focusedPoint.z + ')' );
console.log( 'this.focusedDistance: ' + this.focusedDistance );
}
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
EventsControls.attach( mesh );
//
function render() {
EventsControls.update();
controls.update();
renderer.render(scene, camera);
}

Categories