I'm trying to smooth the normals of a mesh starting from a non indexed BufferGeometry.
This question has been answered before however the Three.js api has changed substantially since and I can't get it to work on r130
From what I've understood, I need to first merge the vertices to get an indexed BufferGeometry then re-compute the normals, but it doesn't seem to work.
Here is a minimal example using the defaulf cube :
// Scene
const scene = new THREE.Scene();
// Geometry
const boxGeometry = new THREE.BoxGeometry(.7,.7,.7);
// Materials
const shadedMaterial = new THREE.MeshStandardMaterial();
shadedMaterial.metalness = 0.4;
shadedMaterial.roughness = 0.4;
shadedMaterial.color = new THREE.Color(0xffffff);
// Mesh
const smoothBoxGeometry=BufferGeometryUtils
.mergeVertices(new THREE.BufferGeometry().copy(boxGeometry))
smoothBoxGeometry.computeVertexNormals();
smoothBoxGeometry.computeBoundingBox();
smoothBoxGeometry.normalizeNormals();
const box = new THREE.Mesh(smoothBoxGeometry, shadedMaterial);
scene.add(box);
flat shaded cube instead of expected smooth shaded cube
What am I missing ?
Try it like so:
let camera, scene, renderer;
let mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene = new THREE.Scene();
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
hemiLight.position.set(0, 20, 0);
scene.add(hemiLight);
const dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(-3, 10, -10);
scene.add(dirLight);
let geometry = new THREE.BoxGeometry();
geometry.deleteAttribute('normal');
geometry.deleteAttribute('uv');
geometry = THREE.BufferGeometryUtils.mergeVertices(geometry);
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/examples/js/utils/BufferGeometryUtils.js"></script>
BufferGeometryUtils.mergeVertices() can only perform the merge if vertex data are identical. To ensure this, it is necessary to remove the existing normal and uv attribute.
Related
I want to create a sun surrounded with a large ring or orbit in three.js. Here is my source code (using require.js modules):
require(['js/interactive.js', 'js/OrbitControls.js', 'js/three.min.js'], function (InteractionManager, OrbitControls, THREE) {
const ZOOM = 300;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(window.innerWidth / -ZOOM, window.innerWidth / ZOOM, window.innerHeight / ZOOM, window.innerHeight / - ZOOM, 1, 1000);
const renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// renderer.setClearColorHex( 0x555555, 1 );
document.body.appendChild(renderer.domElement);
// new a interaction, then you can add interaction-event with your free style
const interaction = new InteractionManager(renderer, camera, renderer.domElement);
const loader = new THREE.TextureLoader();
const controls = new OrbitControls(camera, renderer.domElement);
const light = new THREE.AmbientLight(0xffffff); // soft white light
scene.add(light);
// const light2 = new THREE.PointLight(0xffffff); // soft white light
// light2.position.set(10, 0, 0);
// scene.add(light2);
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshPhongMaterial({
map: loader.load('./resources/2k_sun.jpeg'),
specular: 0x555555,
shininess: 50
});
const sun = new THREE.Mesh(geometry, material);
scene.add(sun);
interaction.add(sun);
sun.addEventListener('click', function(ev) {
var bb = new THREE.Box3()
bb.setFromObject(sun);
bb.getCenter(controls.target);
camera.zoom = 1;
camera.updateProjectionMatrix();
});
const ringGeometry = new THREE.RingGeometry(50, 51, 32);
const ringMaterial = new THREE.MeshBasicMaterial( { color: 0x808080, side: THREE.DoubleSide } );
const ring = new THREE.Mesh( ringGeometry, ringMaterial );
scene.add( ring );
camera.position.set(0, 0, 5);
controls.update();
const animate = function () {
requestAnimationFrame(animate);
sun.rotation.x += 0.01;
sun.rotation.y += 0.01;
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
};
animate();
})
interactive.js is an interaction library I downloaded from here. I used it to zoom in on the sun when it's clicked.
However, when I run this code, parts of the ring will not render at certain angles. This is only fixed if I make the ring much smaller. Below is a screenshot of this behavior:
My question: How can I make this ring fully visible at any angle and maintain the large size?
I am working in a three.js project. In the project I have to show all edges of geometries even those edges are intersecting with other objects' surfaces.
Here is the snippet code that illustrates my problem.
var camera, scene, renderer, material, stats, group, wireframeMaterial;
init();
animate();
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer({antialias: true, alpha:true,clearAlpha:0,clearColor: 0xff0000});
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
// Create scene.
scene = new THREE.Scene();
group=new THREE.Group()
// Create material
material = new THREE.MeshBasicMaterial();
wireframeMaterial=new THREE.LineBasicMaterial( { color: 0x000000, side:THREE.FrontSide ,transparent:false,opacity:1,linewidth: 1 })
// Create cube and add to scene.
var geometry = new THREE.BoxGeometry(200, 200, 200);
var mesh1 = new THREE.Mesh(geometry, material);
group.add(mesh1);
var geometry2 = new THREE.BoxGeometry(100,100,100);
var mesh2 = new THREE.Mesh(geometry2, material);
group.add(mesh2);
mesh2.position.fromArray([0,150,0])
var edges = new THREE.EdgesGeometry( geometry );
var line = new THREE.LineSegments( edges, wireframeMaterial );
mesh1.add( line );
var edges2 = new THREE.EdgesGeometry( geometry2 );
var line2 = new THREE.LineSegments( edges2, wireframeMaterial );
mesh2.add( line2 );
scene.add(group)
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
group.rotation.x += 0.005;
group.rotation.y += 0.01;
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
In fiddle code you can see there are two cubes top of each other. I want bottom edges of small cube to become visible. One solution is making mesh basic material transparent. However in that situation edges that are behind cubes it self will be visible too which is not allowed in the project.
So are there any alternative solution for this issue?
Found a good solution, you need to use the polygonOffset parameters of the basic material.
var material = new THREE.MeshBasicMaterial({
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1
});
Found it on this question: three.js EdgesHelper showing certain diagonal lines on Collada model
EdgesGeometry will render the hard edges only.
WireframeGeometry will render all edges.
var camera, scene, renderer, material, stats, group, wireframeMaterial;
init();
animate();
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer({antialias: true, alpha:true,clearAlpha:0,clearColor: 0xff0000});
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
// Create scene.
scene = new THREE.Scene();
group=new THREE.Group()
// Create materials
var material = new THREE.MeshBasicMaterial({
polygonOffset: true,
polygonOffsetFactor: 1, // positive value pushes polygon further away
polygonOffsetUnits: 1
});
var wireframeMaterial= new THREE.LineBasicMaterial( { color: 0x000000, linewidth: 2 } );
// Create cube and add to scene.
var geometry = new THREE.BoxGeometry(200, 200, 200);
var edges = new THREE.EdgesGeometry(geometry);
var line = new THREE.LineSegments( edges, wireframeMaterial );
var mesh = new THREE.Mesh(geometry, material);
group.add(mesh, line);
var geometry2 = new THREE.BoxGeometry(100,100,100);
var wire = new THREE.EdgesGeometry(geometry2);
var line2 = new THREE.LineSegments( wire, wireframeMaterial );
var mesh2 = new THREE.Mesh(geometry2, material);
line2.position.fromArray([0,150,0]);
mesh2.position.fromArray([0,150,0]);
group.add(mesh2, line2);
scene.add(group)
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
// Add stats to page.
stats = new Stats();
document.body.appendChild( stats.dom );
}
function animate() {
requestAnimationFrame(animate);
group.rotation.x += 0.005;
group.rotation.y += 0.01;
renderer.render(scene, camera);
stats.update();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
<script type="text/javascript" src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/mrdoob/stats.js/r17/build/stats.min.js"></script>
I'm wondering if anyone has an idea on how to rotate a camera so the camera faces directly above a point on the sphere.
UPDATE
Radio's answer is correct. After some debugging it looks like my coordinates were reversed.
I have created a fiddle: http://jsfiddle.net/kbtn6pz5/
This moves the camera over a random point on the sphere, applies a lookAt property and then sets the altitude of the camera over the sphere's surface.
Based on the fiddle boilerplate...
var camera, scene, renderer, geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
scene.add(camera);
var rad = 200;
geometry = new THREE.SphereGeometry( rad, 32, 32 );
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var point = THREE.GeometryUtils.randomPointsInGeometry( geometry, 1 );
var altitude = 100;
var coeff = 1+ altitude/rad;
camera.position.x = point[0].x * coeff;
camera.position.y = point[0].y * coeff;
camera.position.z = point[0].z * coeff;
camera.lookAt(mesh.position);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
jsfiddle: http://jsfiddle.net/VsWb9/2151/
After a reading a few articles i am at a loss.
Rotate camera around object with Three.js
Below is simple code for a cube rotating with a floor. This should hopefully be a simple example to follow.
I am trying to add a camera so on click and drag it rotates around the scene. Similar to this http://threejs.org/examples/#misc_controls_trackball
For anyone with the same quesiton lets get you to my point below:
Download three.js here:
http://threejs.org/
You need to have something call orbit controls in your js folder. You will find a link to orbitcontrols to download here:
https://dl.dropboxusercontent.com/u/3587259/Code/Threejs/OrbitControls.js
Copy this and put it in your site folder.
You then need to link to orbit control and three.js it in your html. Like the below:
<script src="js/OrbitControls.js"></script>
<script src="js/three.min.js"></script>
Then see below for a simple wireframe cube with a floor.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 0.1, 1000);
var axisHelper = new THREE.AxisHelper( 5 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//This breaks it?
//controls = new THREE.OrbitControls( camera, renderer.domElement );
var geometry = new THREE.CubeGeometry(2,1,1);
var material = new THREE.MeshBasicMaterial({wireframe: true});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
var floorMaterial = new THREE.MeshBasicMaterial( {wireframe: true} );
var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.position.y = -50.0;
floor.rotation.x = Math.PI / 2;
scene.add(floor);
camera.position.z = 3;
var render = function () {
requestAnimationFrame(render);
cube.rotation.z += 0.005;
cube.rotation.x += 0.005;
renderer.render(scene, camera);
};
render();
So i was using THREE.JS and created a triangle. But even though i gave it color: 0xFF0000, it appears black on Screen.
Here is my Script:
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 500;
scene.add(camera);
var geometry = new THREE.Geometry(200,200,200);
var v1 = new THREE.Vector3(200,0,0); // Vector3 used to specify position
var v2 = new THREE.Vector3(-100,0,0);
var v3 = new THREE.Vector3(0,50,0); // 2d = all vertices in the same plane.. z = 0
// Push vertices represented by position vectors
geometry.vertices.push(v1);
geometry.vertices.push(v2);
geometry.vertices.push(v3);
// Push face, defined with vertices in counter clock-wise order
geometry.faces.push(new THREE.Face3(0, 2, 1));
// Create a material and combine with geometry to create our mesh
var redMat = new THREE.MeshLambertMaterial({color: 0xFF0000});
var triangle = new THREE.Mesh(geometry, redMat);
scene.add(triangle);
renderer = new THREE.CanvasRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
Hope you can find the mistake i made. I am sure it's just something really stupid.
late to the party...but for all who come after me.
The issue here is the type of material used
change
new THREE.MeshLambertMaterial({color: 0xFF0000});
to
new THREE.MeshBasicMaterial({color: 0xFF0000});
and it will show up red
Even I've struggled from this problem for sometime. The solution for me was to add an ambient light to the scene if you're using Lambert Material.
Just add:
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
Try add computeFaceNormals call upon geometry object:
geometry.computeFaceNormals();
*from https://stackoverflow.com/a/49226898/2154075