WebGL not drawing within normalized coords? - javascript

I wrote a simple WebGL page (well, as simple as I could make it) and can't figure out why normalized coordinates aren't working. That is, I'm trying to draw a square from (-0.5, -0.5) to (0.5, 0.5) via two triangles, and instead I get a large rectangle that goes off screen.
I'm completely unsure what I am doing wrong, so I pasted it into a jsFiddle:
Link
Notice how I define my verts and indices correctly (right?) and my shader and fragment programs are as simple as they come.
Why is it drawing as a weird large off-screen rectangle?

Okay I finally got help from someone in #webgl on Freenode: the <canvas> element needs a width and height attribute, whereas I'm setting them via CSS. Apparently the actual width and height attributes determine the width and height of the OpenGL buffer.
I was not able to find this documented anywhere else, except that WebGL examples have the width and height attribute on the canvas tag.

Same as other elements the size something is displayed is separate from the size it is. Examples:
Display a 4x4 JPGs as 4x4 pixels
<img src="some4x4pixel.jpg" />
Display a 4x4 JPGs at 16x16 pixels
<img src="some4x4pixel.jpg" width="16" height="16" />
Display a 4x4 JPG at 16x16 pixels
<img src="some4x4pixel.jpg" style="width:16px; height:16px;"/>
Display a 4x4 JPG at whatever size it's container is
<img src="some4x4pixel.jpg" style="width:100%; height:100%;"/>
Display a 300x150 pixel canvas (the default size) at 300x150 pixels
<canvas></canvas>
Display a 4x4 canvas at 4x4 pixels
<canvas width="4" height="4"></canvas>
Display a 4x4 canvas at 16x16 pixels
<canvas width="4" height="4" style="width:16px; height:16px;"/></canvas>
Display a 4x4 canvas at whatever size it's container is
<canvas width="4" height="4" style="width:100%; height:100%;"/></canvas>
If you're doing 3D and using the fairly common perspective function found in most 3D math libraries you generally want to set the aspect ratio to the display size, not the actual size.
// 45degrees, assumes math library uses radians
var fieldOfView = Math.PI / 4;
// Use the size the canvas is displayed, NOT the size it actually is.
var aspectRatio = canvas.clientWidth / canvas.clientHeight;
var zNear = 0.1;
var zFar = 1000;
var matrix = someMathLibrary.perspective(fieldOfView, aspectRatio, zNear, zFar, ...)
If you want the size the canvas exists to be the same size as it's displayed use
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
Though note, a WebGL canvas is not required to be the size you request since various GPUs have limits on size. If you want to know the size you actually got vs the size you requested check
gl.drawingBufferWidth
gl.drawingBufferHeight
For the perspective matrix used by most 3D apps the actual size of the canvas is not that important. For certain kinds of effects or for 2D it is often important.
The reason this is hard to find documentation for is because it's taken for granted that people know that CSS decides the display size for all elements regardless of the actual size of their content.
https://www.google.com/webhp?sourceid=chrome-instant&ix=sea&ie=UTF-8&ion=1#sclient=psy-ab&hl=en&site=webhp&source=hp&q=canvas%20size%20css&pbx=1&oq=&aq=&aqi=&aql=&gs_sm=&gs_upl=&fp=f131b50fb368d7bf&ix=sea&ion=1&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&biw=1629&bih=931

Related

How can i get high resolution image from smaller canvas?

I am using one canvas in my web app and it's actual height and width are 500px. I am showing this canvas on screen as 500px square but i want image exported from this canvas as 1600px square. I have tried below code with no luck.
canvas.width = 1600;
canvas.style.width = 500;
Any help will be appreciated.
You can have the canvas display at 500px while still having a resolution of 1600px. Display size and resolution are independent. For resolution you set the canvas width and height properties. For display size you set the canvas style width and height properties.
// create a canvas or get it from the page
var canvas = document.createElement("canvas");
// set the resolution (number of pixels)
canvas.width = canvas.height = 1600;
// set the display size
canvas.style.width = canvas.style.height = "500px";
// get the rendering context
var ctx = canvas.getContext("2d");
To get the rendering to match the display size you need to scale up all rendering. You can do this by setting the transform scale to the canvas resolution divided by the display size
var scale = 1600 / 500; // get the scale that matches display size
ctx.setTransform(scale,0,0,scale,0,0);
Now when you render to the canvas you use the screen size coordinates.
ctx.fillRect(0,0,500,500); // fill all of the canvas.
ctx.fillStyle = "red"; // draw a red circle 100 display pixels in size.
ctx.beginPath();
ctx.arc(250,250,100,0,Math.PI * 2);
ctx.fill();
When you then save the canvas, what ever method you use as long as it is not screen capture the saved canvas will be 1600 by 1600 and all the rendering will be correctly positions and proportional
HTML
<canvas width="1600px" height="1600px" > </canvas>
CSS
canvas{
position :absolute;
transform:scale(0.3125);
left:-500px; //adjust
top:-350px; //adjust
}
Use transform:scale() to adjust size of your canvas
Now 1600 * 1600 will be the actual size of your canvas, so you can directly export images from your canvas
But in view it show as 500px * 500px beacuse it's scaled down, it dose not affect the image quality while exporting
Honest answer: you can't.
If you did, then you'd have found a way to losslessly compress data with less than 1/9th of the original size, and without any encoding, which is unarguably impossible.
What you can do is scale it up in a way that it at least doesn't get blurry. To do that, you need the final image to be an integer multiple of the previous canvas, so the browser won't apply anti-aliasing. Or if you want to use your own copying formula with putImageData that would get rid of anti-aliasing, you'll still get various incongruences and it would be very slow
In your case, the closest you could get is 1500x1500 ( 3*500x3*500 ). If your point was to process an image, you're not in luck, but if you just want to display something good enough, you can resort to various other tricks such as centering the canvas and using properties like box-shadow to make it clear that it's separate from the rest of the screen

