How to make a moving/sliding element in Three.js? - javascript

I discovered three.js which I love to use now, but I built a rotating car animation with keyboard arrows to control it, but I can't find out how to make a sliding animation (e.g.,
-> -> ->
) Instead of a rotation animation. I tried searching but I don't even know how to explain it other than a sliding animation. Does someone know how / if you can do this?
Here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ThreeJS Animation</title>
<style>
body {
margin: 0;
font-weight: 100;
float: none;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.js"></script>
<script>
let carBottomColor = "#999";
let carTopColor = "#FFF";
let carWindowColor = "#666";
const scene = new THREE.Scene();
scene.background = new THREE.Color("#f1f1f1");
const car = createCar();
scene.add(car);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(200, 500, 300);
scene.add(dirLight);
const aspectRatio = window.innerWidth / window.innerHeight;
const cameraWidth = 300;
const cameraHeight = cameraWidth / aspectRatio;
const camera = new THREE.OrthographicCamera(
cameraWidth / -2, // left
cameraWidth / 2, // right
cameraHeight / 2, // top
cameraHeight / -2, // bottom
200, // near plane
800 // far plane
);
camera.position.set(200, 200, 200);
camera.lookAt(0, 10, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
let x = 0;
let y = 0;
let keydown = '';
document.body.addEventListener('keydown', e => {
keydown = e.key;
});
document.body.addEventListener('keyup', e => {
keydown = '';
});
const update = () => {
switch (keydown) {
case 'ArrowUp':
x -= 0.1;
car.rotation.x = x;
break;
case 'ArrowDown':
x += 0.1;
car.rotation.x = x;
break;
case 'ArrowLeft':
y -= 0.1;
car.rotation.y = y;
break;
case 'ArrowRight':
y += 0.1;
car.rotation.y = y;
break;
}
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
document.body.appendChild(renderer.domElement);
function createCar() {
const car = new THREE.Group();
const backWheel = createWheels();
backWheel.position.y = 6;
backWheel.position.x = -18;
car.add(backWheel);
const frontWheel = createWheels();
frontWheel.position.y = 6;
frontWheel.position.x = 18;
car.add(frontWheel);
const main = new THREE.Mesh(
new THREE.BoxGeometry(60, 15, 30),
new THREE.MeshLambertMaterial({ color: carBottomColor })
);
main.position.y = 12;
car.add(main);
const carFrontTexture = getCarFrontTexture();
const carBackTexture = getCarFrontTexture();
const carRightSideTexture = getCarSideTexture();
const carLeftSideTexture = getCarSideTexture();
carLeftSideTexture.center = new THREE.Vector2(0.5, 0.5);
carLeftSideTexture.rotation = Math.PI;
carLeftSideTexture.flipY = false;
const cabin = new THREE.Mesh(new THREE.BoxGeometry(33, 12, 24), [
new THREE.MeshLambertMaterial({ map: carFrontTexture }),
new THREE.MeshLambertMaterial({ map: carBackTexture }),
new THREE.MeshLambertMaterial({ color: carTopColor }), // top
new THREE.MeshLambertMaterial({ color: carTopColor }), // bottom
new THREE.MeshLambertMaterial({ map: carRightSideTexture }),
new THREE.MeshLambertMaterial({ map: carLeftSideTexture })
]);
cabin.position.x = -6;
cabin.position.y = 25.5;
car.add(cabin);
return car;
}
function createWheels() {
const geometry = new THREE.BoxGeometry(12, 12, 33);
const material = new THREE.MeshLambertMaterial({ color: "#333" });
const wheel = new THREE.Mesh(geometry, material);
return wheel;
}
function getCarFrontTexture() {
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 32;
const context = canvas.getContext("2d");
context.fillStyle = "#ffffff";
context.fillRect(0, 0, 64, 32);
context.fillStyle = carWindowColor || carBaseColor; // Wheel color
context.fillRect(8, 8, 48, 24);
return new THREE.CanvasTexture(canvas);
}
function getCarSideTexture() {
const canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 32;
const context = canvas.getContext("2d");
context.fillStyle = "#ffffff";
context.fillRect(0, 0, 128, 32);
context.fillStyle = carWindowColor || carBaseColor; // Wheel color
context.fillRect(10, 8, 38, 24);
context.fillRect(58, 8, 60, 24);
return new THREE.CanvasTexture(canvas);
}
</script>
</body>
</html>

I made some assumptions, but I think what you're trying to do is rotate the car along the y-axis when pressing left/right arrows, and you're trying to move the card forward when pressing up/down arrows. If that's the case, then you can do car.translateX() to create that forward/backward motion, and car.rotation.y to make the car turn.
Here's the modified example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ThreeJS Animation</title>
<style>
body {
margin: 0;
font-weight: 100;
float: none;
overflow: hidden;
}
canvas {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script src="https://threejs.org/build/three.js"></script>
<script>
let carBottomColor = "#999";
let carTopColor = "#FFF";
let carWindowColor = "#666";
const scene = new THREE.Scene();
scene.background = new THREE.Color("#f1f1f1");
const car = createCar();
scene.add(car);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(200, 500, 300);
scene.add(dirLight);
const aspectRatio = window.innerWidth / window.innerHeight;
const cameraWidth = 300;
const cameraHeight = cameraWidth / aspectRatio;
const camera = new THREE.OrthographicCamera(
cameraWidth / -2, // left
cameraWidth / 2, // right
cameraHeight / 2, // top
cameraHeight / -2, // bottom
200, // near plane
800 // far plane
);
camera.position.set(200, 200, 200);
camera.lookAt(0, 10, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
let x = 0;
let y = 0;
let keydown = '';
document.body.addEventListener('keydown', e => {
e.preventDefault();
keydown = e.key;
});
document.body.addEventListener('keyup', e => {
keydown = '';
});
const update = () => {
switch (keydown) {
case 'ArrowUp':
car.translateX(1.0);
break;
case 'ArrowDown':
car.translateX(-1.0);
break;
case 'ArrowLeft':
y += 0.1;
car.rotation.y = y;
break;
case 'ArrowRight':
y -= 0.1;
car.rotation.y = y;
break;
}
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
document.body.appendChild(renderer.domElement);
function createCar() {
const car = new THREE.Group();
const backWheel = createWheels();
backWheel.position.y = 6;
backWheel.position.x = -18;
car.add(backWheel);
const frontWheel = createWheels();
frontWheel.position.y = 6;
frontWheel.position.x = 18;
car.add(frontWheel);
const main = new THREE.Mesh(
new THREE.BoxGeometry(60, 15, 30),
new THREE.MeshLambertMaterial({ color: carBottomColor })
);
main.position.y = 12;
car.add(main);
const carFrontTexture = getCarFrontTexture();
const carBackTexture = getCarFrontTexture();
const carRightSideTexture = getCarSideTexture();
const carLeftSideTexture = getCarSideTexture();
carLeftSideTexture.center = new THREE.Vector2(0.5, 0.5);
carLeftSideTexture.rotation = Math.PI;
carLeftSideTexture.flipY = false;
const cabin = new THREE.Mesh(new THREE.BoxGeometry(33, 12, 24), [
new THREE.MeshLambertMaterial({ map: carFrontTexture }),
new THREE.MeshLambertMaterial({ map: carBackTexture }),
new THREE.MeshLambertMaterial({ color: carTopColor }), // top
new THREE.MeshLambertMaterial({ color: carTopColor }), // bottom
new THREE.MeshLambertMaterial({ map: carRightSideTexture }),
new THREE.MeshLambertMaterial({ map: carLeftSideTexture })
]);
cabin.position.x = -6;
cabin.position.y = 25.5;
car.add(cabin);
return car;
}
function createWheels() {
const geometry = new THREE.BoxGeometry(12, 12, 33);
const material = new THREE.MeshLambertMaterial({ color: "#333" });
const wheel = new THREE.Mesh(geometry, material);
return wheel;
}
function getCarFrontTexture() {
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = 32;
const context = canvas.getContext("2d");
context.fillStyle = "#ffffff";
context.fillRect(0, 0, 64, 32);
context.fillStyle = carWindowColor || carBaseColor; // Wheel color
context.fillRect(8, 8, 48, 24);
return new THREE.CanvasTexture(canvas);
}
function getCarSideTexture() {
const canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 32;
const context = canvas.getContext("2d");
context.fillStyle = "#ffffff";
context.fillRect(0, 0, 128, 32);
context.fillStyle = carWindowColor || carBaseColor; // Wheel color
context.fillRect(10, 8, 38, 24);
context.fillRect(58, 8, 60, 24);
return new THREE.CanvasTexture(canvas);
}
</script>
</body>
</html>

Related

How to blend text onto a texture in three.js?

I want to add text to a can of beans. The example code uses render.copyTextureToTexture to blend textures together.
However, when I try to use it doesn't do anything.
When I tried to display textTexture on the cylinder it was fully transparent. Is the texture made before text is rendered in the first canvas?
Or do I need to somehow wait until the image is loaded and only then use copyTextureToTexture to add the text?
//import * as THREE from 'https://threejs.org/build/three.module.js';
const ctx = document.getElementById('textCanvas').getContext('2d');
ctx.font = '2em Lucida Sans Unicode';
ctx.fillText('Text to be added', 0, 75, 500);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, 4 / 3, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(320, 240);
document.body.appendChild(renderer.domElement);
var img1 = "https://i.imgur.com/IIToHlc.png";
var texture1 = new THREE.TextureLoader().load(img1);
textTexture = new THREE.CanvasTexture(document.getElementById('textCanvas'));
const position = new THREE.Vector2();
position.x = 1;
position.y = 1;
renderer.copyTextureToTexture(position, textTexture, texture1);
const geometry = new THREE.CylinderGeometry(5, 5, 12, 32);
const material = [
new THREE.MeshLambertMaterial({
map: texture1
}),
new THREE.MeshLambertMaterial({
color: 0x5f5f5f
}),
new THREE.MeshLambertMaterial({
color: 0x5f5f7f
})
]
const cylinder = new THREE.Mesh(geometry, material);
scene.add(cylinder);
const light = new THREE.AmbientLight(0x404040); // soft white light
scene.add(light);
var directionalLight = new THREE.DirectionalLight(0xafafaf, 1);
directionalLight.position.z = 1;
directionalLight.position.x = -0.5;
directionalLight.position.y = 0.2;
scene.add(directionalLight);
camera.position.z = 15;
camera.position.y = 0;
var zRotation = -0.005;
var maxZRotation = 0.5;
cylinder.rotation.z = maxZRotation;
var animate = function() {
requestAnimationFrame(animate);
cylinder.rotation.y += -0.02;
cylinder.rotation.z += zRotation;
if (cylinder.rotation.z >= maxZRotation) {
zRotation = -0.005;
}
if (cylinder.rotation.z <= -maxZRotation) {
zRotation = 0.005;
}
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
overflow: hidden;
background-color: rgba(0, 0, 0, 0);
}
canvas {
background-color: rgba(0, 0, 0, 0);
}
<!DOCTYPE html>
<html>
<head>
<title>Floating can of BEANS</title>
</head>
<body>
<canvas id="textCanvas" width="500" height="150" style="height:150px; width:500px; position: absolute; top: 512px; "></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</body>
You can modify a material to make it able to blend material's color with the text texture:
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {
OrbitControls
} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 3, 8);
let renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(innerWidth, innerHeight);
//renderer.setClearColor(0xffffff)
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.25));
let c = document.createElement("canvas");
c.width = 256;
c.height = 128;
let ctx = c.getContext("2d");
ctx.fillStyle = "rgba(255, 255, 255, 0)";
ctx.fillRect(0, 0, c.width, c.height);
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "magenta";
ctx.font = "bold 36px Arial";
let text = "I love Three.js";
ctx.fillText(text, c.width * 0.5, c.height * 0.5);
ctx.strokeStyle = "red";
ctx.strokeText(text, c.width * 0.5, c.height * 0.5);
let tex = new THREE.CanvasTexture(c);
tex.offset.y = 0.25;
let u = {
time: {value: 0},
textTex: {value: tex}
}
let g = new THREE.CylinderGeometry(2, 2, 4.5, 36, 1, true);
g.rotateY(Math.PI);
let m = new THREE.MeshLambertMaterial({
color: 0x7f7f64,
map: new THREE.TextureLoader().load("https://threejs.org/examples/textures/floors/FloorsCheckerboard_S_Diffuse.jpg", tex => {
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
tex.repeat.set( 3, 1 );
}),
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.uniforms.time = u.time;
shader.uniforms.textTex = u.textTex;
shader.fragmentShader = `
uniform float time;
uniform sampler2D textTex;
${shader.fragmentShader}
`.replace(
`#include <map_fragment>`,
`#include <map_fragment>
vec4 textCol = texture(textTex, (vUv * 2. - 0.5) + vec2(-2., sin(time) * 0.25));
vec3 col = mix(diffuseColor.rgb, textCol.rgb, textCol.a);
diffuseColor = vec4( col, opacity );
`
);
//console.log(shader.fragmentShader);
}
});
m.defines = {"USE_UV" : ""};
let o = new THREE.Mesh(g, m);
scene.add(o);
let clock = new THREE.Clock();
renderer.setAnimationLoop(() => {
let time = clock.getElapsedTime();
u.time.value = time;
renderer.render(scene, camera);
});
</script>

Accessing the vertices using the new PlaneBufferGeometry in ThreeJS

I understand PlaneGeometry has been deprecated and we should use PlaneBufferGeometry with the latest releases. The following code worked with the build before R125, I just don't know how to tweak the code to make it work with PlaneBufferGeometry:
var flagGeom = new THREE.PlaneBufferGeometry(4.5, 2.2, 40, 100, 200); // Replaced "PlaneGeometry " with "PlaneBufferGeometry" here.
flagGeom.translate(2.2, 1.1, 0);
flagGeom.vertices.forEach(v => { // Undefined error here.
v.init = v.clone()
});
Looks like your question is related to this forum topic.
You can do the things this way (just an option, not the ultimate solution): use an additional array stored in userData, and use .setXYZ() method.
body{
overflow: hidden;
margin: 0;
}
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script type="module">
console.clear();
import * as THREE from 'https://cdn.skypack.dev/three#0.129.0/build/three.module.js';
import { OrbitControls } from 'https://cdn.skypack.dev/three#0.129.0/examples/jsm/controls/OrbitControls.js';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(-1, 0.5, 2).setLength(2.5);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x401000);
document.body.appendChild(renderer.domElement);
var controls = new OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xff5500, 2);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xff0000, 1));
var loader = new THREE.TextureLoader();
// flag
var flagGeom = new THREE.PlaneGeometry(4, 2, 40, 20);
flagGeom.translate(2, 1, 0);
let pos = flagGeom.attributes.position;
flagGeom.userData = {
init: []
}
for(let i = 0; i < pos.count; i++){
flagGeom.userData.init.push(new THREE.Vector3().fromBufferAttribute(pos, i));
}
console.log(flagGeom.userData.init);
var flagMat = new THREE.MeshStandardMaterial({
color: 0x777777,
map: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_map.png"),
alphaMap: loader.load("https://cywarr.github.io/small-shop/PW_flag/PW_flag_alphaMap.png"),
alphaTest: 0.5,
side: THREE.DoubleSide,
metalness: 0.5,
roughness: 0.5
});
var flag = new THREE.Mesh(flagGeom, flagMat);
flag.position.set(-2, -1, 0);
flag.rotation.x = THREE.Math.degToRad(-18);
scene.add(flag);
// staff
var staff = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 4, 8), new THREE.MeshStandardMaterial({
color: "black"
}));
flag.add(staff);
// background canvas texture
var canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 64;
var ctx = canvas.getContext('2d');
var image = ctx.createImageData(canvas.width, canvas.height);
var data = image.data;
var canvasTexture = new THREE.CanvasTexture(canvas);
scene.background = canvasTexture;
window.addEventListener( 'resize', onWindowResize );
var clock = new THREE.Clock();
var delta = 0;
var time = 0;
var v = new THREE.Vector3();
render();
function render() {
requestAnimationFrame(render);
delta = clock.getDelta();
time += delta;
flagGeom.userData.init.forEach( (vi, idx) => {
v.copy(vi);
let yFade = Math.sin(v.y / flagGeom.parameters.height * Math.PI) * 0.25;
v.x = v.x + yFade;
let xFade = (v.x / flagGeom.parameters.width);
v.z = noise.perlin2((v.x - (time * 2)) / flagGeom.parameters.width * 4, v.y / flagGeom.parameters.height * 2) * xFade;
pos.setXYZ(idx, v.x, v.y, v.z);
});
flagGeom.computeVertexNormals();
pos.needsUpdate = true;
drawFrame(time);
canvasTexture.needsUpdate = true;
renderer.render(scene, camera);
}
function drawFrame(time) {
var cWidth = canvas.width;
var cHeight = canvas.height;
for (var x = 0; x < cWidth; x++) {
for (var y = 0; y < cHeight; y++) {
var value = Math.abs(noise.simplex3(x / cWidth, y / cHeight, time * 0.25));
var cell = (x + y * cWidth) * 4;
data[cell] = 128 + Math.floor(128 * value);
data[cell + 1] = Math.floor(256 * value);
data[cell + 2] = 0;
data[cell + 3] = 255; // alpha.
}
}
ctx.fillColor = 'black';
ctx.fillRect(0, 0, 100, 100);
ctx.putImageData(image, 0, 0);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>

How to make an object float on water like Buoyancy effect using threejs

I have created a water effect using THREE.WATER and added few gltf objects to the scene, now i am trying to make the added objects float on water like a boat is floating.
here is the code attached.
var scene, camera, renderer, ambient, directional, controls, stats, clock, imageLoader;
var terrain, water, mixer, model,modelL;
var lillyCoordPosition = [
[3.5, -6.1, -18],
[8.5, -6.1, -18],
[13.3, -6.1, -18],
[3.5, -8.1, -16],
[8.5, -8.1, -16],
[13.3, -8.1, -16],
[3.5, -7.1, -17],
[8.5, -7.1, -17],
[13.3, -7.1, -17]
];
init();
animate();
resize();
function resize() {
window.addEventListener("resize", function () {
let width = window.innerWidth;
let height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
}
function init() {
clock = new THREE.Clock();
imageLoader = new THREE.TextureLoader();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20000);
camera.position.y = 6.5;
camera.position.z = 2;
ambient = new THREE.AmbientLight(0xe8eb34);
scene.add(ambient);
directional = new THREE.DirectionalLight(0xe8eb34, 1);
directional.position.set(30, 80, 20);
scene.add(directional);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0x70c8ff, 1);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// controls = new THREE.OrbitControls( camera, renderer.domElement );
// controls.target.set(0,120,-500);
// controls.update();
document.body.appendChild(renderer.domElement);
scene.fog = new THREE.FogExp2(0xEDC9AF, 0.003);
loadWater();
/*lillyCoordPosition.map( position => {
loadLilly(position);
})*/
}
function loadBar() {
var texture = new THREE.Texture(generateTexture());
texture.needsUpdate = true;
var geometry = new THREE.PlaneGeometry(0.060, 10);
var material = new THREE.MeshBasicMaterial({
map: texture,
// color: 0xffffff,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
plane.position.set(1.500, 6.390, 0.000);
plane.scale.set(1, 0.110, 1);
//border
var geo = new THREE.EdgesGeometry(plane.geometry);
var mat = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 4 });
var wireframe = new THREE.LineSegments(geo, mat);
wireframe.renderOrder = 1; // make sure wireframes are rendered 2nd
plane.add(wireframe);
scene.add(plane);
//indicator
var indicatorGeo = new THREE.PlaneGeometry(0.110, 0.300);
var indMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide });
var indicator = new THREE.Mesh(indicatorGeo, indMaterial);
indicator.position.set(1.500, 6.000, 0.000);
indicator.scale.set(1, 0.110, 1);
scene.add(indicator);
}
function generateTexture() {
var size = 25;
canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
var context = canvas.getContext('2d');
var gradient = context.createLinearGradient(0, size, 0, 0);
gradient.addColorStop(0, '#ff0000');
gradient.addColorStop(1, '#00ff00');
context.fillStyle = gradient;
context.fillRect(0, 0, size, size);
return canvas;
}
function loadLilly(coord){
var loader = new THREE.GLTFLoader();
loader.load('model/lillypad/scene.gltf', function(lilly){
modelL = lilly.scene;
modelL.scale.set(3,1.5,1.5);
modelL.position.z = coord[2];
modelL.position.y = coord[1];
modelL.position.x = coord[0];
// modelL.position.x = i*1.6;
scene.add(modelL);
});
}
function loadFrog() {
var loader = new THREE.GLTFLoader();
loader.load(
'model/frog/scene.gltf', function (gltf) {
//console.log(gltf.animations[0]);
model = gltf.scene;
//console.log(model);
model.scale.set(0.7, 0.7, 1.1);
model.position.z = -30;
scene.add(model);
mixer = new THREE.AnimationMixer(model);
mixer.clipAction(gltf.animations[0]).play();
});
}
function loadPlant() {
var loader = new THREE.GLTFLoader();
loader.load('model/forest_2/scene.gltf', function (plant) {
var modelP = plant.scene;
modelP.scale.set(46, 40, 40);
modelP.position.z = -80;
modelP.position.y = 10;
modelP.position.x = 4;
modelP.rotation.y = 20;
scene.add(modelP);
});
}
function loadWater() {
var geo = new THREE.PlaneBufferGeometry(15000, 15000, 10, 10);
var c = directional.position.clone();
var normal = imageLoader.load('https://www.titansoftime.com/textures/water/waternormals.jpg');
normal.wrapS = THREE.RepeatWrapping;
normal.wrapT = THREE.RepeatWrapping;
water = new THREE.Water(geo, {
textureWidth: 204,
textureHeight: 204,
waterNormals: normal,
alpha: 0.01,
fog: true,
distortionScale: 10.0,
sunDirection: c.normalize(),
sunColor: 0x73a9ff,
waterColor: 0x73a9ff,
side: THREE.DoubleSide
});
water.rotation.x = - Math.PI * 0.5;
water.matrixAutoUpdate = false;
water.rotationAutoUpdate = false;
water.updateMatrix();
water.name = 'water';
scene.add(water);
}
function animate() {
requestAnimationFrame(animate);
var delta = clock.getDelta();
if (water) {
water.material.uniforms.time.value += 0.5 * delta;
}
if (mixer != null) mixer.update(delta);
renderer.render(scene, camera);
}
<body>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
body {
overflow: hidden;
/* background:url('https://www.publicdomainpictures.net/pictures/30000/velka/blue-sky-1330598792xLu.jpg'); */
}
canvas {
width: 100vw;
height: 100vh;
position: absolute;
left: 0;
}
#question {
width: fit-content;
height: 90px;
background-color: rgba(0, 0, 0, 0.2);
position: absolute;
top: 50px;
left: 30%;
border-radius: 20px;
color: white;
text-align: center;
z-index: 1;
box-shadow: 0 10px 10px rgba(0, 1, 0, 0.3);
padding: 8px;
}
.score,
.pause {
width: 90px;
height: 90px;
border-radius: 20px;
background-color: rgba(0, 0, 0, 0.2);
box-shadow: 0 10px 10px rgba(0, 1, 0, 0.3);
position: absolute;
top: 50px;
z-index: 1;
right: 10;
text-align: center;
color: white;
}
</style>
<div id="question">
</div>
<span class="score">
<h1>23</h1>
</span>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/objects/Water.js"></script>
<script src="index.js"></script>
</body>
In the above code, loadLilly() is a function which loads lillypads on the water, but i want the lillypads to float on water.
lillypad object: https://sketchfab.com/3d-models/lilypad-8c8285abc29f4d6cb5836f23037b741b
Please help me in resolving the issue, I have tried using obje.position.y but of no use.
Thank you.
You can load that lilly model once and then re-use its geometry and material in a loop, creating a new mesh for each lilly.
In the purpose of an example, I simply created lilly's geometry and material in the code.
Effect of floating is made with sin function, applied to y-coordinate. You can use the same principle to make lillies move on any coordinate axis.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 2.5, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
scene.add(new THREE.GridHelper(10, 100, "aqua", "aqua"));
// lilly geom
let lCurve = new THREE.EllipseCurve(0, 0, 1, 1, 0, 1.9 * Math.PI);
let lPts = lCurve.getPoints(32);
lPts.push(new THREE.Vector2());
let lShape = new THREE.Shape(lPts);
let lGeom = new THREE.ExtrudeBufferGeometry(lShape, {
depth: 0.25,
bevelEnabled: false
});
lGeom.rotateX(-Math.PI * 0.5);
lGeom.translate(0, -0.125, 0);
let lMat = new THREE.MeshLambertMaterial({
color: "green"
});
// lillies
var lillies = [];
var lillyCoordPosition = [
[-2.5, -2.5],
[0, -2.5],
[2.5, -2.5],
[-2.5, 0],
[0, 0],
[2.5, 0],
[-2.5, 2.5],
[0, 2.5],
[2.5, 2.5]
].forEach(p => {
let l = new THREE.Mesh(lGeom, lMat);
l.position.set(p[0], 0, p[1]);
l.rotation.y = Math.random() * Math.PI * 2; // random rotation
let s = Math.random() * 0.25 + 0.75; // slightly different size of each one
l.scale.set(s, 1, s);
l.userData.initFloating = Math.random() * Math.PI * 2; // initial value for start of floating
lillies.push(l);
scene.add(l);
});
var clock = new THREE.Clock();
renderer.setAnimationLoop(al);
function al() {
let t = clock.getElapsedTime();
lillies.forEach(lil => {
lil.position.y = Math.sin(lil.userData.initFloating + t) * 0.05;
});
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Directional/spotlight light in threejs with helper

i m following this guide for the light Light in Threejs
and i already add some light in my scene.
Now i m try to add a light on the character of my game, but still dont work.
i use the same code of the guide changing just the position.set
const color = 0xFFFFFF;
const intensity = 1;
const light2 = new THREE.SpotLight(color, intensity,0,Math.PI/3);
light2.position.set(100,-5000,1000);
light2.target = lightTarget;
light2.castShadow = true;
const helper2 = new THREE.SpotLightHelper(light2);
and after i add in this way to my character
self.flame.add( helper2 );
self.flame.add(lightTarget);
self.flame.add(light2);
I added a helper too, but if I use just the helper in the scene , so if comment
self.flame.add(light2)
I see the position of the light in perfect way, when add the light the helper disappear (in other light don't happened) and the light go as her want.
Someone can help me?
The helpers have to be parented to the scene (or at least the SpotLightHelper does). You may or may not want to parent the target to the scene.
You also need to call helper.update for each helper
'use strict';
/* global THREE, dat */
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(0, 5, 0);
controls.update();
const scene = new THREE.Scene();
scene.background = new THREE.Color('black');
{
const planeSize = 40;
const loader = new THREE.TextureLoader();
const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.magFilter = THREE.NearestFilter;
const repeats = planeSize / 2;
texture.repeat.set(repeats, repeats);
const planeGeo = new THREE.PlaneBufferGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
map: texture,
side: THREE.DoubleSide,
});
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
scene.add(mesh);
}
const cubes = [];
let parent = scene;
{
const cubeSize = 1;
const cubeGeo = new THREE.BoxBufferGeometry(cubeSize, cubeSize, cubeSize);
const cubeMat = new THREE.MeshPhongMaterial({
color: '#8AC',
emissive: '#333',
});
for (let i = 0; i < 6; ++i) {
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.position.set(1, 0, 0);
parent.add(mesh);
cubes.push(mesh);
parent = mesh;
}
}
cubes[0].position.set(-3, 7, 0);
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.SpotLight(color, intensity);
light.position.set(0, 0, 0);
light.target.position.set(0, -1, 0);
parent.add(light);
parent.add(light.target);
//scene.add(light.target);
const helper = new THREE.SpotLightHelper(light);
scene.add(helper);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
for (const cube of cubes) {
cube.rotation.z = Math.sin(time) * .4;
}
light.angle = THREE.Math.lerp(
THREE.Math.degToRad(20),
THREE.Math.degToRad(80),
Math.sin(time * 0.77) * 0.5 + 0.5);
helper.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
html, body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
<canvas id="c"></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r105/js/controls/OrbitControls.js"></script>

