Using parallax scrolling, cube has distortion - javascript

I am creating a 2d platformer right now, and I decided that I would use parallax scrolling.
So what I did is I had 3 tile maps, one for the background, one for the foreground, and one for on front of the foreground.
Each tile map would display the tiles at a different size.
Background -> 48px
Foreground -> 64px
On front of foreground -> 80px
I got the pixels from looking at other 3d games(such as Minecraft), when cubes are further a way. I wanted each tile to be as if the size of it is a cube.
I used the below code to calculate where each tile goes on the screen, based on its x, y, and size.
x - (cameraX * size / 64) + canvas.width / 2; // 64 is the size of the texture.
y - (cameraY * size / 64) + canvas.height / 2;
Here was the result:
As you can see the tiles in the background are moving slower.
However, when you jump, there would be white space between the tiles:
I understand why this is happening, it is because there should be a top face on the tile (as if it is a cube), but since it is only a square, it shows white space.
So I used this code to make it look as if it is a 3d cube.
var i = (size - size * 0.25);
while (i < size)
{
ctx.drawImage(images[this.type], getProperX(x*(i), i), getProperY(y*i, i), i, i);
// The first argument of getProperX/getProperY is the x/y, the second argument is the size of the tile.
i += 1;
}
This keeps drawing loads of squares, with a size slightly smaller than the last, so that the getProperX/Y function will display it in the right place.
Now they looks like cubes, so the problem is nearly fixed:
The only problem now, is that the length of the cubes are very long when you are looking on top/bottom of it, or to the right/left of it (as if it is distorted).
Why are the cubes/tiles so long, can anyone explain to me?
How would you go about fixing something like this?
Did I do something wrong in my parallax scrolling function getProperX/Y()? because I don't see why there is so much space between the distant tiles and the close ones.
Thanks for all help, very appreciated.

Related

How to get the position of object in Three.js relative to the camera to draw a trail?

I have an object (a pen) in my scene, which is rotating around its axis in the render loop.
groupPen.rotation.y += speed;
groupPen.rotation.x += speed;
and I have also a TrackballControls, which allows the user to rotate the whole scene.
What I now want is to get the "real" position of the pen (or its pick) and place small spheres to create a trail behind it.
This means I need to know where the camera is looking at and place the trail spheres behind the peak of the pen and exclude them from the animation and the TrackballControls.
What I tried is:
groupSphereTrail.lookAt(camera.position);
didn't work. Means no reaction at all.
camera.add(groupSphereTrail);
didn't work. groupSphereTrail is than not in the view area, couldn't make it visible - manipulating position.z didn't help.
Then I tried something like sending a tray with traycaster. The idea was to send a ray from the center of the camera through the peak of the pen and then draw the trail there. But then I still doesn't have the "real" position.
Another idea was to create a 2d vector of the current position of the pen peak and just draw an html element on top of the canvas:
var p = penPeak.position.clone();
var vector = p.project(camera);
vector.x = (vector.x + 1) / 2 * width;
vector.y = -(vector.y - 1) / 2 * height;
but this also doesn't work.
What could be another working solution?
Current progress:
https://zhaw.swissmade.xyz
(click on the cap of the pen to see the writing - this writing trail should stay at its place when you rotate the camera)
If i understood the question right, you want to show the trail as if it were draw on the screen itself (screen space)?
yourTrailParticle.position.project(camera)
camera.add(yourTrailParticle)
That's the basic idea, but it gets a bit tricky with PerspectiveCamera. You could set up a whole new THREE.Scene to hold the trail, and render it with a fixed size orthographic camera.
The point is .project() will give you a normalized screen space coordinate of a world space vector, and you need to keep it somehow in sync with that camera (since the screen is too). The perspective camera has distortion so you need to figure out the appropriate distance to map the coordinate to. With a separate scene, this may become easier.

Complex mathematical horizontal parallax easing function