HTML5 Canvas bitmaps

I am building a retro styled game, that uses pixelated images. I have not yet created these images, because I wanted to know the best way of doing things.
These images will probably be a 16 or 32 PX square, but I would like to be able to scale the images as big as I like, just without any blur/distortion.
What format should I use? And how should I import them to my canvas. as well?
EDIT#1: Fixed typo & put Q back on topic. (Thank you Spence for pointing it out)
Try "Inkscape", its free
https://inkscape.org/en/
it uses SVG format (scalar vector graphics) so you will be able to scale the images as big as you like, just without any blur/distortion.
The only way to enlarge without any blur or distortion is turn each 1 pixel into a set of 2x2, 3x3, ... pixels.
For example, a single blue pixel in the top-left of the image would become a set of 4 blue pixels at [0,0], [1,0], [0,1] & [1,1]. And the same for every other pixel on the original image. The resulting image would be twice the width & height of the original image.
Since your graphics style is pixelated images, this adjustment would preserve your pixilation while also enlarging the original image.
You can code a function that uses an in-memory html5 canvas to "resize-by-multiplying" your original images as needed. This will use canvas's ability to set the RGBA values every pixel using context.getImageData and context.putImageData.
CanvasContext2d does have an option to disable the image smoothing : imageSmoothingEnabled which is set to true by default.
According to the specs, if set to false,
The image [drawn by drawImage() method] must be rendered using
nearest-neighbor interpolation.
This algorithm is the same as the one proposed by #markE in his answer.
Unfortunately, browsers still use vendor-prefix for this attribute and it wasn't implemented in both IE9 and IE10...
var img = document.querySelector('img'),
canvas = document.querySelector('canvas'),
ctx = canvas.getContext('2d');
// draw the image
img.onload = function(){
canvas.width = img.width*50;
canvas.height = img.height*50;
// disable smoothing after we change canvas' width/height
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.msImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0,0, canvas.width, canvas.height);
}
//32x32px image taken from https://stackoverflow.com/q/31910043/3702797
img.src="http://i.stack.imgur.com/3Sp5x.png"
canvas{border:.5px solid}
<img/>
<canvas></canvas>
Scroll to see the resized image in canvas
Create large icon images to which you apply a 16x16 or 32x32 tile effect. Then when you write them to the canvas (after loading the images of course) scale them down to the size you want using
context.drawImage(img,x,y,width,height);
File sizes are unlikely to jump greatly since each tile should compress fairly easily.

How redraw on canvas resize without blurring?