Trying to create boundaries for a movable cube in three.js

I'm trying to create a simple app where I can move a cube either left or right. I've got that part figured out. Now I'm trying to make it so that the cube cannot leave the plane. What's the easiest way to go about this?
Below is the code I currently have.
<html>
<head>
<title>Time Car</title>
<style>
body {
width: 100%;
height: 100%;
margin: 0;
padding 0;
}
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script src="js/Detector.js"></script>
<script>
var scene, camera, renderer, object, raycaster, board1, board2, board3, board4;
var vy = 0,
vx = 0,
direction = "",
gravity = 0.3;
function init() {
if (!Detector.webgl) Detector.addGetWebGLMessage();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xCCFFFF);
document.body.appendChild(renderer.domElement);
var light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(1, 1, 1).normalize();
scene.add(light);
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(-1, -1, -1).normalize();
scene.add(light);
var geometry = new THREE.BoxGeometry(20, 10, 10);
object = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0x3333FF
}));
scene.add(object);
var geometry = new THREE.BoxGeometry(80, 160, 10);
board1 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xCC0000
}));
board1.position.set(0, 0, -200);
scene.add(board1);
var geometry = new THREE.BoxGeometry(80, 160, 10);
board2 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xCC0000
}));
board2.position.set(400, 0, -200);
scene.add(board2);
var geometry = new THREE.BoxGeometry(80, 160, 10);
board3 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xCC0000
}));
board3.position.set(800, 0, -200);
scene.add(board3);
var geometry = new THREE.BoxGeometry(80, 160, 10);
board4 = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0xCC0000
}));
board4.position.set(1200, 0, -200);
scene.add(board4);
geometry = new THREE.PlaneGeometry(5000, 800, 800);
var plane = new THREE.Mesh(geometry, new THREE.MeshLambertMaterial({
color: 0x99FF66
}));
plane.rotation.x = -Math.PI / 2;
plane.position.set(0, -20, 0);
scene.add(plane);
camera.position.set(0, 100, 100);
raycaster = new THREE.Raycaster();
raycaster.ray.direction.set(0, -1, 0);
render();
}
function render() {
requestAnimationFrame(render);
if (direction == "left") {
vx = -2;
}
if (direction == "right") {
vx = 2;
}
object.position.x += vx;
vx = vx * 0.95;
camera.lookAt(object.position);
camera.position.x += (((object.position.x - 20) - camera.position.x)) * 0.03;
camera.position.y += (((object.position.y + 50) - camera.position.y));
renderer.render(scene, camera);
}
window.addEventListener('keydown', function (e) {
switch (e.keyCode) {
case 65: //left
direction = "left";
break;
break;
case 68: //right
direction = "right";
break;
};
}, false);
window.addEventListener('keyup', function (e) {
switch (e.keyCode) {
case 65: //left
direction = "";
break;
case 68: //right
direction = "";
break;
};
}, false);
init();
</script>
</body>
</html>
If I got you right. You set the plane as
geometry = new THREE.PlaneGeometry(5000, 800, 800);
...
plane.position.set(0, -20, 0);
Here you can compute its bounding box
plane.geometry.computeBoundingBox();
Its size along x-axis is 5000 and you don't translate it along this axis, so plane.geometry.boundingBox.min.x is -2500, plane.geometry.boundingBox.max.x is 2500. Then in your animation loop you can check the object's position. Something like this
object.position.x += vx;
var objHalfWidth = object.geometry.parameters.width / 2;
if (object.position.x + objHalfWidth >= plane.geometry.boundingBox.max.x){
object.position.x = plane.geometry.boundingBox.max.x - objHalfWidth;
}
if (object.position.x - objHalfWidth <= plane.geometry.boundingBox.min.x){
object.position.x = plane.geometry.boundingBox.min.x + objHalfWidth;
}

Categories