THREE.ShaderMaterial opacity not working - javascript

I am switching to ShaderMaterial from MeshBasicMaterial in order to provide filters on my mesh textures. ShaderMaterial inherits from Material and hence opacity parameter is present. But changing this parameter doesn't change opacity of object. I am using THREE.HueSaturationShader which does not set alpha value.
I have added a very simple fiddle to showcase the situation
http://jsfiddle.net/thenectorgod/89aahytL/1/.
// BufferGeometry Tester
var hostDiv, scene, renderer, camera;
var WIDTH = 500;//window.innerWidth,
HEIGHT = 500;//window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function init() {
hostDiv = document.createElement('div');
document.body.appendChild(hostDiv);
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.setClearColor( 0x888888, 1 );
hostDiv.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
camera.lookAt(scene.position);
var geometry = new THREE.PlaneBufferGeometry(20,20);
var material = new THREE.ShaderMaterial({
transparent: true,
depthTest: false
});
material.opacity = 0;
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
scene.add(camera);
animate();
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
}
init();
Though I set the opacity to 0 the object is still visible.
How can I use both opacity and Shaders without adding extra opacity parameter in all of them.

Your shader material has no shader. By default it renders something like MeshBasicMaterial but with a hardcoded GLSL color of red, and no transparency / opacity.
If you want a ShaderMaterial, you actually need to write GLSL shaders for it.

Related

Three.js: Smoothing normals on non indexed BufferGeometry

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.

How to rotate a 3d model horizontally with three.js

I am having trouble rotating my 3d man object model horizontally via three.js.
My main confusion is that I dont know if I should to be controlling the camera angle or the object angle in order to achieve this and based on my research, there seemes to be different methods to achieving what I need but I am not sure which method would work with what I implemented so far.
<script src="js/three.js"></script>
<script src="js/OBJLoader.js"></script>
<script src="js/OrbitControls.js"></script>
<script>
var scene, camera, renderer , controls, ambientLight, pointLight, textureLoader, map, material, loader;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initChris();
initCamera();
initRenderer();
initControl();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 8000);
ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
pointLight = new THREE.PointLight( 0xffffff, 0.8 );
camera.position.set(0, 0, 4000);
camera.add( pointLight );
scene.add( camera );
scene.add( ambientLight );
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.setClearColor( 0x000000, 0 );
}
function initControl(){
controls = new THREE.OrbitControls( camera );
controls.enableZoom = false;
controls.minPolarAngle = Math.PI * 0.5;
controls.maxPolarAngle = Math.PI * 0.5;
controls.update();
}
function initChris() {
textureLoader = new THREE.TextureLoader();
map = textureLoader.load('img/CHRIS.BMP');
material = new THREE.MeshPhongMaterial({map: map});
loader = new THREE.OBJLoader();
loader.load( 'obj/CHRIS.OBJ', function ( object ) {
object.traverse( function ( node ) {
if ( node.isMesh ){
node.material = material;
}
});
scene.add( object );
});
}
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
init();
render();
</script>
I also dont know if there is something wrong with my 3d man model because I tried using different models and each model gave me a different rotation angle. For example, the model I am currently working with is showing my 3d man model at birds-eye-view and the rotation pivot is occuring at his feet while if I change the 3d man model taken from three.js example, I get to see the 3d man model at front view and the rotation pivot is centered which is what I am tryin to achieve with the 3d man model that I am working with.
Any help or advise is greatly appreciated.

Three.js - How to scale a circle without scaling it's stroke?