When the canvas element is resized (via the style changing) I also want to scale the canvas' drawn image as well. I cannot just change the height/width as this causes the canvas to clear itself, so I do:
Create a temporary canvas element
Draw the current canvas' image onto that temporary canvas
Resize the current canvas
Draw the temp canvas' image back to the current canvas but scaled to the new size
This results in some blurring - very noticeable after many resizes (example: when dragging to resize). How would I do this without any blurring?
EDIT: Turning off image smoothing (context.webkitImageSmoothingEnabled = false;) does not fix the problem, it simply makes it redraw it more and more jagged until the image looks nothing like the original after a number of resizes.
Called on resize event:
var tmpCanvas = null;
//Make a temporary canvas
tmpCanvas = document.createElement( "canvas" );
//Set its size to be equal
tmpCanvas.height = originalCanvas.height;
tmpCanvas.width = originalCanvas.width;
//Draw our current canvas onto it
tmpCanvas.getContext( "2d" ).drawImage( originalCanvas, 0, 0 );
//Set new dimensions
originalCanvas.width = originalCanvas.offsetWidth;
originalCanvas.height = originalCanvas.offsetHeight;
var originalContext = originalCanvas.getContext( "2d" );
//Set background and colors
originalContext.fillStyle = "#ffffff";
originalContext.strokeStyle = "#000000";
//Set paintbrush
originalContext.lineWidth = 4;
originalContext.lineCap = "round";
//Fill background as white
originalContext.fillRect( 0, 0, originalCanvas.width, originalCanvas.height );
//We have a saved signature
if ( SignatureCanvas.hasSignature === true )
{
//Draw it back but scaled (results in blurred image)
originalContext.drawImage( tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height, 0, 0, originalCanvas.width, originalCanvas.height );
/**
* This results in a blurred image as well
//Draw it back but scaled
originalContext.scale( originalCanvas.width / tmpCanvas.width, originalCanvas.height / tmpCanvas.height );
originalContext.drawImage( tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height, 0, 0, tmpCanvas.width, tmpCanvas.height );
*/
}
Is there a way to get the strokes and "scale" all those points and redraw?
Instead of taking the rendered image from the original canvas, actually redraw the image. By that, I mean execute the same logic you executed against the original canvas, but with the points involved scaled to the new size.
If you can, think about using SVG instead. It scales well by its nature.
Edit: Another option I've thought of is to simply use a gigantic canvas to start with. Sizing down tends to look better than sizing up, especially with smoothing on.
Edit II: The original answer was irrelevant, though the comment I had made is relevant, and am now promoting it and editing it to be an answer...and the answer I had given was not all that great anyway **.
Of course if you scale up raster graphics, that is, from an image with a smaller pixel dimensions of pixels, create an image with higher pixel dimensions, you are going to get blurred images. By scaling up, you're making a low resolution picture high resolution, but without the high resolution details.
There's absolutely no way around that blurriness unless you make multiple additional assumptions about your raster image like the only gray you'd see is at an image edge, or corners can only occur at apparent inflection points where the angle between the tangents of the joined curves must be 100 degrees or less. Essentially, you'd have to give additional information so that your higher resolution image can have detail "filled in". It's not all that terribly different from reverse engineering an SVG from a raster.
So, you appear to want to emulate is scaling vector graphics, in which the only solution is to save the commands, draw a SVG, or draw to a bigger canvas like Stuart Branham suggested.
** I had originally proposed that invoking drawImage would distort the pixels even if it were not scaled, and that it would be better to work with the actual pixel data. If that's true, I can't find proof, but that's irrelevant, as he wanted his image scaled up, without blurring...which is impossible, as I just mentioned.

Drawing a Canvas into a smaller Canvas not working

For a Project I want to take the content of a canvas (Called SAVE_CV) and display it in another, smaller canvas.
Some things that I am aware of so far that could be causing me problems: resizing a canvas clears its content, the JS-size of a canvas is different from the CSS-size.
I want the smaller canvas to be 500px wide and appropriately high.
function restoreTaggingCV() {
var cv = document.getElementById( 'taggingCV' );
var ctx = cv.getContext( "2d" );
var styleHeight = SAVE_CV.height * 500 / SAVE_CV.width;
ctx.drawImage(SAVE_CV, 0, 0, cv.width, cv.height);
}
This is my Code so far. Whenever I try to resize the smaller canvas appropriately it only gives me a blank canvas with nothing in it. I tried to set the size with "cv.height = X" and "cv.style.height = styleHeight + 'px'" but neither worked. Also I would love to set the width of the canvas using CSS.
Appreciate any help.
EDIT
I want the image in a picture because later I want the user to mark areas in the smaller version which I then want to use to create individual imaged from the big version. I want to visualise thise area to the user. I probably could do all this by using an image and putting divs over it or something but I just fell more comfident using a canvas since I am pritty new to HTML and CSS.
Try using the CanvasRenderingContext2d.prototype.scale method. It sets the scale factor of the canvas and renders anything in the current state with it's dimensions multiplied by the factor.
So before you use the drawImage function, you scale the context appropriately (in this case, down). For example:
context.save();
context.scale(0.5, 0.5);
context.drawImage(canvas, 0, 0);
context.restore();
This would render the canvas on the context at 0.5 times it's current size. See in this fiddle how I used it to mirror a larger canvas onto a smaller, separate one.
Canvas objects don't like to be resised. After drawing Your image simply convert it toDataURL() and set as image source. They You may resize image as you want.
$img.attr('src',canvas.toDataURL());

HTML5 canvas image scaling to a smaller size and back to original

A situation I'm trying to solve. I'm loading an image onto a canvas, then making some edits(resize, crop etc.) to it and saving the resultant image to a folder. Now if the original image is larger than the canvas dimensions, the resultant image is cropped. Hence I'm trying to scale the image to a smaller size that fits in the canvas, apply the effects, and then scale it back to the original dimensions and save it. Is there a way to do this?
My JS pseudo code is somewhat like this:
//load the image using drawimage
//compare image dimensions with canvas dimensions, scale down the image if large
//apply the effects
(I have done till here)
//scale it back to original size
//save
How do I scale it to and fro using canvas scale method? I'm assuming I have to calculate a widths ratio for scaling down and up like so:
scale_down_width = image_orig_width / canvas_width
scale_down_height = image_orig_height / canvas_height
//For scaling down
scale(1*scale_down_width, 1*scale_down_height)
//For scaling up
scale(1/scale_down_width, 1/scale_down_height)
Am I doing this correct?
Scale down works fine, but scale up doesn't, how do I do it right?

Categories