THREE.js , texture not displayed on a sprite - three.js

I'm trying to modify the canves/lines.js example and to display textured sprites instead of dots. I load a texture from a PNG file and I have replaced regular dots with sprites. They do not show.
function init() {
var container, separation = 100, amountX = 50, amountY = 50,
particles, particle,sprite;
var crateTexture = THREE.ImageUtils.loadTexture( 'images/crate.png' );
var crateMaterial = new THREE.SpriteMaterial( { map: crateTexture, useScreenCoordinates: false, color: 0xff0000 } );
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 100;
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setClearColor (0x5bb0d2, 1);
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// particles
var PI2 = Math.PI * 2;
var material = new THREE.SpriteCanvasMaterial( {
color: 0xfffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
var geometry = new THREE.Geometry();
var x,y,z=0;
for ( var i = 0; i < 100; i ++ ) {
particle = new THREE.Sprite( crateMaterial );
particle.position.x = Math.random() * 2 - 1;
particle.position.y = Math.random() * 2 - 1;
particle.position.z = Math.random() * 2 - 1;
particle.position.normalize();
particle.position.multiplyScalar( Math.random() * 10 + 450 );
particle.scale.x = particle.scale.y = 64;
scene.add( particle );
}
what am I doing wrong ?

All I did was correct, there is an issue when reading textures from local computer. The solution is to assign proper privilages to web serwer OR to allow Chrome or other browser to read local files.
in case of Chrome, you can create a shortcut such as this:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" chrome --allow-file-access-from-files

Related

Uncaught TypeError: Cannot read property 'map' of undefined in Three.js

I am trying to create a wave effect on an image using Three.js, I am following this tutorial on youtube to do so : -
Tutorial That I Followed
After completing the code, I am getting Uncaught TypeError: Cannot read property 'map' of undefined error in my console and the wireframe is also not visible.
Maybe this is due to the new Three.js update that introduced BufferGeometry instead of normal one.
My Js Code is as follows :-
const section = document.querySelector('#wrapper');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize( window.innerWidth, window.innerHeight );
section.appendChild( renderer.domElement );
const texture = new THREE.TextureLoader().load( 'Wrapper2 Central.jpg' );
const geometry = new THREE.PlaneGeometry(7.5, 4 , 50, 30);
const material = new THREE.MeshBasicMaterial({
map: texture
});
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
cube.rotation.set(0, 0, 0)
camera.position.z = 5;
const clock = new THREE.Clock()
function animate() {
const t = clock.getElapsedTime()
cube.geometry.vertices.map( v => {
const wavex1 = 0.5 * Math.sin(v.x * 2 * t)
const wavex2 = 0.25 * Math.sin(v.x * 3 * t * 2)
const wavex3 = 0.5 * Math.sin(v.y * t)
v.z = wavex1 + wavex2 + wavex3
})
cube.geometry.verticesNeedUpdate()
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
There is no geometry.vertices anymore, as THREE.Geometry() was deprecated since r125, and how all geometry constructors return THREE.BufferGeometry, where vertices are stored in geometry.attributes.position:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.jsdelivr.net/npm/three#0.125.2/build/three.module.js";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const texture = new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/uv_grid_opengl.jpg' );
const geometry = new THREE.PlaneGeometry(7.5, 4 , 50, 30);
const material = new THREE.MeshBasicMaterial({
map: texture
});
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
cube.rotation.set(0, 0, 0)
camera.position.z = 5;
const clock = new THREE.Clock()
let v = new THREE.Vector3();
let pos = cube.geometry.attributes.position;
function animate() {
const t = clock.getElapsedTime()
for (let i = 0; i < pos.count; i++){
v.fromBufferAttribute(pos, i);
const wavex1 = 0.5 * Math.sin(v.x * 2 * t)
const wavex2 = 0.25 * Math.sin(v.x * 3 * t * 2)
const wavex3 = 0.5 * Math.sin(v.y * t)
pos.setZ(i, wavex1 + wavex2 + wavex3)
}
pos.needsUpdate = true;
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
</script>

Converting THREE.js Geometry into BufferGeometry?

I'm relatively new to THREE.js and I got this code but I'd like to reconstruct this Geometry into BufferGeometry to get the efficiency benefits. I saw this (var bufferGeometry = new THREE.BufferGeometry().fromGeometry( geometry );) as a possible solution but I could not implement it I'm sure it's simple I just lack the experience with THREE.js to recognize this.
let rainGeo = new THREE.Geometry()
for (let i = 0; i < rainCount; i++) {
rainDrop = new THREE.Vector3(
Math.random() * 120 - 60,
Math.random() * 180 - 80,
Math.random() * 130 - 60,
)
rainDrop.velocity = {}
rainDrop.velocity = 0
bufferGeometry.vertices.push(rainDrop)
}
rainMaterial = new THREE.PointsMaterial({
color: '#ffffff',
size: .3,
transparent: true,
map: THREE.ImageUtils.loadTexture(
'images/snow_mask_2.png'),
blending: THREE.AdditiveBlending,
})
rain = new THREE.Points(bufferGeometry, rainMaterial)
rain.rotation.x = -1.5707963267948963
rain.rotation.y = -3.22
scene.add(rain)
function rainVariation() {
bufferGeometry.vertices.forEach(p => {
p.velocity -= 0.1 + Math.random() * 0.1;
p.y += p.velocity;
if (p.y < -60) {
p.y = 60;
p.velocity = 0;
}
});
bufferGeometry.verticesNeedUpdate = true;
rain.rotation.y += 0.008
}
Try it with this ported code as a basis. I suggest that you manage the velocity per raindrop in a separate array (since these data are not necessary in the shader).
let camera, scene, renderer, rain;
const vertex = new THREE.Vector3();
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 100;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const vertices = [];
for (let i = 0; i < 1000; i++) {
vertices.push(
Math.random() * 120 - 60,
Math.random() * 180 - 80,
Math.random() * 130 - 60
);
}
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
const material = new THREE.PointsMaterial( { color: '#ffffff' } );
rain = new THREE.Points( geometry, material );
scene.add(rain);
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function rainVariation() {
var positionAttribute = rain.geometry.getAttribute( 'position' );
for ( var i = 0; i < positionAttribute.count; i ++ ) {
vertex.fromBufferAttribute( positionAttribute, i );
vertex.y -= 1;
if (vertex.y < - 60) {
vertex.y = 90;
}
positionAttribute.setXYZ( i, vertex.x, vertex.y, vertex.z );
}
positionAttribute.needsUpdate = true;
}
function animate() {
requestAnimationFrame( animate );
rainVariation();
renderer.render( scene, camera );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.115/build/three.js"></script>

Three.js moving the camera left and right side of the scene

I am working on a three.js prototype in which a 3d model of the train is added to the scene. My requirement is to move the camera either left / right side of the scene to view the complete train.
I tried using below code -
function onKeyDown(){
var zdelta = 20;
switch( event.keyCode ) {
case 65: // look left
camera.position.z = camera.position.z + zdelta;
}
}
But the scene was rotating rather than panning in the left side.
So it will be great help, if anyone shares their idea on this :)
Thanks,
Satheesh K
Using a keydown event listener is definitely the right approach. Try it with this code:
var scene, camera, renderer, cubeObj;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
camera.position.y = - 10;
camera.position.x = 10;
var cubeGeo = new THREE.BoxGeometry( 200, 150, 100 );
var cubeGeoMaterial = new THREE.MeshPhongMaterial( { color: 0x808080 } );
cubeObj = new THREE.Mesh( cubeGeo, cubeGeoMaterial );
cubeObj.position.x = - 70;
cubeObj.position.y = - 50;
cubeObj.position.z = 0;
cubeObj.rotation.x = 0.5;
scene.add( cubeObj );
var cubeGeo2 = new THREE.BoxGeometry( 200, 150, 100 );
var cubeGeoMaterial2 = new THREE.MeshPhongMaterial( { color: 0x808080 } );
var cubeObj2 = new THREE.Mesh( cubeGeo2, cubeGeoMaterial2 );
cubeObj2.position.x = 160;
cubeObj2.position.y = - 50;
cubeObj2.position.z = - 5;
cubeObj2.rotation.x = 0.5;
scene.add( cubeObj2 );
var cubeGeo3 = new THREE.BoxGeometry( 200, 150, 100 );
var cubeGeoMaterial3 = new THREE.MeshPhongMaterial( { color: 0x808080 } );
var cubeObj3 = new THREE.Mesh( cubeGeo3, cubeGeoMaterial3 );
cubeObj3.position.x = 440;
cubeObj3.position.y = - 50;
cubeObj3.position.z = 0;
cubeObj3.rotation.x = 0.5;
scene.add( cubeObj3 );
renderer = new THREE.WebGLRenderer( {
antialias: true
} );
var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 100, 1000, 100 );
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 500;
spotLight.shadow.camera.far = 4000;
spotLight.shadow.camera.fov = 30;
//scene.add( spotLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
scene.add( directionalLight );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xcce0ff );
document.body.appendChild( renderer.domElement );
document.addEventListener( 'keydown', onKeyDown, false );
}
function onKeyDown( event ) {
const step = 5; // world units
switch ( event.keyCode ) {
case 37:
camera.position.x -= step;
break;
case 39:
camera.position.x += step;
break;
}
}
function animate() {
renderer.render( scene, camera );
requestAnimationFrame( animate );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.114/build/three.js"></script>

Rendered object looks flat even with shadows

I'm a three.js newb. I've been playing around with it for a couple of days and haven't been able to figure out how to make my objects look more realistic. I suspect there's no simple answer for this question, but is there anything I can do to improve my rendering quality without going into the depth of "rendering science"? Maybe I'm missing some configs. Thank you for any advice!
Here's the relevant code used in rendering a kitchen cabinet frame.
this.renderer = new THREE.WebGLRenderer({ antialias: true })
this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight)
this.renderer.sortObjects = false
this.renderer.setClearColor(0xf0f0f0)
this.renderer.gammaFactor = 2.2
this.renderer.gammaOutput = true
this.renderer.setPixelRatio(window.devicePixelRatio)
this.renderer.shadowMap.enabled = true
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
const light = new THREE.AmbientLight(0xffffff, 0.8)
const light2 = new THREE.PointLight(0xffffff, 0.3)
light2.position.set(400, 400, 400)
light2.shadow.camera.near = 10
light2.shadow.camera.far = 10000
light2.shadow.mapSize.width = 2048
light2.shadow.mapSize.height = 2048
light2.castShadow = true
this.scene.add(light2)
this.scene.add(light)
const material = new THREE.MeshStandardMaterial({ color: 0xffffff, metalness: 0, roughness: 0 })
let scene, camera, controls, ambient, point, loader, renderer, container, stats;
const targetRotation = 0;
const targetRotationOnMouseDown = 0;
const mouseX = 0;
const mouseXOnMouseDown = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
init();
animate();
var box, b1, b2, b3;
function init() {
// Create a scene which will hold all our meshes to be rendered
scene = new THREE.Scene();
// Create and position a camera
camera = new THREE.PerspectiveCamera(
60, // Field of view
window.innerWidth / window.innerHeight, // Aspect ratio
/*window.innerWidth / -8,
window.innerWidth / 8,
window.innerHeight / 8,
window.innerHeight / -8,
*/
0.1, // Near clipping pane
1000 // Far clipping pane
);
scene.add(camera)
// Reposition the camera
camera.position.set(0, 5, 10);
// Point the camera at a given coordinate
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add orbit control
controls = new THREE.OrbitControls(camera);
controls.target.set(0, -0.5, 0);
controls.update();
// Add an ambient lights
ambient = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambient);
// Add a point light that will cast shadows
point = new THREE.PointLight(0xffffff, 1);
point.position.set(25, 50, 25);
point.castShadow = true;
point.shadow.mapSize.width = 1024;
point.shadow.mapSize.height = 1024;
scene.add(point);
group = new THREE.Group();
group.position.y = 0;
scene.add(group);
rotationAnchor = new THREE.Object3D()
group.add(rotationAnchor);
box = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({
color: 'grey'
}))
b1 = box.clone();
b2 = box.clone();
b3 = box.clone();
b3.material = b3.material.clone()
b3.material.color.set('red')
group.add(box);
group.add(b1);
b1.position.y += 1
group.add(b2);
b2.position.z += 1
rotationAnchor.add(b3);
rotationAnchor.position.set(0.5, 0.5, 1.5)
b3.position.set(-.5, -.5, -.5)
// Create a renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
// Set size
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Set color
renderer.setClearColor(0xf8a5c2);
renderer.gammaOutput = true;
// Enable shadow mapping
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Append to the document
container = document.createElement("div");
document.body.appendChild(container);
document.body.appendChild(renderer.domElement);
// Add resize listener
window.addEventListener("resize", onWindowResize, false);
// Enable FPS stats
stats = new Stats();
container.appendChild(stats.dom);
var gui = new dat.GUI({
height: 5 * 32 - 1
});
let params = {
'test': 4,
'bevelThickness': 1,
'bevelSize': 1.5,
'bevelSegments': 3
}
gui.add(params, 'test', 0, 10).onChange(val => {
test = val
})
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
rotationAnchor.rotation.z = (Math.cos(performance.now() * 0.001) * Math.PI * 0.25) + (Math.PI * 1.25)
requestAnimationFrame(animate);
// Re-render scene
renderer.render(scene, camera);
// Update stats
stats.update();
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.min.js"></script>

Directional light shadow of boxes is not rendered on plane mesh

The following light is added to my scene:
var light = new THREE.DirectionalLight( 0xffffff, 1.5 );
light.position.set( 1000, 1000, 1000 );
light.castShadow = true;
light.shadow = new THREE.DirectionalLightShadow(
new THREE.OrthographicCamera(
-100, 100,
100, -100, -500, 500 ) );
light.shadow.bias = - 0.00022;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
The plane is as follows:
var planeGeometry = new THREE.PlaneGeometry( 200, 200 );
planeGeometry.rotateX( - Math.PI / 2 );
var planeMaterial = new THREE.ShadowMaterial();
planeMaterial.opacity = 1;
var plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = 0;
plane.receiveShadow = true;
scene.add( plane );
The boxes are:
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
for ( var i = 0; i < 20; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
object.scale.x = Math.random() * 5 + 1;
object.scale.y = Math.random() * 5 + 1;
object.scale.z = Math.random() * 5 + 1;
object.position.x = Math.random() * 30;
object.position.y = object.scale.y / 2.0;
object.position.z = Math.random() * 60;
object.castShadow = true;
object.receiveShadow = true;
scene.add( object );
}
Directional light shadow of boxes is not rendered on plane mesh. Could you pls help what do I wrong? Please refer to the following screenshot.
Some of the shadow map properties have been renamed in the recent versions.
Setting up the renderer for shadow maps (and choosing the more computational expensive shadow map type):
var renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap
Setting up the light (notice how it also works with THREE.PointLight):
var light = new THREE.PointLight( 0xffffff, 1, 100 );
light.position.set( 0, 12, 0 );
light.castShadow = true; // default false
light.shadow.mapSize.width = 1024; // default 512
light.shadow.mapSize.height = 1024; // default 512
light.shadow.camera.near = 2; // default 0.5
light.shadow.camera.far = 100; // default 500
scene.add( light );
For plane material use var planeMaterial = new THREE.MeshLambertMaterial();
( ShadowMaterial works from r77 https://github.com/mrdoob/three.js/issues/1791)
var renderer = new THREE.WebGLRenderer( {antialias:true, alpha: true } );
renderer.shadowMap.enabled = true; // enable shadows rendering
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // to antialias the shadow
var light_target = new THREE.Object3D(); // believe me
light_target.position.set(0,0,0); // not necessary, but to be clear
var light = new THREE.DirectionalLight( 0xffffff,1.5,1000) );
light.position.set(1000, 1000, 1000);
light.target = light_target;
light.castShadow = true;
light.shadowMapWidth = 2048*2; // px of the rendered shadow texture
light.shadowMapHeight = 2048*2; // px of the rendered shadow texture
var d = 100; /// your plane is 200x200 and we targeted its center
var distance = light.position.distanceTo(light_target.position);
light.shadowCameraLeft = -d;
light.shadowCameraRight = d; light.shadowCameraTop = d; light.shadowCameraBottom = -d; light.shadowCameraNear = distance-d; light.shadowCameraFar = distance+d; light.shadowBias = -0.001;
light.shadowDarkness = 0.5;
scene.add( light ); // you missed this!

Resources