I'm using a THREE.Path to create a Circular path and then using a TubeGeometry to use that Path to basically create a circle with transparent fill and a stroke whose thickness I can control. My question is, how can I scale up the Circular path at runtime?
Accessing the vertices through mesh.vertices results in a very weird result, because I'm accessing the Tube's Geometry, not the Path. If I change the path, then I have to create a brand new TubeGeometry and update the Tube with the new geometry by doing mesh.geometry = newTubeGeometry, and that just doesn't work at all. Scaling the Tube itself increases it's radius as well, so that's not the right solution either.
Any ideas?
Thanks
Just changing the geometry property does not work, you have to create a new instance of THREE.Mesh. You should also call geometry.dispose() on your old geometry because the renderer internally caches the corresponding WebGLBuffer objects. More information here:
https://discourse.threejs.org/t/changing-the-geometry-of-a-mesh/306
Just an example of how you can do that trick with THREE.TorusGeometry():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 5, 10);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.TorusGeometry(5, 0.5, 8, 32);
geometry.rotateX(Math.PI * -0.5);
geometry.vertices.forEach(v => {
v.initPosition = new THREE.Vector3().copy(v);
});
var mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: "red",
wireframe: true
}));
scene.add(mesh);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
geometry.vertices.forEach(v => {
v // take a vertex
.setY(0) // make it scalable in two dimensions (x, z)
.normalize() // make a normal from it
.multiplyScalar(Math.sin(time) * 2) // set the distance
.add(v.initPosition); // add the initial position of the vertex
});
geometry.verticesNeedUpdate = true;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Threejs apply clipping to specific area of the object

I'm using THREE.Plane to clip my STL model.
localPlane = new THREE.Plane( new THREE.Vector3( 0, -1, 0 ), 4);
.
.
.
material = new THREE.MeshPhongMaterial( {
color: 0xffffff,
side: THREE.DoubleSide,
clippingPlanes: [
localPlane,
],
clipShadows: true
} );
It's working; but the problem is that the whole object's top is clipped by this infinity sized plane. I want it to clip just a small part of it (It seems that there is no way to scale THREE.Plane)
I also tried using ThreeCSG.js but it seems inconvenient with STL objects!
Here is what I get:
Yes, the removal of the intersection of clipping planes is supported in three.js. You can use a pattern like this one:
// clipping planes
var localPlanes = [
new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 1 ),
new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 1 )
];
// material
var material = new THREE.MeshPhongMaterial( {
color: 0xffffff,
side: THREE.DoubleSide,
clippingPlanes: localPlanes,
clipIntersection: true
} );
Also, see the three.js example.
three.js r.85
Edit: Follow WestLangley's advice. I'll leave this her as an alternate though less efficient means of performing the clipping.
Clipping planes are infinite. There's no getting around that. So what can you do? Multiple clipping planes in multiple render passes!
To do this, you'll need to turn off auto-clearing, and do your own manual buffer clearing.
renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
Now let's say plane1 is the clipping plane you currently have.
material = new THREE.MeshPhongMaterial( {
...
clippingPlanes: [
plane1,
],
clipShadows: true
} );
var myMesh = new THREE.Mesh(geometry, material);
That clips the top half of myMesh when you call render. So you want to work with the remainder.
First, make another plane, plane2, be the inverse of plane1. plane2 will then clip the BOTTOM of myMesh. But if you render one pass using plane1, and another using plane2, then you're back with a full mesh. So you'll need a third clip plane, plane3, which clips only the desired half of myMesh. Putting plane2 and plane3 in the same render pass will result in only 1/4 of myMesh rendering.
var pass1ClipPlanes = [
plane1
],
pass2ClipLanes = [
plane2, // this plane is the inverse of plane 1, so it clips the opposite of plane1
plane3 // this clips the left/right half of the model
];
Then when you go to render, clear the draw buffers first, then call two render passes, updating the clip planes between them.
// clear the draw buffers
renderer.clear();
// clip the top
myMesh.material.clipPlanes = pass1ClipPlanes;
renderer.render(scene, camera);
// clip the bottom and one side
myMesh.material.clipPlanes = pass2ClipPlanes;
renderer.render(scene, camera);
The first pass renders the bottom of the model, and the second pass renders half of the top.
ETA: Example
var renderer, scene, camera, controls, stats;
var cube,
pass1ClipPlanes,
pass2ClipPlanes;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.localClippingEnabled = true;
renderer.autoClear = false;
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
// POPULATE EXAMPLE
var plane1 = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0),
plane2 = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
plane3 = new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0);
pass1ClipPlanes = [plane1];
pass2ClipPlanes = [plane2, plane3];
var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
cubeMat = new THREE.MeshPhongMaterial({
color: "red",
side: THREE.DoubleSide
});
cube = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(cube);
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.clear();
cube.material.clippingPlanes = pass1ClipPlanes;
renderer.render(scene, camera);
cube.material.clippingPlanes = pass2ClipPlanes;
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();

THREE.js Triangle is Black

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

Categories