Hello Community,
I am New in Three Js.I am Creating 2D Game With Three JS and I am facing Some issue to make Game Responsive and also when Size of the game play is decrease some functionalities are not working properly like touch event on object.I want to know that can we apply css to the three js objects so that we can create the responsive desing game.
Please Guide me about it.
Initialize your camera and renderer to the window size from the start and then add an event listener that will alter properties of objects that relate to the screen size. Usually something like this:
// keep track of the window size in an object
const size = { width: window.innerWidth, height: window.innerHeight };
// initialize camera based on recorded sizes
const camera = new THREE.PerspectiveCamera(
75,
size.width / size.height,
0.1,
100
);
camera.position.set(...);
scene.add(camera);
// initialize renderer based on recorded sizes
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(size.width, size.height);
renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
// adjust relevant properties on window size change
window.addEventListener('resize', () => {
size.width = window.innerWidth;
size.height = window.innerHeight;
camera.aspect = size.width / size.height;
camera.updateProjectionMatrix();
renderer.setSize(size.width, size.height);
renderer.setPixelRatio(Math.min(2, window.devicePixelRatio));
});
Hopefully this is the answer you're looking for.
Related
I am adding augmented reality experience to my web app which originally created using threejs
I have a compound object(which is an Object3D instance with multiple meshes). but placing it into A-Frame giving unexpected flickering s shown in below
Pic of the original web app with threejs is given below
I have the three.js code like below
scene = new THREE.Scene();
mainObj = new THREE.Object3D();
scene.add(mainObj);
renderer = new THREE.WebGLRenderer({alpha: true, antialias: true});
renderer.sortObjects = false
container = document.getElementById("canvas-container");
width = $(container).innerWidth();
height = $(container).innerHeight();
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(75, width / height, 10, 2000);
camera.position.set(0, 67, 100);
controls = new THREE.OrbitControls( camera , container);
controls.dampingFactor = 0.2;
controls.enableDamping = true;
controls.target.set( 0, 10, -20 );
controls.maxPolarAngle = (Math.PI/2) - 0.05;
controls.maxDistance = 800;
controls.minDistance = 2;
controls.target.set(0, buildingConfig.h/2, buildingConfig.l/-2)
controls.enableKeys = false;
controls.update();
scene.add(camera);
light = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.7);
light.position.set(1, 10000, 1);
light.castShadow = true
scene.add( light );
renderer.render(scene, camera);
// logic to add child meshes to mainObj
Which I changed to include in A-Frame.
<script type="text/javascript">
initmodels();
AFRAME.registerComponent('e1', {
init:function(){
this.el.setObject3D('building', mainObj);
}
});
</script>
<a-scene e1>
</a-scene>
I guess this issue is related to the scene or renderer. Can anyone help me with a proper scene or renderer setup in A-Frame
This looks like z-fighting. Is the three.js version running on a different system than your aframe version?
Different platforms have different z-buffer precision. It may require you to make changes to the geometry to compensate for the limited resolution.
Also, I don't know about AR.s, but the THREE renderer has a "logarithmicDepthBuffer" option that can increase the resolution of your z-buffer at near scales, but may have some side effects.
I had exactly the same issue. After a few trial and errors, I was able to fix it
Added recent version of aframe
Add <a-scene embedded artoolkit='sourceType: webcam;' renderer='logarithmicDepthBuffer: true;'>
Add <a-marker-camera camera="far:100000; near:0.01" >
Main reason was the light, Remove all lights and add only the hemisphere light into the scene and improvise upon other lights later on.
Hope this would resolve your issue too.
I am getting a strange issue with scaling scene in three.js only on mobile devices.
Everything is alright when I watch it on desktop, but on mobile it always scaling and blinking...
Here is link to codepen: https://codepen.io/elliepooh/pen/evXgdE
And here is my init function:
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
camera.lookAt(scene.position);
camera.position.set(30, 15, 15);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
renderer.setClearColor(0xF2A9B4);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
controls = new THREE.OrbitControls(camera, renderer.domElement);
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
addLights();
drawChameleon();
drawBranch();
drawFly();
document.body.appendChild(renderer.domElement);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('touchmove', onTouchMove);
window.addEventListener('resize', onResize);
}
Maybe I missed something?
Any assistance is appreciated.
Finally I solved the problem by appending domElement to existing div#world and adding this to my css:
body {
margin: 0;
}
#world {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
}
Great thanks to #AlexFallenstedt. Once I knew the reason was in iOS, not in my code, it made me relax and resolve the problem.
I think I know what you mean. I encountered this "blinking" bug before when displaying a three.js scene on mobile. Are you testing this on iOS? If so, when you scroll down on ios, the browser's nav bar shrinks. Your code has renderer.setSize(width, height), and will resize the entire canvas because of your event listener. When your canvas resizes, weird things happen because it's both trying to animate and resize...
How I got around it was by adding a function in render()
function resizeCanvas() {
if (canvas.width != canvas.clientWidth ||
canvas.height != canvas.clientHeight) {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
}
}
It asks if the canvas' width and height is looks like the client's width and height. If it doesn't, it kind of forces the canvas to be that width and height.
You can see it in action here:
https://github.com/Fallenstedt/Fallenstedt.github.io/blob/master/_js/index.js#L128-L135
I apologize for one more basic question which basically is a copy of another old thread, but I'm new to three.js and learning more or less through trial and error over here...
I found the old thread 'Putting three.js animation inside of div' where they succeeded putting a three.js animation inside of a div with the following lines of code:
container = document.getElementById( 'canvas' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setSize( 200, 200 );
container.appendChild( renderer.domElement );
A JSFiddle of above script (http://jsfiddle.net/fek9ddg5/1/)
I'm trying to do the same and added the lines to my script, but ended up with a completely white screen instead. I managed to basically part the screen in two parts, one completely white and another with the script. However, it failed since the script reacted to the mouse even if it moved in the white part. I made a codepen, I placed the script I added inside of comment-signs.
See it here: http://codepen.io/anon/pen/qRdGWo
Usually to add three.js to a page I would use this:
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
As can be seen in this fiddle
But, to add it to a div that is already on the page with the ID 'canvas-container' I would use..
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
// get the div that will hold the renderer
var container = document.getElementById('canvas-container');
var w = container.offsetWidth;
var h = container.offsetHeight;
renderer.setSize(w, h);
// add the renderer to the div
container.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, w / h, 1, 1000);
camera.position.z = 400;
Which can be seen in this fiddle
Basically where the renderer is added is determined on what dom element you call appendChild(renderer.domElement) on.
I'm trying to create two scenes on two different canvases. Is it possible in Three.js?
var scene1 = new THREE.Scene()
var scene2 = new THREE.Scene()
scene1.add(camera1)
scene2.add(camera2)
...
renderer.render(scene1, camera1)
renderer.render(scene2, camera2)
Will it work like that?
Yes - it is totally possible, but the renderer-instance is always bound to the WebGLContext of the canvas. So you need to create create a renderer for every canvas you have. So this would be
renderer1.render(scene1, camera1);
renderer2.render(scene2, camera2);
(the other way around works as well: you can use multiple renderers to render the same scene with different cameras)
EDIT BASED ON COMMENTS
You can also render multiple scenes into different regions of the same canvas, using just one renderer. For this you need to setup a different viewport and scissor-test for every scene like this (based on https://threejs.org/examples/#webgl_multiple_views)
// first, render scene normally:
camera.aspect = totalWidth / totalHeight;
camera.updateProjectionMatrix();
renderer.setViewport(0, 0, totalWidth, totalHeight);
renderer.setScissorTest(false);
renderer.render( scene1, camera1 );
// then, render the overlay
renderer.setViewport(left, bottom, width, height);
renderer.setScissor(left, bottom, width, height);
renderer.setScissorTest(true);
renderer.setClearColor(view.background);
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.render( scene2, camera2 );
I'm trying to have text sprites in the 3d scene with constant size (regardless of camera distance) using a PerspectiveCamera. In order to get non-sprites to have constant size, I make them children of a special "scaled" object which adjusts its scale as the camera distance to origin changes (see the code below). This works well to keep a general object roughly the same visual size, but when I add a sprite to the scaled object, the sprite seems to ignore its parent's scale (so it gets smaller and bigger as you zoom out and in).
Interestingly, when we switch to an orthographic camera (uncomment the appropriate line below), this special scaled object doesn't seem to affect children anymore (i.e., children don't stay a constant size). However, since we have an orthographic camera, sprites no longer scale as the camera distance changes (so they maintain a constant size), but this is independent of the scaled object.
I notice a few other similar questions and answers, including adjust the scale of the sprites themselves (it seems much easier to add all my sprites to a single scaling object), use an orthographic camera overlay to draw sprites (see also this) (but I want my sprites to be inside the 3d perspective scene).
So, my questions are: why do sprites not use scale according to their parent's scale when using a PerspectiveCamera? Also, why does my scaled object not work with the orthographic camera? Are these bugs or features of the cameras?
Thanks!
http://jsfiddle.net/LLbcs/8/
var camera, scene, renderer, geometry, material, mesh, text, controls;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000); var scenescale=1;
//camera = new THREE.OrthographicCamera( -7,7,7,-7, 1, 20 );
camera.position.z = 10;
scene.add(camera);
scaled=new THREE.Object3D();
scene.add(scaled);
var textmaterial = new THREE.SpriteMaterial( {color: 'red', useScreenCoordinates: true, map: texttexture("hi")});
text = new THREE.Sprite( textmaterial );
text.position.set( 1, 1, 0);
scaled.add(text);
var geometry = new THREE.BoxGeometry( 1, 1,1 );
var material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
mesh = new THREE.Mesh( geometry, material );
mesh.position.set(0,3,0);
scaled.add(mesh);
var light = new THREE.PointLight('green');
light.position.set(10,15,10);
camera.add(light);
light = new THREE.PointLight(0x333333);
light.position.set(-10,-15,-8);
camera.add(light);
renderer = new THREE.WebGLRenderer();
controls = new THREE.OrbitControls( camera, renderer.domElement );
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
var scale = camera.position.length()/10;
scaled.scale.set(scale,scale,scale);
render();
}
function render() {
renderer.render(scene, camera);
}
function texttexture(string) {
var fontFace = "Arial"
var size = "50";
var color = "white"
var squareTexture = true;
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.height = size;
var font = "Normal " + size + "px " + fontFace;
context.font = font;
var metrics = context.measureText(string);
var textWidth = metrics.width;
canvas.width = textWidth;
if (squareTexture) {
canvas.height = canvas.width;
}
var aspect = canvas.width / canvas.height;
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = color;
// Must set the font again for the fillText call
context.font = font;
context.fillText(string, canvas.width / 2, canvas.height / 2);
var t = new THREE.Texture(canvas);
t.needsUpdate = true;
return t;
}
If you want text to appear over a 3D scene and you don't care if it is static, why not try layering a div over the scene instead?
This will allow you to save graphics bandwidth and memory, improving performance of your scene and give you much better flexibility over what you display. It's also much easier to do and to maintain.