I'm trying to engrave some text on a surface using Three.js. I've achieved it using csg.js and ThreeCSG and it works perfect, the result is really good but the problem is it takes a lot of time. On my PC it takes about 30 seconds to engrave the word Hello.
Searching for other solution I found this site. They make custom jewelry and you can engrave text on it and the time it takes to engrave the text is really short! So I assume they are not using csg.js. What other technique can be used to achieve this result?
I though about using bump maps, I should generate a bump map for each letter but I don't know if that's the correct approach.
I can see by looking at the shaders that the site your linked to uses bump maps.
I don't think you would create a bump map for each letter, you would just do all the drawing (text) on a single canvas and apply that as a bump map.
Click on "Run Code Snippet" below for a demo of canvas bump maps (click and drag in the white box).
I hope this helps.
var camera, scene, renderer, mesh, material, stats;
var drawStartPos = {x:0, y:0};
init();
setupCanvasDrawing();
animate();
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.getElementById('threejs-container').appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
// Create scene.
scene = new THREE.Scene();
// Create material
material = new THREE.MeshPhongMaterial();
// Create cube and add to scene.
var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Create ambient light and add to scene.
var light = new THREE.AmbientLight(0x404040); // soft white light
scene.add(light);
// Create directional light and add to scene.
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
// Add stats to page.
stats = new Stats();
document.body.appendChild( stats.dom );
}
function setupCanvasDrawing() {
// get canvas and context
var drawingCanvas = document.getElementById('drawing-canvas');
var drawingContext = drawingCanvas.getContext('2d');
// draw white background
drawingContext.fillStyle = "#FFFFFF";
drawingContext.fillRect(0,0,128,128);
// set canvas as bumpmap
material.bumpMap = new THREE.Texture(drawingCanvas);
// set the variable to keep track of when to draw
var paint = false;
// add canvas event listeners
drawingCanvas.addEventListener('mousedown', function(e){
paint = true
drawStartPos = {x:e.offsetX, y:e.offsetY};
});
drawingCanvas.addEventListener('mousemove', function(e){
if(paint){
draw(drawingContext, e.offsetX, e.offsetY);
}
});
drawingCanvas.addEventListener('mouseup', function(e){
paint = false;
});
drawingCanvas.addEventListener('mouseleave', function(e){
paint = false;
});
}
// Draw function
function draw(drawContext, x, y) {
drawContext.moveTo(drawStartPos.x, drawStartPos.y);
drawContext.lineTo(x,y);
drawContext.stroke();
drawStartPos = {x:x, y:y};
material.bumpMap.needsUpdate = true;
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
stats.update();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
#drawing-canvas {
position: absolute;
background-color: #000;
top: 0px;
right: 0px;
z-index: 3;
}
#threejs-container {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
z-index: 1;
}
<script src="https://rawgit.com/mrdoob/three.js/r83/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/stats.js/r17/build/stats.min.js"></script>
<canvas id="drawing-canvas" height="128" width="128"></canvas>
<div id="threejs-container"></div>
Related
So I'm trying to learn how to use THREE.js to make browser games, however when I try to use the code to import a model the screen renders black. I'm using Brackets to code and run it and I haven't had an issue until now.
Heres the code
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.background = new THREE.Color(0xdddddd);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var loader = new THREE.GLTFLoader();
loader.load('CabbageBase.glb', function(gltf) {
scene.add(gltf.scene);
}, undefined, function(error) {
console.error(error);
});
camera.position.z = 5;
var animate = function() {
requestAnimationFrame(animate);
/*cube.rotation.x += 0.01;
cube.rotation.y += 0.01;*/
renderer.render(scene, camera);
};
animate();
body {
background: linear-gradient(#e4e0ba, #f7d9aa);
margin: 0px;
overflow: hidden;
}
canvas {
display: block;
}
<script src="//cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="GLTFLoader.js"></script>
I don't see anything wrong with the code except for the URLs.
You need to use a specific version of three.js by either downloading it or using a versioned CDN. All of the loaders, controls, and the examples require a specific version. Never link to threejs.org, always either download your own version or use a CDN. For example jsdeliver.
https://www.jsdelivr.com/package/npm/three
So for the three.js library the URL is
https://cdn.jsdelivr.net/npm/three#0.113.2/build/three.min.js
and for the version compatible GLTFLoader the URL is
https://cdn.jsdelivr.net/npm/three#0.113.2/examples/js/loaders/GLTFLoader.js
After that you need a model. I pointed to one on the three.js site (which is also bad since there is no guarantee it will stay their or stay compatible but for models we have less choices, normally you'd use your own model)
And with that the code works as is.
Note: For three.js you also generally need to run a local server which is covered in the docs
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
scene.background = new THREE.Color(0xdddddd);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var loader = new THREE.GLTFLoader();
loader.load('https://threejs.org/examples/models/gltf/Horse.glb', function(gltf) {
scene.add(gltf.scene);
}, undefined, function(error) {
console.error(error);
});
camera.position.z = 5;
var animate = function() {
requestAnimationFrame(animate);
/*cube.rotation.x += 0.01;
cube.rotation.y += 0.01;*/
renderer.render(scene, camera);
};
animate();
body {
background: linear-gradient(#e4e0ba, #f7d9aa);
margin: 0px;
overflow: hidden;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.113.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.113.2/examples/js/loaders/GLTFLoader.js"></script>
I was looking online, but I didn’t found much. I’m basically looking for the camera function that renders an image when a button is pressed.
Notice that I have 2 THREE.PerspectiveCamera (main and 2nd). The main camera is the one I use for the OrbitControls. and the 2nd is for capturing the image.
This is how I declare the Three.PerspectiveCamera:
camera_RT = new THREE.PerspectiveCamera(20, window.innerWidth / window.innerHeight, -100, -1000);
camera_RT.position.set( //Set 2nd camera's position according to its parent
camera_RT.position.x,
camera_RT.position.y,
camera_RT_Holder.position.z
);
camera_RT.updateMatrixWorld(); //Update camera's Matrix Wolrd (location in the scene)
//Add the Camera to the camera Holder (parent objects)
camera_RT_Holder.add(camera_RT);
//Create 2nd Camera Helper (View volume)
camera_RT_Helper = new THREE.CameraHelper( camera_RT ); //Create camera helper
scene_Main.add( camera_RT_Helper );//Add the camera helper to the scene
UPDATE:
function saveImage(){
let imgData;
var final_Image;
try {
noLoop(); //STOP p5JS ITERATION - draw() function is not called
camera_RT.imageData = renderer_Main.domElement.toDataURL();
imgData = camera_RT.imageData;
console.log(imgData); //CONSOLE PRINTS : data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABD0AAAJFCAYAAADeVYzuAAAgAElEQVR4Xu3YwQ0AMAwCMbr/0K3UMU7OBjG8ONvuHAECBAgQIECAAAECBAgQIEAgJnCMHrFEvUOAAAECBAgQIECAAAECBAh8AaOHIhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIEAWdyI4AAA/sSURBVCBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAkaPZKyeIkCAAAECBAgQIECAAAECBIweOkCAAAECBAgQIECAAAECBAgkBYweyVg9RYAAAQIECBAgQIAAAQIECBg9dIAAAQIECBAgQIAAAQIECBBIChg9krF6igABAgQIECBAgAABAgQIEDB66AABAgQIECBAgAABAgQIECCQFDB6JGP1FAECBAgQIECAAAECBAgQIGD00AECBAgQIECAAAECBAgQIEAgKWD0SMbqKQIECBAgQIAAAQIECBAgQMDooQMECBAgQIAAAQIECBAgQIBAUsDokYzVUwQIECBAgAABAgQIECBAgIDRQwcIECBAgAABAgQIECBAgACBpIDRIxmrpwgQIECAAAECBAgQIECAAAGjhw4QIECAAAECBAgQIECAAAECSQGjRzJWTxEgQIAAAQIECBAgQIAAAQJGDx0gQIAAAQIECBAgQIAAAQIEkgJGj2SsniJAgAABAgQIECBAgAABAgSMHjpAgAABAgQIECBAgAABAgQIJAWMHslYPUWAAAECBAgQIECAAAECBAgYPXSAAAECBAgQIECAAAECBAgQSAoYPZKxeooAAQIECBAgQIAAAQIECBAweugAAQIECBAgQIAAAQIECBAgkBQweiRj9RQBAgQIECBAgAABAgQIECBg9NABAgQIECBAgAABAgQIECBAIClg9EjG6ikCBAgQIECAAAECBAgQIEDA6KEDBAgQIECAAAECBAgQIECAQFLA6JGM1VMECBAgQIAAAQIECBAgQICA0UMHCBAgQIAAAQIECBAgQIAAgaSA0SMZq6cIECBAgAABAgQIECBAgAABo4cOECBAgAABAgQIECBAgAABAkkBo0cyVk8RIECAAAECBAgQIECAAAECRg8dIECAAAECBAgQIECAAAECBJICRo9krJ4iQIAAAQIECBAgQIAAAQIEjB46QIAAAQIECBAgQIAAAQIECCQFjB7JWD1FgAABAgQIECBAgAABAgQIGD10gAABAgQIECBAgAABAgQIEEgKGD2SsXqKAAECBAgQIECAAAECBAgQMHroAAECBAgQIECAAAECBAgQIJAUMHokY/UUAQIECBAgQIAAAQIECBAgYPTQAQIECBAgQIAAAQIECBAgQCApYPRIxuopAgQIECBAgAABAgQIECBAwOihAwQIECBAgAABAgQIECBAgEBSwOiRjNVTBAgQIECAAAECBAgQIECAgNFDBwgQIECAAAECBAgQIECAAIGkgNEjGaunCBAgQIAAAQIECBAgQIAAAaOHDhAgQIAAAQIECBAgQIAAAQJJAaNHMlZPESBAgAABAgQIECBAgAABAkYPHSBAgAABAgQIECBAgAABAgSSAg8FMUUfXYyMwAAAAABJRU5ErkJggg==
final_Image = createImg(imgData);
final_Image.attribute("id", "finalImage");
final_Image.attribute("style", "display:block; margin:20px auto;");
final_Image.parent(canvas_Parent);
//Hide Scene_Main and disable slider
document.getElementById("canvas_3d_element").style.display = "none";
document.getElementById("slider-element").disabled = true;
stage_9_declared = true;
saveImageRequest = true;
} catch (e) {
console.log(e);
return;
}
}
function animate() {
...
if(!stage_9_declared && !saveImageRequest){
console.log(saveImageRequest);
this.render(); //Render Scene and Camera every frame
}
}
function render() {
//Render only if the camera_Main is declared
if(camera_Main_declared){
renderer_Main.render(scene_Main, camera_Main);
}
}
Here is an example of taking a screenshot.
To change which camera the screenshot is taken from, you would just change which camera you send to the renderer.render() function.
In this example I only have one camera though.
UPDATE: The screenshot button in this example code may not actually work in the demo as it is in an iframe on stackoverflow (in Chrome, and maybe other browsers) because of this.
But here is a jsfiddle demo
var camera, scene, renderer, mesh, material;
init();
animate();
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer({
antialias: true,
//preserveDrawingBuffer: true
});
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// add Screenshot listener
document.getElementById("shot").addEventListener('click', takeScreenshot);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
// Create scene.
scene = new THREE.Scene();
// Create material
material = new THREE.MeshPhongMaterial();
// Create cube and add to scene.
var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// Create ambient light and add to scene.
var light = new THREE.AmbientLight(0x404040); // soft white light
scene.add(light);
// Create directional light and add to scene.
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
}
function takeScreenshot() {
// open in new window like this
//
/*
var w = window.open('', '');
w.document.title = "Screenshot";
//w.document.body.style.backgroundColor = "red";
var img = new Image();
// Without 'preserveDrawingBuffer' set to true, we must render now
renderer.render(scene, camera);
img.src = renderer.domElement.toDataURL();
w.document.body.appendChild(img);
*/
/*
// download file like this.
//
var a = document.createElement('a');
// Without 'preserveDrawingBuffer' set to true, we must render now
renderer.render(scene, camera);
a.href = renderer.domElement.toDataURL().replace("image/png", "image/octet-stream");
a.download = 'canvas.png'
a.click();
*/
// New version of file download using toBlob.
// toBlob should be faster than toDataUrl.
// But maybe not because also calling createOjectURL.
// I dunno....
//
renderer.render(scene, camera);
renderer.domElement.toBlob(function(blob){
var a = document.createElement('a');
var url = URL.createObjectURL(blob);
a.href = url;
a.download = 'canvas.png';
a.click();
}, 'image/png', 1.0);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
}
#shot {
position: absolute;
top: 0px;
right: 0px;
margin: 5px;
}
<script src="https://rawgit.com/mrdoob/three.js/r102/build/three.min.js"></script>
<button id="shot">Take Screenshot</button>
On button click use the method toDataURL() to capture the image from the camera.
imageData = renderer.domElement.toDataURL();
The DOM element of renderer is a canvas. Refer: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL for more details
see my previous question here for reference to what I am trying to achieve
TL;DR:
I am trying to get HTML elements to rotate in conjunction with OrbitControls to make it seem as if these HTML elements are stuck to the globe and move with it. (think map markers on a 3D earth above certain countries)
I achieved this almost successfully using the THREE.js CSS3DRenderer, and was able to get the HTML elements to stick to a location on my 3D globe with a lat/long calculation and rotate with the globe when OrbitControls are used.
The problem
The only issue I am having is that the HTML elements are scaling proportionate to how close/far they are from the camera. Usually I assume this would be the desired effect to help the sense of getting closer/further, but the scaling is causing me not to be able to size my HTML elements correctly and consistently, and also causing text and SVGs inside the elements to blur/become pixelated
What I want
I am looking for a way to turn off this scaling so that the HTML elements still transform in every other way, but stay the same size (i.e. scale(1, 1, 1) or their original scale) no matter where they are in the 3D renderer created by CSS3DRenderer.
I assume I will have to edit the CSS3DRenderer.js code for this, but I have absolutely no idea where to start and I cannot find any information anywhere else.
Thanks.
Some of my code:
Creating the CSS3DRenderer
//CSS3D Renderer
rendererHTML = new THREE.CSS3DRenderer();
rendererHTML.setSize(WIDTH, HEIGHT);
rendererHTML.domElement.classList.add('CSS3D-container');
containerHTML = document.querySelector('.globe__container');
containerHTML.appendChild(rendererHTML.domElement);
Resizing function (called on window resize event)
HEIGHT = sizeControlElem.getBoundingClientRect().width;
WIDTH = sizeControlElem.getBoundingClientRect().width;
renderer.setSize(WIDTH, HEIGHT);
rendererHTML.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
Creating the CSS3DSprite objects from <li> elements in the HTML and setting their initial positions on the globe
for (let key in this.locationsObject) {
_this.locationsObject[key].coordinates = calcPosFromLatLonRad(this.locationsObject[key].lat, this.locationsObject[key].long, 300);
let CSS3D_Object = new THREE.CSS3DSprite(_this.locationsObject[key].element);
CSS3D_Object.position.set(_this.locationsObject[key].coordinates[0], _this.locationsObject[key].coordinates[1], _this.locationsObject[key].coordinates[2]);
CSS3D_Object.receiveShadow = false;
CSS3D_Object.castShadow = false;
sceneHTML.add(CSS3D_Object);
_this.locationsObject[key].CSS_Object = CSS3D_Object;
console.info(CSS3D_Object);
}
You can see some more of my code in the question here
The only way to stop the scaling is by projecting the 3D positions to 2D with the Vector3.project() method. Take a look at the code sample below, I commented the key points in the JavaScript code, but a quick explanation is this:
Copy the 3D position where you want the hotspot into a new vector.
Use vector.project(camera) to translate that 3D point to 2D coordinates.
Transform the range of 2D coords from [-1, 1] to [0, window.width]
Apply these coordinates via CSS to your hotspot.
Bonus: You can still use the .z attribute of the 2D vector to determine if it's within the camera's frustum or not.
var camera, controls, scene, renderer;
// This array will hold all positions in 3D space
var posArray3D = [];
// This array will hold all hotspot DIVs
var divArray = [];
// Create temp vector to reuse on loops
var tempVec = new THREE.Vector3();
init();
animate();
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xcccccc );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 3000 );
camera.position.set( 400, 200, 0 );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
controls.minDistance = 100;
controls.maxDistance = 500;
controls.maxPolarAngle = Math.PI / 2;
// world
var geometry = new THREE.CylinderBufferGeometry( 0, 10, 30, 4, 1 );
var material = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true } );
// This is where all hotspot DIVs will go
var hotspotBox = document.getElementById("hotspotBox");
for ( var i = 0; i < 100; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = Math.random() * 1600 - 800;
mesh.position.y = 0;
mesh.position.z = Math.random() * 1600 - 800;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
// Populate array of 3D positions
posArray3D.push(mesh.position);
// Create 'hotspot' DIV, and place within 'hotspotBox' holder
divArray.push(document.createElement("div"));
divArray[i].classList.add("hotspot");
hotspotBox.appendChild(divArray[i]);
}
// lights
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
var light = new THREE.DirectionalLight( 0x002288 );
light.position.set( - 1, - 1, - 1 );
scene.add( light );
var light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
// Loops through all divs and updates their positions based on the camera
function updateDivs() {
var vectorScreen = new THREE.Vector3();
// Loop through all positions
for (var i = 0; i < posArray3D.length; i ++) {
vectorScreen.copy(worldToScreen(posArray3D[i], camera));
// Update CSS attributes of each DIV
divArray[i].style.transform = "translate(" + vectorScreen.x + "px, " + vectorScreen.y + "px)";
// Checks for depth, hides if it's behind the camera
if(vectorScreen.z <= 1) {
divArray[i].style.display = "block";
} else {
divArray[i].style.display = "none";
}
}
}
// Projects 3D coordinates into 2D space
function worldToScreen(_position, _cam) {
tempVec.copy(_position);
tempVec.project(_cam);
// Converts range from [-1, 1] to [0, windowWidth]
tempVec.x = ( tempVec.x + 1 ) * window.innerWidth / 2;
tempVec.y = ( - tempVec.y + 1 ) * window.innerHeight / 2;
return tempVec;
}
function animate() {
requestAnimationFrame( animate );
controls.update();
updateDivs();
render();
}
function render() {
renderer.render( scene, camera );
}
body {
color: #000;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
/*hotspotBox holds all .hotspots It's placed on top of WebGL canvas*/
#hotspotBox{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border: 1px dashed #f90;
pointer-events: none;
}
/*100 hotspots to be placed within #hotspotBox */
.hotspot {
background: #f90;
width: 10px;
height: 10px;
border-radius: 5px;
position: absolute;
cursor: pointer;
pointer-events: auto;
}
<div id="hotspotBox"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
It turns out that the easiest solution to my question is simply to use the CSS2DRenderer instead of the CSS3DRenderer.
It essentially does the same thing but only transforms the HTML element with translate, and does not rotate or scale it, meaning you can modify the size of the HTML elements freely using CSS width and size etc... which is what I wanted to do.
It is implemented in the exact same way, the only thing I had to change in my code was replacing CSS3DSprite with CSS2DObject.
Read more about CSS2DRenderer here.
Ok, very new to three.js here but I am trying to achieve what Google has with https://beinternetawesome.withgoogle.com/interland
See - they have their FULL SCREEN Three.js scene and their text, buttons (which are clearly HTML divs, not generated via JS) overlaid perfectly on top.
I have looked at
https://discourse.threejs.org/t/trying-to-overlay-a-button-and-text-on-top-of-a-three-js-scene/390/2
and similar Questions but can't understand the proper way to do this. I don't want to generate my UI elements in Three.js - I just want to use HTML.
What I have so far is I generate my Three.js canvas and add it to my document so that it gets and STAYS the full width/height of my window:
var controls, camera, renderer, scene, container, w, h;
renderer = new THREE.WebGLRenderer()
// container = document.getElementById('canvas');
container = window;
w = container.innerWidth;
h = container.innerHeight;
renderer.setSize(w, h)
renderer.setPixelRatio( window.devicePixelRatio );
document.body.appendChild(renderer.domElement);
And then with my existing HTML:
<body>
<div id="ui">
<p id="message">Test to change</p>
</div>
I get the UI div stuck on top, no matter how I play with the CSS or the z index of the UI div:
How can I achieve the Google effect with HTML overlaid over my three.js scene? What am I doing wrong?
I need my canvas to fill the whole window just as Google's does and can't achieve that it seems making the canvas HTML element beforehand and trying to attach everything in JS.
Creativity is up to you, as there are many ways to achieve the desired result.
You can start from looking at the source code of examples from the official web site of Three.js. There you can see how the info section of an example sets on a web page.
var buttons = document.getElementsByTagName("button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", onButtonClick, false);
};
function onButtonClick(event) {
alert(event.target.id);
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geometry = new THREE.BoxGeometry(1, 1, 1);
var material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true
});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
var animate = function() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
body {
overflow: hidden;
margin: 0;
}
.ui {
position: absolute;
}
button{
margin: 20px;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<div style="width:100%;height:50px;text-align: center; color:white;" class="ui">Info header</div>
<button style="top:0" id="fire" class="ui">Fire</button>
<button style="bottom:0" id="spell" class="ui">Spell</button>
<button style="right:0" id="health" class="ui">Health</button>
<button style="right:0; bottom:0;" id="shield" class="ui">Shield</button>
z-index only works with position property except for the case position: static and position: initial.
This button class worked fine with my threejs scene. You can edit left, top accordingly.
button {
position: absolute;
display: block;
z-index: 99;
left: 50%;
top: 50%;
}
I am rendering a 3D human head using three.js and OBJLoader:
let renderer, camera, scene, head, light, projectiles;
new THREE.OBJLoader().load(objUrl, initialize);
function initialize(obj) {
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight);
scene = new THREE.Scene();
head = obj.clone();
head.children.forEach(child => child.material = new THREE.MeshPhongMaterial({ color: "#ffc700" }));
head.position.y = -34;
head.position.z = -110;
scene.add(head);
light = new THREE.SpotLight();
light.target = head;
scene.add(light);
projectiles = [];
window.addEventListener("mousedown", createProjectile, false);
animate();
}
function animate() {
head.rotation.y += THREE.Math.degToRad(1);
projectiles.forEach(updateProjectile);
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
function createProjectile() {
let projectile = new THREE.Mesh();
projectile.material = new THREE.MeshToonMaterial({ color: "#ff0000" });
projectile.geometry = new THREE.SphereGeometry(3, 20, 20);
projectile.position.copy(getMouthPosition());
scene.add(projectile);
projectiles.push(projectile);
}
function updateProjectile(projectile) {
// TODO: Move projectile in the direction the mouth was facing when projectile was first created.
projectile.position.x += 2;
}
function getMouthPosition() {
// TODO: Determine the world position of the mouth.
let box = new THREE.Box3().setFromObject(head);
return box.getCenter();
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js">
</script>
<script src="https://wzrd.in/standalone/three-obj-loader#1.1.3">
</script>
<script>
threeObjLoader(THREE);
objUrl = "https://cdn.rawgit.com/mrdoob/three.js/f32fc45/examples/obj/walt/WaltHead.obj";
</script>
When the mouse is clicked, I want to "shoot" a projectile/bullet from the rotating head's mouth. But as you can see from the TODO comments in the code, there are two functions I don't know how to implement: getMouthPosition() and updateProjectile().
For getMouthPosition(), I want to determine the current position of the mouth and spawn the projectile at this location (ideally, just in front of the mouth).
For updateProjectile(), I want to move the projectile in the direction the head was facing at the time when the projectile was first created, like this:
If someone could shed light on how to write these functions, that would be great. Thanks.
Look. Somehow you'll get where the mouth locates (in coordinates of the head group it locates at about [0, 25, 20]). Then, to get position of the mouth of the spinning head, you can use .localToWorld(v) method, like so:
head.localToWorld(mouthPosition.copy(spawnPoint.position));
spawnPoint is a "helper" object to indicate where our spawn point is.
Further, you have to know where your head points at. You can get it with another method .getWorldDirection() of the head object.
Concluding all of this: you know position of the head's mouth, you know its direction, thus you can cast a projectile, using those values.
let renderer, camera, scene, head, light, projectiles, spawnPoint, clock = new THREE.Clock(), delta = 0;
new THREE.OBJLoader().load(objUrl, initialize);
function initialize(obj) {
renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight);
scene = new THREE.Scene();
head = obj.clone();
head.children.forEach(child => child.material = new THREE.MeshPhongMaterial({ color: Math.random() * 0xffffff }));
head.position.y = -34;
head.position.z = -110;
scene.add(head);
light = new THREE.SpotLight();
light.target = head;
scene.add(light);
spawnPoint = new THREE.Mesh(new THREE.SphereGeometry(1, 4, 2), new THREE.MeshBasicMaterial({color: "red", wireframe: true}));
spawnPoint.position.set(0, 25, 20);
head.add(spawnPoint);
projectiles = [];
window.addEventListener("mousedown", event => { createProjectile(); }, false);
animate();
}
function animate() {
delta = clock.getDelta();
requestAnimationFrame(animate);
head.rotation.y += THREE.Math.degToRad(20) * delta;
projectiles.forEach(p => {
p.position.addScaledVector(p.userData.direction, p.userData.speed * delta);
});
renderer.render(scene, camera);
}
function createProjectile() {
let projectile = new THREE.Mesh();
projectile.material = new THREE.MeshToonMaterial({ color: 0xff0000 });
projectile.geometry = new THREE.SphereGeometry(3, 16, 12);
let pos = getMouthPosition();
console.log("pos", pos);
projectile.position.copy(pos);
projectile.userData.direction = new THREE.Vector3().copy(head.getWorldDirection().normalize());
console.log(projectile.userData.direction);
projectile.userData.speed = 50;
scene.add(projectile);
projectiles.push(projectile);
console.log(projectiles);
}
function getMouthPosition() {
let mouthPosition = new THREE.Vector3();
console.log("spawnPoint", spawnPoint);
head.localToWorld(mouthPosition.copy(spawnPoint.position));
console.log("mouthPosition", mouthPosition);
return mouthPosition;
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js">
</script>
<script src="https://wzrd.in/standalone/three-obj-loader#1.1.3">
</script>
<script>
threeObjLoader(THREE);
objUrl = "https://cdn.rawgit.com/mrdoob/three.js/f32fc45/examples/obj/walt/WaltHead.obj";
</script>