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

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>

Related

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

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>

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>

Three.js draw Shape between Object3D and Sprite

I have a spinning Object3D with Sprite labels attached around it. The Sprites are positioned correctly and rotate around with the Object3D, always facing the camera.
I need to draw speech bubble style tails on the labels, and have them point back to the location on the Object3D. This is fairly simple with a line, but gets more complex with a shape.
Green is Object3D cube
Blue is the Shape which needs its rotation.y fixing
White is the Sprite label
So far I have the blue Shape sized and positioned correctly. However the rotation y axis needs to dynamically update vs the Object3D rotation and the camera position. I've tried .lookAt(), but which doesn't work as it rotates all axis. I need only the y rotation to be affected.
Also questioning whether this is the best approach in general?
Thanks!
// Scene setup
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var ambientLight = new THREE.AmbientLight(0xffffff, .5);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
var group = new THREE.Group();
scene.add(group);
// Cube
var cubeSize = 1;
var geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var material = new THREE.MeshLambertMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
group.add(cube);
// Text label
var config = {
fontface: 'Arial',
fontsize: 64,
fontweight: 500,
lineheight: 1,
padding: 20
};
var text = 'Hello world!';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
const textMetrics = ctx.measureText(text);
var textWidth = textMetrics.width;
var textHeight = config.fontsize * config.lineheight;
canvas.width = textWidth + config.padding * 2;
canvas.height = textHeight + config.padding * 2;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textWidth + config.padding * 2, textHeight + config.padding * 2);
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
ctx.fillText(text, config.padding, config.padding);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
var aspectRatio = spriteMaterial.map.image.height / spriteMaterial.map.image.width;
var sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(0, cubeSize * 2, 0);
sprite.scale.set(1, aspectRatio, 1);
group.add(sprite);
// ShapeGeometry Mesh
var arrowWidth = textHeight / 220;
var arrowHeight = sprite.position.y / 1.41;
var arrowShape = new THREE.Shape();
arrowShape.moveTo(0, 0);
arrowShape.lineTo(-arrowWidth / 2, arrowHeight);
arrowShape.lineTo(arrowWidth / 2, arrowHeight);
arrowShape.lineTo(0, 0);
var arrowGeometry = new THREE.ShapeGeometry(arrowShape);
var arrowMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// depthWrite: false,
side: THREE.DoubleSide
});
var arrow = new THREE.Mesh(arrowGeometry, arrowMaterial);
arrow.position.set(0, cubeSize / 2, 0);
/* arrow.up.copy(new THREE.Vector3(0, 1, 0)); */
group.add(arrow);
window.group = group;
var animate = function() {
requestAnimationFrame(animate);
group.rotation.x += 0.005;
group.rotation.y += 0.005;
group.rotation.z += 0.005;
// Using lookAt with depthWrite:false almost works
// arrow.lookAt(camera.position);
// arrow.rotation.x = 0;
// arrow.rotation.z = 0;
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.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>

three.js rendering text to scene

I'm new at three.js.
In my work, I have to made 3d graphical website.
So after searched in google, I found that three.js is suitable to manipulate WebGL conveniently.
In three.js document(https://threejs.org/docs/#api/en/geometries/TextGeometry),
TextGeometry is API for draw text in the scene.
[src.js]
init = () => {
window.addEventListener('resize', resizeWindow);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
var controls = new THREE.OrbitControls( camera );
controls.update();
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdd3b56);
renderer.setSize(window.innerWidth, window.innerHeight);
// Set shadow
renderer.shadowMap.enabled = true;
// Show Axis
var axes = new THREE.AxisHelper(5);
scene.add(axes);
// Text
var loader = new THREE.FontLoader();
loader.load( './helvetiker_regular.typeface.json', function ( font ) {
var geometry = new THREE.TextGeometry( 'Hello three.js!', {
font: font,
size: 80,
height: 5,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 10,
bevelSize: 8,
bevelSegments: 5
} );
} );
var textMaterial = new THREE.MeshPhongMaterial({color: 0xFE98A0});
var text = new THREE.Mesh(geometry, textMaterial);
text.position.x = 0;
text.position.y = 10;
text.position.z = 10;
scene.add(text);
// Light
var spotLight = new THREE.SpotLight(0xFFFFFF);
spotLight.position.set(-40, 60, 30);
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 5120;
spotLight.shadow.mapSize.height = 5120;
scene.add(spotLight);
// Camera Setting
camera.position.x = 0;
camera.position.y = 30;
camera.position.z = 30;
camera.lookAt(scene.position);
document.getElementById("threejs_scene").appendChild(renderer.domElement);
renderScene();
function renderScene() {
requestAnimationFrame(renderScene);
controls.update();
renderer.render(scene, camera);
}
}
window.onload = init();
[index.html]
<html>
<head>
<script src="three.js"></script>
<script src="OrbitControls.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="threejs_scene"></div>
<script src="src.js"></script>
</body>
</html>
When I execute my code, it throws [.WebGL-0x7fb612852000]RENDER WARNING: Render count or primcount is 0. and WebGL: too many errors, no more errors will be reported to the console for this context. errors.
So I searched it at google, it occured when Three.js is trying to render an object that does not exist yet.
But in my code, I already defined it.
var textMaterial = new THREE.MeshPhongMaterial({color: 0xFE98A0});
var text = new THREE.Mesh(geometry, textMaterial);
text.position.x = 0;
text.position.y = 10;
text.position.z = 10;
How can I solve this issue?
My last goal is display text in the scene.
Thanks.
window.onload = function(params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
var light = new THREE.AmbientLight(0xffffff, 0.5);
var light1 = new THREE.PointLight(0xffffff, 0.5);
light1.position.set(0, 5, 0);
light1.castShadow = true;
// set the renderer
var renderer = new THREE.WebGLRenderer();
var camera = new THREE.PerspectiveCamera();
camera.position.set(10, 10, 10);
camera.lookAt(new THREE.Vector3(0, 0, 0));
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
scene.add(light);
scene.add(light1);
var ground = new THREE.Mesh(new THREE.BoxGeometry(10, 0.5, 10), new THREE.MeshLambertMaterial())
ground.receiveShadow = true;
scene.add(ground)
var geometry;
var loader = new THREE.FontLoader();
var mesh;
requestAnimationFrame(render);
function render() {
if (mesh) {
mesh.rotation.y += 0.01;
mesh.rotation.z += 0.007;
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
loader.load('https://cdn.rawgit.com/mrdoob/three.js/master/examples/fonts/helvetiker_regular.typeface.json', function(font) {
var geometry = new THREE.TextGeometry('Hello three.js!', {
font: font,
size: 80,
height: 5,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 10,
bevelSize: 8,
bevelSegments: 5
});
var material = new THREE.MeshLambertMaterial({
color: 0xF3FFE2
});
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 2, 0);
mesh.scale.multiplyScalar(0.01)
mesh.castShadow = true;
scene.add(mesh);
var canv = document.createElement('canvas')
canv.width = canv.height = 256;
var ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canv.width, canv.height);
ctx.fillStyle = 'black'
ctx.fillText("HERE IS SOME 2D TEXT", 20, 20);
var tex = new THREE.Texture(canv);
tex.needsUpdate = true;
var mat = new THREE.MeshBasicMaterial({
map: tex
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), mat);
scene.add(plane)
});
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>

Categories