I m new in WebGl and i wish resize the canvas without enlarge all the elements.
i try to edit in the HTML file this line of code:
<canvas id="gl-canvas" width="512"" height="512">
with the new one
<canvas id="gl-canvas" width="1024"" height="1024">
but the figure inside the html will be more big and i dont want. I want just resize the space that contain the elements.
i tried to do this one too:
In the JS file i tried to edit
gl.viewport( 0, 0, canvas.width, canvas.height );
with
gl.viewport( 0, 0, 0.5*canvas.width, 0.5*canvas.height );
the figure will be half smaller, but the space remain the same. I need more space for the animation because now at some point the animation exits the screen even if I have a lot of space still available (which is white).
I want upload some photos for explain better my point
My Canvas
Details of the Web Inspector
thanks.
WebGL requires you to draw the items yourself to match the size. WebGL takes normalized coordinates in (called clip space). They always go from -1 to +1 across the canvas and -1 to +1 up the canvas regardless of size. To draw anything you supply math via JavaScript and/or GLSL shaders that take whatever data you supply and convert that data to clip space. That means if you want the items to stay the same size you need to adjust the math you're using to draw the things you are drawing. Without seeing your math we can't really know what to suggest.
The most common way to draw things in WebGL is to multiply 2D or 3D data by 1 or more matrices. You can read about that here. You may need to read the articles before that one to understand that one. You may also wish to read the articles after that one as they continue from 2D to 3D
So, then there is no easy answer except to say it's up to you to decide on a solution to fix your math. Without knowing what math you're using we can't suggest a way to keep the size the same.
If you were using clip space coordinates directly then you could just pass in an X and Y scale. if the canvas gets twice as large then scale by half as much and the objects would stay the same size.
gl_Position = someClipspaceCoordinates * vec4(scaleX, scaleY, 1, 1);
If you were using an orthographic projection then you'd choose some number of units per pixel and adjust the values you pass into your orthographic projection matrix making function
const pixelsPerUnit = ???;
const unitsAcross = gl.canvas.clientWidth / pixelsPerUnit;
const unitsDown = gl.canvas.clientHeight / pixelsPerUnit;
// assuming you want 0,0 at the center
const left = -unitsAcross / 2;
const right = unitsAcross / 2;
const bottom = -unitsDown / 2;
const top = unitsDown / 2;
const near = ???;
const far = ???;
const projectionMatrix = someOrthographicProjectionMatrixFunction(
left, right, bottom, top, near, far);
If you were drawing in 3D using a perspective projection then you'd have to either calculate a field of view based on the width or height of the canvas to keep objects the same size, OR, move the camera closer to or further away from the objects.
To adjust by field of view you'd do something like
const fovPerPixel = 0.1 * Math.PI / 180;
const fov = gl.canvas.clientHeight * fovPerPixel;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const near = 0.5;
const far = 1000;
const projectionMatrix = somePerspectiveProjectionMatrixFunction(
fov, aspect, near, far);
Note that this method has the issue that as the canvas gets larger the things drawn toward the edges of the canvas will get more and more distorted as the field of view gets wider and wider to keep everything the same size. When the field of view hits 180 degrees or wider the perspective math will break and things won't display.
To adjust by moving the camera see this answer as a starting point.
Related
I'm trying to make a simple side scrolling avoider game in html/javascript using canvas. Having some troubles removing (clearRect) the moving variable height clipped image so it doesn't also remove the sprite/image the user is controlling above, I can get it to remove everything above/below the image but cannot find how to exactly remove the moving shadow image so it doesn't remove the hero of the game as well whilst animating!
https://jsfiddle.net/6k354f5x/3/
Currently the banana is also cleared, any help would be greatly appreciated!
//<canvas id="board" width="480" height="640"></canvas>
//Getting the canvas
var board = document.getElementById("board");
var cx = board.getContext("2d");
//Example Images
var pipe = new Image();
pipe.src = "http://www.appcycle.me/flappy/img/pipe.png";
var hero = new Image();
hero.src = "http://vignette2.wikia.nocookie.net/drunken-peasants-podcast/images/9/9c/Banana-in-8-bit.png/revision/latest?cb=20150821213530";
//Pipe randomness calculated from board height
var pipeVariation = Math.floor((Math.random() * 250) + 1);
var pipeY = 456;
var pipeX = 350;
//interval
var timer = setInterval(function() {
//draw the hero
cx.drawImage(hero, 0, 150);
//clear the afterimage
cx.clearRect(pipeX, 80, pipe.width / 1.6, pipe.height / 1.6);
//move it on the X-axis some px
pipeX -= 2;
//draw the clipped pipe with some Y-axis placement variation from pipeVariation variable
cx.drawImage(pipe,
0, -pipeY+pipeVariation, pipe.width, pipe.height,
pipeX, 80, pipe.width / 1.6, pipe.height / 1.6)
//Temporary to keep pipe from running away while testing
if (pipeX <= 0) {
clearInterval(timer);
}
});
Full canvas animation redraw everything.
When rendering canvas animation that has many elements, such as a game, the most effective method to render is to redraw everything every frame.
At the start of every frame you either clear the whole screen or draw the background image over the top of the last frame (same as clearing) then draw the game elements one by one in order of lowest to highest z-index (where the highest element is on top of all the other elements)
This makes the code very simple as opposed to complex dirty rects schemes that quickly become very complex and can reduce performance.
On all but a small number of devices most rendering is done via the GPU and is very fast (NOTE this does not apply to moveTo, lineTo, shadows, arcs, text, ellipses) Clearing and re-rendering everything can easily be done in 1/60 of a second creating smooth high quality animations
Note on shadows. Do not use the context2D shadows, most browsers and the hardware do not do it well at all and can be a major performance hit. This can be so even if you render one shadow for one small image. It is best to either pre render the shadow, or load a separate shadow as an image.
I use getImageData and putImageData to draw on canvas from a buffer canvas. I use these methods because I have a large number of particles and these proved to provide the best performance.
Now I'd like to add rotation of particles but I'm having problems with that.
Here is a jsfiddle which uses transformation matrix for rotation. As you can see in the picture (or fiddle) there are holes in the resulting image which I kinda expected from using this matrix.
nx = ~~ (xx * Math.cos(angle) + yy * Math.sin(angle) + cx);
ny = ~~ (xx * Math.sin(angle) - yy * Math.cos(angle) + cy);
But I don't know how to make this better, especially when I'm looking performance effecient solution?
jsfiddle demo
Image - square after rotation (square is used as a simple body):
Currently my backup is procedurally generated sprite animation which is prepared in advance with standard canvas states: save -> translate -> rotate -> restore.
Thank you very much for any directions you can give me.
The problem is that you are trying to map a single pixel to a single pixel. When you rotate an image, each pixel in the original can influence any of the surrounding pixels in the new image. You are effectively mapping the top left corner of each pixel to it's location in the new image, but you need map the center of each pixel to it's location in the new image and then check the overlap of this rotated pixel with that location, and the 8 surrounding pixels in the new image.
Here you can see the effect. The yellow dots are the centers of the pixel which find the "home" location for the pixel (i.e. where the majority of the influence will be placed). You then need to figure out the percentage of that pixel (the underlying blue/white grid) cell is covered by the original pixel (black box surrounding the yellow dot). Once you figure out the home location influence, you need to repeat that process for the 8 surrounding pixel with respect to current pixel in the original image. In your current code, you are using the top left corner of each pixel to find the home pixel for the new image. You should use the center of the pixel.
Since multiple iterations might affect the same pixel, you'll need to calculate the transformation in a buffer before drawing it to the final image. For pixels in the transformation that are not fully covered by pixels in the original image, figure out the percentage of the pixel that is covered and use that to influence the alpha channel. You'll have to take care when applying the pixels to the final image that you account for the alpha portion and blend with what's already there.
I've been playing with canvas element and discovered that when I attempt to draw NxN uniform solid-colored cells next to each other, in some width/height configurations, there are blurry white-ish lines between them.
For instance, this canvas is supposed to look black but contains some sort of grid which I conjecture to be a result of faulty antialiasing in the browser.
Suffice to say, this bug appears only in some configurations but I would like to get rid of it for good. Is there any way to circumvent this? Have you ever had problems with antialiasing in canvas?
I have made this fiddle which demonstrates the issue and allows you to play with the dimensions of the canvas and number of cells. It also contains the code I use to draw the cells, so that you can inspect it and tell me if I'm doing anything wrong.
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
for (var i = 0; i < numberOfCells; ++i) {
for (var j = 0; j < numberOfCells; ++j) {
ctx.fillStyle = '#000';
ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight);
}
}
Thanks in advance,
Petr.
jsFiddle : https://jsfiddle.net/ngxjnywz/2/
snippet of javascript
var cellWidth = Math.ceil(canvasWidth / numberOfCells);
var cellHeight = Math.ceil(canvasHeight / numberOfCells);
Depending on the width, height and your numberOfCells you are sometimes getting a... lets say 4.2 which is 4, however this would be displayed wrong and will allow a 1 pixel blank line to appear. So all you need to do is use the Math.ceil function and this will cause your cellWidth and cellHeight to always be the higher number and you won't get blank lines anymore
The best solution is to add a 0.5 pixel wide stroke around all the fills, using the same style as the fill and offsetting all drawing so that you render at the center of pixels rather than the top left.
If you add scaling or translation you will have to adjust the coordinates so that you still give the centers for your drawing coordinates.
In the end you can only reduce the artifacts but for many situations you will not be able to completely remove them.
This answer shows you how to remove the artifacts for an untransformed canvas.
How to fill the gaps
After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.
After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.
In spite of my efforts, the lines are still there. Any suggestions?
Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/
I've searched through many related questions on stack overflow, but I couldn't quite find the answer to this problem.
I have a big, dynamic group of Object3D's in my scene, with an orthographic camera looking at them head-on.
I want to have a simple function which, when called, will simply match the top/left/bottom/right/zoom properties of the Orthographic camera to properly fit the Object3D group.
I've tried all kinds of things, but none of my code is worth posting. I need to look at this from a whole new angle (pun intended). I have found various other answers which discuss changing the fov of the camera, once you know the distance from the face of bounding box of the group to the camera, but I don't know how to implement that with an orthographic camera, since (as far as I've tried) the fov property doesn't work with it (maybe it actually does, I just don't know).
So I don't particularly like asking for code, but nevertheless I would like a function which would automatically adjsut the appropriate properties of the Orthographic camera to fit the object passed to it as a parameter, for example:
function fitOrthographicCameraToObject3DGroup(group) {
//implement here (my question)
}
Calculate the bounding box of your mesh and apply this code.
This works for me
var camera = new THREE.OrthographicCamera(container.offsetWidth / -2, container.offsetWidth / 2, container.offsetHeight / 2, container.offsetHeight / -2, 100, 100000);
//For centering the meshGroup
var box = new THREE.Box3().setFromObject(meshGroup);
box.center(meshGroup.position);
meshGroup.localToWorld(box);
meshGroup.position.multiplyScalar(-1);
//For fitting the object to the screen when using orthographic camera
Camera.zoom = Math.min(container.offsetWidth / (box.max.x - box.min.x),
container.offsetHeight / (box.max.y - box.min.y)) * 0.4;
Camera.updateProjectionMatrix();
Camera.updateMatrix();
Setting the top/left/bottom/right of the orthographic camera once you have the bounding box should not be a problem. Just take the half lengths of the bounding box.
The zoom is the issue and for that you can confine your scene in a unit cube by scaling up or down your scene by the appropriate amount depending on your bounding box size. Then you dont have to worry about the zoom and the above top/left/bottom/right values become 0.5/0.5/-0.5/-0.5.
Here is the graphics:
http://snag.gy/aVFGA.jpg
the big rectangle is canvas element, the small rectangle is the image object in the canvas. I want to find what is the real distance from the left.
values are such from what I see in console:
regX: 564.256
regY: 41.4
scaleX: 0.4491319444444445
scaleY: 0.4491319444444445
x: 363.3333333333333
y: 409.77777777777777
So as I see x is not real. It somehow relates with regX and scaleX. But I am not finding how it relates. From the image I think the x should be about 100 - 150 px.
THe bigger the x - the more it is to the right.
But the bigger regX - the more it makes rectangle go to the left.
So if I would just take the difference 564.256 - 363.333 = ~200 - left corner of the rectangle should be in them middle of canvas because canvas is 400px widh. But it is not, so substraction does not help. So how do I get how many pixels are in real from the left?
You can do this by using the localToGlobal method (see here).
It depends to which object the given attributes belong.
If they belong to the shape and your rectangle inside the image / shape starts at (0,0):
var point = shape.localToGlobal(0, 0);
// this will calculate the global point of the shape's local point (0,0)
If they belong to the stage:
var point = stage.localToGlobal(yourRectObject.x, yourRectObject.y);
// point.x should contain the position on the canvas
You should use these methods in general because your method might work for the current situation but will probably break as soon as you scale the stage itself or put the shape in a scaled / positioned container.
I guess I found what by experimenting with values:
distanceFromLeft = x - scaleX * regX;
so getting 109.90793888888885 px
If someone has worked more with this library, they could confirm that its not accidental.