Three.js zoom to fit width of objects (ignoring height) - three.js

I have a set of block objects, and I'd like to set the perspective camera so that their entire width is fully visible (the height will be too big - that's OK, we're going to pan up and down).
I've seen there are a number of questions close to this, such as:
Adjusting camera for visible Three.js shape
Three.js - Width of view
THREE.JS: Get object size with respect to camera and object position on screen
How to Fit Camera to Object
ThreeJS. How to implement ZoomALL and make sure a given box fills the canvas area?
However, none of them seem to quite cover everything I'm looking for:
I'm not interested in the height, only the width (they won't be the same - the size will be dynamic but I can presume the height will be larger than the width)
The camera.position.z (or the FOV I guess) is the unknown, so I'm trying to get the equations round the right way to solve that
(I'm not great with 3D maths. Thanks in advance!)

I was able to simplify this problem a lot, in my case...
Since I knew the overall size of the objects, I was able to simply come up with a suitable distance through changing the camera's z position a few times and seeing what looked best.
My real problem was that the same z position gave different widths, relative to the screen width, on different sized screens - due to the different aspect ratios.
So all I did was divide my distance value by camera.aspect. Now the blocks take up the same proportion of the screen's width on all screen sizes :-)

Related

Processing 3 Box2D Velocity Not Scaling With Resolution

I'm currently trying to make my simple game scale with the resolution. I've noticed though when I change the resolution not everything works out. For instance from the shift from 1280x720 to 1920x1080 the jumping distance changes slightly. The main problem I've noticed is that when I fire a projectile with a velocity. On lower resolutions it seems to travel across the screen significantly faster and I can't understand why as it should scale down with the size of the window. Here is a snipet of the code that fires a projectile:
m = new Box(l.pos.x+Width/32*direction2, l.pos.y-Height/288, Width/64, Height/72, true, 4);
m.body.setGravityScale(0f);
boxes.add(m);
m.body.setLinearVelocity(new Vec2(Width*direction2, 0));
In this scenario m is a box I'm creating. In new Box(spawn x coordinate, spawn y cooridinate, width of box, height of box, is the box moveable, type of box) l.pos.x and l.pos.y are the positions I'm firing the box from. The Height and Width variables are the size of the current window in pixels being updated in void draw(), direction2 is either 1 or -1 depending on the direction in which the character is facing.
Hard to tell how the rest of code affects the simulation without seeing more of it.
Ideally you would want to keep phyics related properties independent from the Processing sketch dimensions in terms of dimensions but maintain proportion so you can simply scale up the rendering of the same sized world. If you have mouse interaction the coordinates would scale as well, but other than the position, the rest of physical proeprties should be maintained.
From what I can gather in your code if Width is the sketch's width you should de-couple that from linear velocity:
m.body.setLinearVelocity(new Vec2(Width*direction2, 0));
you should use a value that you will keep constant in relation to the sketch dimensions.

THREE.Sprite size in px with sizeAttenuation false

I'm trying to scale sprites to have size defined in px. regardless of camera FOV and so on. I have sizeAttenuation set to false, as I dont want them to be scaled based on distance from camera, but I struggle with setting the scale. Dont really know the conversion formula and when I hardcoded the scale with some number that's ok on one device, on the other its wrong. Any advice or help how to have the sprites with the correct sizing accross multiple devices? Thanks
Corrected answer:
Sprite size is measured in world units. Converting world units to pixel units may take a lot of calculations because it varies based on your camera's FOV, distance from camera, window height, pixel density, and so on...
To use pixel-based units, I recommend switching from THREE.Sprite to THREE.Points. It's material THREE.PointsMaterial has a size property that's measured in pixels if sizeAttenuation is set to false. Just keep in mind that it has a max size limitation based on the device's hardware, defined by gl.ALIASED_POINT_SIZE_RANGE.
My original answer continues below:
However, "1 px" is a subjective measurement nowadays because if you use renderer.setPixelRatio(window.devicePixelRatio); then you'll get different sprite sizes on different devices. For instance, MacBooks have a pixel ratio of 2 and above, some cell phones have pixel ratio of 3, and desktop monitors are usually at a ratio of 1. This can be avoided by not using setPixelRatio, or if you use it, you'll have to use a multiplication:
const s = 5;
points.size = s * window.devicePixelRatio;
Another thing to keep in mind is that sprites THREE.Points are sized in pixels, whereas meshes are sized in world units. So sometimes when you shrink your browser window vertically, the sprite Point size will remain the same, but the meshes will scale down to fit in the viewport. This means that a 5px sprite Point will take up more real-estate in a small window than it would in a large monitor. If this is the problem, make sure you use the window.innerHeight value when calculating sprite Point size.

Optimize draw calls for merged mesh with individual scaling

I have 10K cubes and the draw calls is very high, well.. 10K
You can see this here:
http://thegrook.com/three.js/merge1.html
If I combine all cubes to a single mesh, the draw calls is down to one.
You can see this here:
http://thegrook.com/three.js/merge2.html
But in the first example, I change the scaling of each cube based on the distance from the camera,
So no matter what is the zoom - the cube staying the same size on screen, and this give me the effect that I need:
The closer the camera is - the lower the density between the cubes
In the second example - this doesn't work, because the scaling affect the whole mesh and not per cube
Any idea how to achieve the density effect with less draw calls?
Thanks.
* Edited 1 *
I managed to solve this with instancing and render 250K objects and stay on 60fps
When I zooming out - the objects are overlapping - that's ok for my case.
But what's wrong is all the textures are flickering a lot..
Seem like there are no fixed drawing order for all the instancing
There is a way to fix this?
Here is an example:
http://thegrook.com/three.js/instancing3.html
* just zoom out with the mouse wheel
* Edited 2 *
Looks like If I disabling the depthWrite on the material - the problem is solved
Is this the right solution?

THREE.JS: Render large, distant objects at correct z-indicies and still zoom into small objects

I've got a scene where I'm drawing(to scale) the earth, moon, and some spacecraft. When the moon is occluded by the earth, instead of disappearing, it is still visible (through the earth).
From my research I found that part of the problem is that the near settings for my camera were much too small, as detailed in the article linked, small values of near cause rounding in z-sorting to get fuddled for very distant objects.
The complexity here is that I need to have fine grain z-indexes for when the camera is zoomed in, to look at a spacecraft (an object with a radius of 61 meters at most, in comparison to the earth, weighing in at r =~ 6.5e+06 meters). In order to make objects on the scale of the moon and earth to render in the correct order, the near has to be at least 100,000 m at which point I cannot look at close objects.
One solution would be to reduce the scale to use kilometers, but I cannot afford to lose that precision, and prefer to use meters.
Any ideas as to how to make very large, distant objects render at the correct z Indices, while retaining scale and ability to zoom into small objects?
My Ideas (which I don't know how to implement):
Change z-buffer to include more values, and higher resolution?
Add distant objects to a "farScene" which is rendered using a "farCamera" which is controlled by the same controls used on a close-up camera?
As per #WestLangley 's answer, the solution is simply to add the optionlogarithmicDepthBuffer: true to the renderer:
this.renderer = new THREE.WebGLRenderer({antialias: true, logarithmicDepthBuffer: true});
Probably that the problem is z-test and not z-precision. this mean: z-test not apply (perhaps because that you render transparent object with alpha blending) or z-test apply with non default testing (e.g. override far instead near).
Try to render the whole scene with simple shader with no transparency in-order to make sure that transparency is not the source of the bug.
to solve the z-order without z-test you should sort the object yourself each frame to determine the order of rendering (from far to close).

three.js - Overlapping layers flickering

When several objects overlap on the same plane, they start to flicker. How do I tell the renderer to put one of the objects in front?
I tried to use .renderDepth, but it only works partly -
see example here: http://liveweave.com/ahTdFQ
Both boxes have the same size and it works as intended. I can change which of the boxes is visible by setting .renderDepth. But if one of the boxes is a bit smaller (say 40,50,50) the contacting layers are flickering and the render depth doesn't work anymore.
How to fix that issue?
When .renderDepth() doesn't work, you have to set the depths yourself.
Moving whole meshes around is indeed not really efficient.
What you are looking for are offsets bound to materials:
material.polygonOffset = true;
material.polygonOffsetFactor = -0.1;
should solve your issue. See update here: http://liveweave.com/syC0L4
Use negative factors to display and positive factors to hide.
Try for starters to reduce the far range on your camera. Try with 1000. Generally speaking, you shouldn't be having overlapping faces in your 3d scene, unless they are treated in a VERY specific way (look up the term 'decal textures'/'decals'). So basically, you have to create depth offsets, and perhaps even pre sort the objects when doing this, which all requires pretty low-level tinkering.
If the far range reduction helps, then you're experiencing a lack of precision (depending on the device). Also look up 'z fighting'
UPDATE
Don't overlap planes.
How do I tell the renderer to put one of the objects in front?
You put one object in front of the other :)
For example if you have a camera at 0,0,0 looking at an object at 0,0,10, if you want another object to be behind the first object put it at 0,0,11 it should work.
UPDATE2
What is z-buffering:
http://en.wikipedia.org/wiki/Z-buffering
http://msdn.microsoft.com/en-us/library/bb976071.aspx
Take note of "floating point in range of 0.0 - 1.0".
What is z-fighting:
http://en.wikipedia.org/wiki/Z-fighting
...have similar values in the z-buffer. It is particularly prevalent with
coplanar polygons, where two faces occupy essentially the same space,
with neither in front. Affected pixels are rendered with fragments
from one polygon or the other arbitrarily, in a manner determined by
the precision of the z-buffer.
"The renderer cannot reposition anything."
I think that this is completely untrue. The renderer can reposition everything, and probably does if it's not shadertoy, or some video filter or something. Every time you move your camera the renderer repositions everything (the camera is actually the only thing that DOES NOT MOVE).
It seems that you are missing some crucial concepts here, i'd start with this:
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
About the depth offset mentioned:
How this would work, say you want to draw a decal on a surface. You can 'draw' another mesh on this surface - by say, projecting a quad onto it. You want to draw a bullet hole over a concrete wall and end up with two coplanar surfaces - the wall, the bullet hole. You can figure out the depth buffer precision, find the smallest value, and then move the bullet hole mesh by that value towards the camera. The object does not get scaled (you're doing this in NDC which you can visualize as a cube and moving planes back and forth in the smallest possible increment), but does translate in depth direction, ending up in front of the other.
I don't see any flicker. The cube movement in 3D seems to be super-smooth. Can you try in a different computer (may be faster one)? I used Chrome on Macbook Pro.

Resources