Texture loading as black. I tried running the.html file through a local server (Servez) but that does not seem to be the issue. I am using visual studio code. The code seems to work fine, the console does not show any errors.
This is my code:
window.onload = function() {
//Define the WebGL renderer:
var renderer = new THREE.WebGLRenderer(); //specify the we will use WebGL
renderer.setSize( 800, 600 ); //scene size (Width, Height)
document.body.appendChild( renderer.domElement );
//Initialize (create) the scene:
var scene = new THREE.Scene();
//Define the camera:
var camera = new THREE.PerspectiveCamera(
35, // Field of view
800 / 600, // Aspect ratio
0.1, // Near plane
10000 // Far plane
);
camera.position.x= 0; //default value anyway
camera.position.y= 0; //default value anyway
camera.position.z = 300;
camera.lookAt( scene.position );
//Define the objects for the scene:
var skygeo = new THREE.SphereGeometry(150, 40, 20);
var skytexture = new THREE.TextureLoader().load("sky.jpg");
var skymat = new THREE.MeshBasicMaterial({map:skytexture});
skymat.side = THREE.Backside;
var skydome = new THREE.Mesh(skygeo, skymat);
scene.add(skydome);
// create a point light:
var pointLight = new THREE.PointLight(0xFFFFFF);
// set the light position:
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 100;
// add the light to the scene:
scene.add(pointLight);
// it renders the scene:
renderer.render( scene, camera );
};
I found here(https://discourse.threejs.org/t/texture-loading-not-working-on-server/14910/10) that it may be because the image files were corrupted. I also found somewhere else that it may be because of browser extensions like AdBlock, but removing extensions did not fix it.
What is the reason for this?
You're calling renderer.render( scene, camera ); only once, immediately after initializing your texture loader. Texture loading is asynchronous, it takes some time, so you're rendering the scene before the texture has loaded. You need to call render after your texture has finished loading. To do this you can use the onLoad callback, or you could see the basic docs on creating a scene for an example on how to render continuously:
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
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'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>
I am trying to display glued cubes, imported from blender, using Three.js.
I am using latest Version of Three.js and Blender 2.78a.
I started by making my cube object;just like this:
https://www.jonathan-petitcolas.com/2015/07/27/importing-blender-modelized-mesh-in-threejs.html
But I have different/more glued cubes.
I created above 3d object in Blender, Then exported the mesh using the Three.js .js exporter.
Here is the marmelab.json file.
{
"metadata":{
"generator":"io_three",
"faces":6,
"type":"Geometry",
"version":3,
"materials":1,
"normals":6,
"vertices":24,
"uvs":1
},
"faces":[43,8,10,12,14,0,0,1,2,3,0,0,0,0,43,16,18,20,22,0,3,0,1,2,1,1,1,1,43,9,15,19,17,0,3,0,1,2,2,2,2,2,43,1,13,21,5,0,3,0,1,2,3,3,3,3,43,3,11,23,7,0,3,0,1,2,4,4,4,4,43,2,0,4,6,0,3,0,1,2,5,5,5,5],
"materials":[{
"DbgIndex":0,
"blending":"NormalBlending",
"opacity":1,
"depthWrite":true,
"visible":true,
"transparent":false,
"colorEmissive":[0,0,0],
"colorDiffuse":[0.8,0.432941,0],
"wireframe":false,
"DbgName":"01 - Default",
"specularCoef":11,
"colorSpecular":[0.18,0.18,0.18],
"DbgColor":15658734,
"depthTest":true,
"shading":"phong"
}],
"normals":[0,0,1,0,0,-1,0,1,-0,1,0,-0,0,-1,-0,-1,0,-0],
"name":"Untitled.001Geometry.1",
"vertices":[-0.307576,-0,-0.433258,-0.002776,-0,-0.433258,-0.307576,-0,-0.738058,-0.002776,-0,-0.738058,-0.307576,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.307576,0.3048,-0.738058,-0.002776,0.3048,-0.738058,-0.307576,-0,-0.433258,-0.307576,-0,-0.433258,-0.307576,-0,-0.738058,-0.307576,-0,-0.738058,-0.002776,-0,-0.738058,-0.002776,-0,-0.738058,-0.002776,-0,-0.433258,-0.002776,-0,-0.433258,-0.307576,0.3048,-0.433258,-0.307576,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.002776,0.3048,-0.433258,-0.002776,0.3048,-0.738058,-0.002776,0.3048,-0.738058,-0.307576,0.3048,-0.738058,-0.307576,0.3048,-0.738058],
"uvs":[[1,0,1,1,0,1,0,0]]
}
javascript to load the mesh and display
var scene, camera, renderer;
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
var SPEED = 0.01;
function init() {
scene = new THREE.Scene();
initMesh();
initCamera();
initLights();
initRenderer();
document.body.appendChild(renderer.domElement);
}
function initCamera() {
camera = new THREE.PerspectiveCamera(70, WIDTH / HEIGHT, 1, 10);
camera.position.set(0, 3.5, 5);
camera.lookAt(scene.position);
}
function initRenderer() {
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
}
function initLights() {
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
}
var mesh = null;
function initMesh() {
var loader = new THREE.JSONLoader();
loader.load('./marmelab.json', function(geometry, materials) {
mesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.75;
mesh.translation = THREE.GeometryUtils.center(geometry);
scene.add(mesh);
});
}
function rotateMesh() {
if (!mesh) {
return;
}
mesh.rotation.x -= SPEED * 2;
mesh.rotation.y -= SPEED;
mesh.rotation.z -= SPEED * 3;
}
function render() {
requestAnimationFrame(render);
rotateMesh();
renderer.render(scene, camera);
}
init();
render();
However in Browser the shape is rtained ; but the color is white.
What am I missing here?
Thank you so much in advance.
EDIT
I updated the Plunker with the latest version of three.js and it now works with your unedited json above - http://plnkr.co/edit/RwkgzrvfqzYlWtShhpjt?p=preview
Also successfully tested with my own, more complicated, model - http://plnkr.co/edit/SWXv1GyKIGryORiNYlgx?p=preview
The problem is caused by using an older version of three.js. I originally tested using the version provided by the website you linked to. I suspect you might have done the same given the result.
Somewhere around revision 70 they changed the exporter to no longer export the colorAmbient property, and that older version of Three.js required it.