I'm currently diving into parallax effects on the web.
Parallax scrolling is a technique in computer graphics and web design, where background images move by the camera slower than foreground images, creating an illusion of depth in a 2D scene and adding to the immersion.
~ Wikipedia
I want to create a little container (could be an image, or any block level element) and move it across the screen horizontally as the user scrolls.
The effect should be scalable across all viewports. Meaning that the hight and the width of the element that the element is moving across should not matter.
When the user has scrolled half of the height of the screen the "moving element" should be in the exact center. Since the user will have scrolled half of the screen the element will be vertically already. We're only worried about horizontally right now.
I've thought about this question for a while and came up with a pretty good idea of how.
Take the hight and the width of the element you want the "moving element" to move across. For example a screen that is 1000px tall and 600px wide.
Divide the width by the height. For example (600px / 1000px = 3/5 = 0.6)
Take the amount of pixels the user scrolled and multiply it by the number we just created. For example (500px * 0.6 = 300px). As you can see this is the exact center.
Move the element across the screen by the amount of pixels just calculated.
This calculation works fine even for every screen size, however it's linear. Meaning that the element will move at the same speed across the screen all the time. Let me show you what I mean.
Let's draw out a screen size. (Let's say 1000 * 500)
Calculate two points for this graph ->
screen factor: (500 / 1000) = 0.5
1. The first point is going to be easy. Let's say we scrolled exactly 0px -> (0.5 * 0) = 0
The "Moving element" will not have moved at all.
2. For the second element we'll take the center. Just for convenience.
The vertical center is at 500px -> (0.5 * 500) = 250 px (Exactly the horizontal center)
Put the results in a graph and draw a line through the points.
In the graph above you can see that whenever the user scrolls down the "moving element" will follow the line (the values on the x-axis).
My question
I really hope I described all that well enough to understand. Now on to my question.
What I want to create is a moving element that would go faster on the edge of the screen and slow down a bit in the middle. If we were to draw that out in the same way we just did. (Creating a graph where we can take the amount of pixels scrolled and see where the element should be positioned horizontally) it would look like this:
Sorry for the poor quality of the image but this is the part I'm having problems with.
As you can see in this graph the "moving element" wouldn't be moving all that much in the middle of the graph. (I over did it a bit in my drawing but you get the general idea.)
What I need is a mathematical function that takes three parameters (Screen height, width and the amount of pixels scrolled) and returns the horizontal position of the "moving element".
My idea:
My idea was to position the element in the dead center of the page and then to move it left and right (translations using CSS and JavaScript) based on how far there has been scrolled.
The graph would look something like this:
The (Hand drawn) graph above would be true for a screen that's 1000x600px since the "moving element" translates -300px when no scrolling has been done and 300px when 100% has been scrolled.
However I have no idea on how to create a mathematical function that would be true for every screen size.
To be clear I need a function that "always" starts at Y= (-screen-width/2) and X = 0. It should always cross the point (sreen-height; (screen-width//2)) and the function should be in a form of x^3 (To get the right easing)
I really hope I explained myself well enough and I really hope somebody can help me out here.
Answer from math.stackexchange
Besides asking my question here I also went ahead and posted this question on math.stackexchange.com. Stackoverflow's mathematical sister site. Somebody there helped me find an answer to my question.
The function had to be a function that would output the moving-element it's horizontal position in pixels relative to the horizontal center of the page based on the amount of pixels scrolled since the element was first visible. The function would have to be "steeper" on the edges and ease into a short stop in the middle and be usable across every possible screen size. Meaning that the mathematical function would have to be positioned based on two variables, screen height and -width.
The answer they came up with on math.stackexchange:
In this example s-width is the width of the screen in pixels. s-height is the height of the screen in pixels. px-scrolled is the amount of pixels scrolled since the element was first visible.
The output pos is the moving elements horizontal position in pixels relative to the center of the screen.
If you put all this math into JavaScript you get the following:
var pos = ((4*win_width)/(Math.pow(win_height, 3))) * Math.pow(px_since_visible - (win_height/2),3)
There is a working example available on codepen. You can find it here.
You can position it with a function f that actually draws that trajectory.
This is the idea I propose:
Create the function trajectory f such that f(0) = 0, and f(1) = 1 (add more constraints in order to reproduce the effect you are looking for, ex: f(0.5) = 0.5)
Within each scroll event, set x as the amount scrolled and position the element using the coordinates (f(x) * (w - s), x * (h - s)), where w is the document width, h is the document height and s is the size of the element
I can see that cubic functions are plotted like the trajectory you want, so I've been testing with different functions and I've got this working example https://codepen.io/anon/pen/YZJxGa
var element = $('.element')
var height = $(document).height()
var scrollable = $('body').height() - $(window).height()
var width = $('body').width();
$(window).scroll(function () {
var winH = $(window).scrollTop()
var x;
// Determine the amount of px scrolled since the element first came into view.
console.log(winH, scrollable)
x = (winH/scrollable) > 1 ? 1 : (winH/scrollable);
var posY = x * (height - 120);
var posX = (-1.000800320128*x
+6.0024009603841*x**2-4.0016006402561*x**3)*(width - 120)
console.log(posY)
if (x > 0) {
console.log(`translate(${posX}px, ${posY}px, 0)`)
element.css({
'transform': `translate3d(${posX}px, ${posY}px, 0)`
})
}
})
You can generate more cubic functions using this tool I've just found http://skisickness.com/2010/04/28/ or solve a couple of systems of linear equations using the fact that you just want to find values for a, b, c and d for f(x) = ax^3 + bx^2 + cx + d

Initial Sprite Size in Three.js

Can someone help me understand how three.js initially determines the size/scale of a sprite?
At the moment I'm working with 4 sprites (PNGs with transparency that are 3000px × 1830px) stacked in 3D space, but I'm having to scale them up between 16x and 22x. In order to keep the sprites from looking squashed, though, I have to scale the y-axis 75% of the x-scale.
Eventually, I want to be able to pull in images systematically, and have them scale appropriately.
It's possible I just haven't set this thing up correctly. It looks right, but it's super hacky-feeling right now. I pretty much changed a bunch of numbers, until it looked right to me. I don't like that. I want to understand.
Here's what I'm working with currently:
http://valorink.com/3d-test/stackoverflow/
Looking into the code of the Sprite class reveals that a simple plane with width and height of 1 is created in the constructor. I wouldn't have expected anything else, because the geometry size is usually not defined by the texture size.
You probably want them to fill the viewport, so you have to scale them. With perspective camera its a bit of math, because the amount of x-scale (or y-scale) to fit the viewport size relates to the distance to the camera. So, it should be something like
var halfHeigt = distanceToCamera / Math.tan( camera.fov/2 * Math.PI / 180 );
y-scale = halfHeight * 2;
And of course you need to consider the aspect ratio in order to not looking squashed. So, x-scale should be y-scale * textureWidth / textureHeight, or the other way round y-scale = x-scale * textureHeight / textureWidth.

Why is canvas blurry? [duplicate]

I'm trying to draw a grid of white lines on a black background.
The bottom 3 horizontal lines seem faded until I redraw them, and I can't figure out why this is happening. Has anyone seen this before and/or know what I'm doing wrong?
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
See illustration : The first horizontal line was drawn with a y position of 1. This line is fuzzy and wide. The second horizontal line was drawn with a y position of 4.5. It is thin and precise.
For example in your code, I had good results by changing your horizontal lines loop to this :
// Horizontal lines
for (var i = 1; i < objCanvas.height / intGridWidth; i++)
{
objContext.strokeStyle = "white";
var y = Math.floor(i*intGridWidth)+0.5
objContext.moveTo(0, y);
objContext.lineTo(objCanvas.width, y);
objContext.stroke();
}
Here's a fiddle demonstrating it with very thin and clean lines :
http://jsfiddle.net/dystroy/7NJ6w/
The fact that a line is drawn over all pixels it is over means the only way to draw an horizontal line with a width of exactly one pixel is to target the middle. I usually have this kind of util functions in my canvas based applications :
function drawThinHorizontalLine(c, x1, x2, y) {
c.lineWidth = 1;
var adaptedY = Math.floor(y)+0.5;
c.beginPath();
c.moveTo(x1, adaptedY);
c.lineTo(x2, adaptedY);
c.stroke();
}
Of course you'd better do it for vertical lines too to have a good looking page.
It doesn't look faded for me. Maybe it's something to do with your OS or PC, which is not able to render the drawing properly. I'm using Chrome 20 on Win 7. Test it out.
You have to define objContext.lineWidth like this:
objContext.lineWidth = 2;
I'm not sure why last line gets faded though.
See http://jsfiddle.net/jSCCY/

Calculate new width when skewing in canvas

I'm using canvas for a project and I have a number of elements that I'm skewing. I'm only skewing on the y value and just want to know what the new width of the image is after skewing (so I can align it with another canvas element). Check out the code below to see what I mean
ctx.save();
//skew the context
ctx.transform(1,0,1.3,0,0,0);
//draw two images with different heights/widths
ctx.drawImage(image,0,0,42,60);
ctx.drawImage(image,0,0,32,25);
The goal would be to know that the 42 by 60 image was now a X by 60 image so I could do some translating before drawing it at 0,0. It's easy enough to measure each image individually, but I have different skew values and heights/widths throughout the project that need to be align. Currently I use this code (works decently for images between 25 and 42 widths):
var skewModifier = imageWidth*(8/6)+(19/3);
var skewAmount = 1.3; //this is dynamic in my app
var width = (skewModifier*skewAmount)+imageWidth;
As images get wider though this formula quickly falls apart (I think it's a sloping formula not a straight value like this one). Any ideas on what canvas does for skews?
You should be able to derive it mathematically. I believe:
Math.atan(skewAmount) is the angle, in radians, that something is skewed with respect to the origin.
So 1.3 would skew the object by 0.915 radians or 52 degrees.
So here's a red unskewed object next to the same object skewed (painted green). So you have a right triangle:
We know the origin angle (0.915 rads) and we know the adjacent side length, which is 60 and 25 for your two images. (red's height).
The hypotenuse is the long side thats being skewed.
And the opposite side is the triangle bottom - how much its been skewed!
Tangent gets us opposite / adjacent if I recall, so for the first one:
tan(0.915) = opposite / 60, solving for the opposite in JavaScript code we have:
opposite = Math.tan(0.915)*60
So the bottom side of the skewed object starts about 77 pixels away from the origin. Lets check our work in the canvas:
http://jsfiddle.net/LBzUt/
Looks good to me!
The triangle in question of course is the canvas origin, that black dot I painted, and the bottom-left of the red rectangle, which is the original position that we're searching for before skewing.
That was a bit of a haphazard explanation. Any questions?
Taking Simon's fiddle example one step further, so you can simply enter the degrees:
Here's the fiddle
http://jsfiddle.net/LBzUt/33/

Categories