Three.js CanvasRenderer issues - javascript

I'm just trying out Three.js. As I want to use CanvasRenderer as a fallback for devices where WebGL is not supported, I've noticed two issues:
Distored line / Bad geometry?
Additional pixel
Here's my current code
var width = 960;
var height = 500;
var aspect = width / height;
var D = 1;
var CanvasRenderer = true;
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera(-D * aspect, D * aspect, D, -D, 1, 1000);
if(CanvasRenderer) {
var renderer = new THREE.CanvasRenderer({
alpha: true,
antialias: true
});
} else {
var renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
}
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry(0.1, 1, 0.5);
var cube = new THREE.Mesh(geometry, [
new THREE.MeshPhongMaterial({
color: 0x222222, // right
overdraw: true
}),
new THREE.MeshPhongMaterial({
color: 0xffffff,
overdraw: true
}),
new THREE.MeshPhongMaterial({
color: 0xEDE6F5, // Top
overdraw: true
}),
new THREE.MeshPhongMaterial({
color: 0xffffff,
overdraw: true
}),
new THREE.MeshPhongMaterial({
color: 0xFFFFFF, // Left
overdraw: true
}),
new THREE.MeshPhongMaterial({
color: 0xffffff,
overdraw: true
})
]);
scene.add(cube);
scene.add(new THREE.AmbientLight(0xFFFFFF));
camera.position.set(20, 20, 20);
camera.lookAt(scene.position);
renderer.render(scene, camera);
body {
margin: 0px;
overflow: hidden;
background: #EEE;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/renderers/Projector.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/renderers/CanvasRenderer.js"></script>
WebGL difference
You can see the difference with WebGL renderer:
JSFiddle
http://jsfiddle.net/4tbs4tpL/2/

When using CanvasRenderer, the Material.overdraw property takes values between 0 and 1. A reasonable value is 0.5.
The artifacts you refer to will likely diminish with lower values of overdraw.
three.js r.86

Related

How to make translucent texture visible through Physical Material in THREE.js?

I would like to render a translucent PNG texture behind a see-through Physical Material. However, THREE.js doesn't render the translucent texture through the Physical Material at all.
I figured out that setting transparent: false on the material makes it visible, but that obviously makes my texture opaque, which is not what I want.
How can I make a translucent PNG texture visible through Physical Material?
const renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('canvas'),
});
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
scene.add(new THREE.AmbientLight(0xffffff, 1));
const texture = new THREE.TextureLoader().load('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE4AAABOCAYAAACOqiAdAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADdSURBVHhe7dAhCgIBFADR1SgYLDatHsv7n2ItXzDILg7Y3ivT57Cu63P57jHdcpvuuU63XKZ7ztO30/TTffo3xyk/Mi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi4yLjIuMi5ZlhcxLwSYJ4qy6wAAAABJRU5ErkJggg==');
const camera = new THREE.PerspectiveCamera(65, renderer.domElement.clientWidth / renderer.domElement.clientHeight, 0.1, 1000);
{
const geometry = new THREE.PlaneBufferGeometry(4, 4);
const material = new THREE.MeshStandardMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(geometry, material);
plane.position.z = 1;
scene.add(plane);
}
{
const geometry = new THREE.PlaneBufferGeometry(3, 3);
const material = new THREE.MeshStandardMaterial({
color: 0xff0000,
side: THREE.DoubleSide,
map: texture,
transparent: true,
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
document.getElementById('transparent').onchange = (evt) => {
material.transparent = evt.target.checked;
material.needsUpdate = true;
};
}
{
const geometry = new THREE.PlaneBufferGeometry(3, 3);
const material = new THREE.MeshPhysicalMaterial({
side: THREE.DoubleSide,
roughness: 0.3,
transmission: 0.4,
color: 0xffffff,
});
const plane = new THREE.Mesh(geometry, material);
plane.position.z = -2;
plane.position.x = 1;
scene.add(plane);
}
const startTime = performance.now();
function render() {
const now = performance.now();
const angle = (now - startTime) * 0.002;
camera.position.set(Math.cos(angle) * 2, Math.sin(angle) * 2, -5);
camera.lookAt(0, 0, 0);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://cdn.jsdelivr.net/npm/three#0.139.2/build/three.min.js"></script>
<div style="position: absolute; left: 0; top: 0; z-index: 1; color: white;">
<input type="checkbox" id="transparent" name="transparent" checked /><label for="transparent"> Transparent</label>
</div>
<canvas id="canvas" width="350" height="150" style="position: absolute; left: 0; top: 0;" />
The "transmission" feature of THREE.MeshPhysicalMaterial is a more advanced type of physically-based transparency that requires additional rendering passes. Currently three.js (and most other realtime engines I'm aware of) cannot display alpha-blending (.transparent=true) or transmissive (.transmission>0) materials through a transmissive material. If you need multiple layers of transparency you'll need to stick with alpha blending and not transmission, provided by .transparent=true and opacity or a texture with an alpha channel.
Related discussion: https://github.com/mrdoob/three.js/issues/22009

How to add Text on the outer surface of Cylinder geometry?

I am trying to add some text on a cylinder geometry ...I tried to map texture which is working but when I try to add text it doesn't work
I just want to map text (like texture) on the outer side of the cylinder geometry in such a way that if the shape changes, the text follows the shape and look like engraved on the surface
var geometry = new THREE.CylinderGeometry(2.5, 2.5, 5, 10000, 1, true, 100);
var materialOuter = new THREE.MeshLambertMaterial({
color: 0x581845
});
var materialOuter = new THREE.MeshBasicMaterial({
// map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/758px-Canestra_di_frutta_(Caravaggio).jpg")
map: new THREE.FontLoader().load('/fonts/gentilis_regular.typeface.json', function (font) {
var textGeo = new THREE.TextGeometry("My Text", {
font: font,
size: 200,
height: 50,
curveSegments: 12,
bevelThickness: 2,
bevelSize: 5,
bevelEnabled: true
});
var textMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var mesh = new THREE.Mesh(textGeo, textMaterial);
// mesh.position.set(x, y, z);
// scene.add(mesh);
})
});
var materialInner = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load("../images/1.jpg"),
side: THREE.BackSide
});
var materialInner = new THREE.MeshLambertMaterial({ color: 0xd2691e, side: THREE.BackSide });
var meshOuter = new THREE.Mesh(geometry, materialOuter);
var meshInner = new THREE.Mesh(geometry, materialInner);
meshOuter.add(meshInner);
var bottomCylinder = new THREE.Mesh(new THREE.CylinderGeometry(2.5, 2.5, 0.01, 10000, 1, false, 100), new THREE.MeshLambertMaterial({ color: 0xFFC300, wireframe: false, shading: THREE.FlatShading }));
bottomCylinder.position.y = -2.5;
scene.add(bottomCylinder);
scene.add(meshOuter);
var light = new THREE.AmbientLight(0xffffff);
light.position.z = 200;
scene.add(light);

How to render many images on a cube side and define their position like a gallery wall?

I'm trying to build a gallery wall via three.js. So far I succeeded rendering an image on each side like this:
I've put paintings on left and right. However, I want to render more images on each wall rather than filling the entire wall with a single image. Something like this:
How can I set arbitrary positions?
Here's my code:
var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1,1000)
console.log(window.innerWidth, window.innerHeight)
var renderer = new THREE.WebGLRenderer()
renderer.setSize( window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
//keep object in view when resize window
window.addEventListener('resize', () => {
let width = window.innerWidth
let height = window.innerHeight
renderer.setSize(width, height)
camera.aspect = width / height
camera.updateProjectionMatrix()
})
controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.enableZoom = false
controls.enableRotate = true
controls.enablePan = false
var geometry = new THREE.BoxGeometry( 2.5, 2, 4 )
var cubeDice = [
new THREE.MeshBasicMaterial({ color: 0x666666, side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({ color: 0x666666, side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({ color: 0x999999, side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({ color: 0x999999, side: THREE.DoubleSide }),
new THREE.MeshPhongMaterial({ map: new THREE.TextureLoader().load('img/h5_1993.132.jpg'), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('img/unnamed.png'), side: THREE.DoubleSide }),
]
// var material = new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: true } )
var material = new THREE.MeshFaceMaterial(cubeDice)
var cube = new THREE.Mesh( geometry,material )
scene.add( cube )
// //set camera closer
camera.position.z = 1
var update = function(){
cube.rotation.y += .005
}
var render = function(){ renderer.render(scene, camera) }
var GameLoop = () => {
requestAnimationFrame(GameLoop)
update()
render()
}
GameLoop()
MeshFaceMaterial does not do what you want. It makes each image fill up an entire face (an entire wall).
To make separate, movable images on the wall, it would be better to make an individual smaller mesh for each image, and then position them very close to the wall. That way the image can cover the entire mesh, but not cover the entire wall:

rotate sphere in three.js so that mapped image of globe matches GeoJSON that is also shown in three.js

I have created a globe in three.js and am using an image mapped to the sphere.
On top of this, I'm using the ThreeGeoJSON library to render geojson data.
However the geographies don't match up.
I need to rotate the globe with the mapped image so that they align, but I can't figure out how to do so. I tried setting a quaternion variable and rotating based on that, but can't get it working. Any help or pointers would be very much appreciated.
Here you can see a working version of what I've done so far:
http://bl.ocks.org/jhubley/8450d7b0df0a4a9fd8ce52d1775515d5
All of the code, images, data here:
https://gist.github.com/jhubley/8450d7b0df0a4a9fd8ce52d1775515d5
I've also pasted the index.html below.
<html>
<head>
<title>ThreeGeoJSON</title>
<script src="threeGeoJSON.js"></script>
<!-- Three.js library, movement controls, and jquery for the geojson-->
<script src="three.min.js"></script>
<script src="TrackballControls.js"></script>
<script src="jquery-1.10.2.min.js"></script>
</head>
<body>
<script type="text/JavaScript">
var width = window.innerWidth,
height = window.innerHeight;
// Earth params
var radius = 9.99,
segments = 32,
rotation = 0 ;
//New scene and camera
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(55, width / height, 0.01, 1000);
camera.position.z = 1;
camera.position.x = -.2;
camera.position.y = .5;
//New Renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
//Add lighting
scene.add(new THREE.AmbientLight(0x333333));
var light = new THREE.DirectionalLight(0xe4eef9, .7);
light.position.set(12,12,8);
scene.add(light);
var quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle( new THREE.Vector3( 0, 1, 0 ), Math.PI / 2 );
var sphere = createSphere(radius, segments);
//sphere.rotation.y = rotation;
sphere.rotation = new THREE.Euler().setFromQuaternion( quaternion );
scene.add(sphere)
//Create a sphere to make visualization easier.
var geometry = new THREE.SphereGeometry(10, 32, 32);
var material = new THREE.MeshPhongMaterial({
//wireframe: true,
//transparent: true
});
function createSphere(radius, segments) {
return new THREE.Mesh(
new THREE.SphereGeometry(radius, segments, segments),
new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('relief.jpg'),
bumpMap: THREE.ImageUtils.loadTexture('elev_bump_4k.jpg'),
bumpScale: 0.005,
specularMap: THREE.ImageUtils.loadTexture('wateretopo.png'),
specular: new THREE.Color('grey')
})
);
}
var clouds = createClouds(radius, segments);
clouds.rotation.y = rotation;
scene.add(clouds)
function createClouds(radius, segments) {
return new THREE.Mesh(
new THREE.SphereGeometry(radius + .003, segments, segments),
new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('n_amer_clouds.png'),
transparent: true
})
);
}
//Draw the GeoJSON
var test_json = $.getJSON("countries_states.geojson", function(data) {
drawThreeGeo(data, 10, 'sphere', {
color: 'red'
})
});
//Draw the GeoJSON loggerhead data
var test_json = $.getJSON("loggerhead-distro-cec-any.json", function(data) {
drawThreeGeo(data, 10, 'sphere', {
color: 'blue'
})
});
//Set the camera position
camera.position.z = 30;
//Enable controls
var controls = new THREE.TrackballControls(camera);
//Render the image
function render() {
controls.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
</script>
</body>
</html>
Instead of ancient r66, I used r81 (just replaced three.min.js). I modified your createSphere() function a bit, and seems it's working.
function createSphere(radius, segments) {
var sphGeom = new THREE.SphereGeometry(radius, segments, segments);
sphGeom.rotateY(THREE.Math.degToRad(-90));
return new THREE.Mesh(
sphGeom,
new THREE.MeshPhongMaterial({
map: new THREE.TextureLoader().load('relief.jpg'),
bumpMap: new THREE.TextureLoader().load('elev_bump_4k.jpg'),
bumpScale: 0.005,
specularMap: new THREE.TextureLoader().load('wateretopo.png'),
specular: new THREE.Color('grey')
})
);
}
The only thing I did was to rotate the sphere's geometry around Y-axis at -90 degrees. The result is here

customize terrain.js (three.js) Need to add lambert/wire material

I've recently started to play with three.js, and I am using terrain.js demo as a start for a design project I am working on.
I will like to add a hybrid shader "wireframe/lambert"
the default comes with wire shader only.
This is the code from the demo, using basic material:
var matrix = new THREE.MeshBasicMaterial({
color:0x10ce58,
wireframe:true
});
var geometry = new THREE.PlaneGeometry(width, height, modelWidth, modelHeight);
mesh = new THREE.Mesh(geometry, matrix);
mesh.doubleSided = false;
and I tried something like this but I only get the "lambert" rendering and not the lambert and wire combined, any ideas?
var darkMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff , shading: THREE.FlatShading, overdraw: true} );
var wireframeMaterial = new THREE.MeshBasicMaterial( { color: 0x10ce58, wireframe: true, transparent: true } );
var multiMaterial = [ darkMaterial, wireframeMaterial ];
var geometry = new THREE.PlaneGeometry(width, height, modelWidth, modelHeight);
mesh = new THREE.Mesh(geometry, multiMaterial);
mesh.doubleSided = false;
Thanks for your time in advanced,
Regards
-Manuel
This is the code I am using in my non working example for materials (http://jsfiddle.net/xnqUb/3/)
var geometry = new THREE.PlaneGeometry(width, height, model.length - 1, model.length - 1, materials);
materials = [
new THREE.MeshLambertMaterial( { color: 0xffffff, shading: THREE.FlatShading, overdraw: true } ),
new THREE.MeshLambertMaterial({ color: 0x10ce58, wireframe: true,})
];
var mesh = new THREE.Mesh(geometry);
object = THREE.SceneUtils.createMultiMaterialObject(geometry, materials);

Categories

Resources