Draw a single pixel in WebGL using GL.points

I work through the Tavares WebGL tutorial and get stuck in the mud.

I want to draw single pixels using GL.points . Something is clearly wrong with my array. See In FF or Chrome Canary:

http://codepen.io/anon/pen/EPJVjK

 /** * Creates a program, attaches shaders, links the program. * @param {WebGLShader[]} shaders. The shaders to attach. */ var createGLProgram = function( gl, shaders ) { var program = gl.createProgram(); for ( var i = 0; i < shaders.length; i += 1 ) { gl.attachShader( program, shaders[ i ] ); } gl.linkProgram( program ); // Check the link status var linked = gl.getProgramParameter( program, gl.LINK_STATUS ); if ( !linked ) { // Something went wrong with the link var lastError = gl.getProgramInfoLog( program ); window.console.error( "Error in program linking: " + lastError ); gl.deleteProgram( program ); return null; } return program; }; var myCreateShader = function( gl, shaderScriptText, shaderType ) { // Create the shader object var shader = gl.createShader( shaderType ); // Load the shader source gl.shaderSource( shader, shaderScriptText ); // Compile the shader gl.compileShader( shader ); return shader; }; // Get A WebGL context. var canvas = document.getElementById( "canvas" ); var gl = canvas.getContext( "webgl", { antialias: false } ) var vertexShader = myCreateShader( gl, `attribute vec2 a_position; uniform vec2 u_resolution; void main() { // convert the rectangle from pixels to 0.0 to 1.0 vec2 zeroToOne = a_position / u_resolution; // convert from 0 -> 1 to 0 -> 2 vec2 zeroToTwo = zeroToOne * 2.0; // convert from 0 -> 2 to -1 -> +1 (clipspace) vec2 clipSpace = zeroToTwo - 1.0; // Flip 0,0 from bottom left to conventional 2D top left. gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); }`, gl.VERTEX_SHADER ); var fragmentShader = myCreateShader( gl, `precision mediump float; uniform vec4 u_color; void main() { gl_FragColor = u_color; }`, gl.FRAGMENT_SHADER ); var program = createGLProgram( gl, [ vertexShader, fragmentShader ] ); gl.useProgram( program ); // Store color location. var colorLocation = gl.getUniformLocation( program, "u_color" ); // Look up where the vertex data needs to go. var positionLocation = gl.getAttribLocation( program, "a_position" ); // Set the resolution. var resolutionLocation = gl.getUniformLocation( program, "u_resolution"); gl.uniform2f( resolutionLocation, canvas.width, canvas.height); // Create a buffer. var buffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); gl.enableVertexAttribArray( positionLocation ); // Send the vertex data to the shader program. gl.vertexAttribPointer( positionLocation, 2, gl.FLOAT, false, 0, 0 ); // Set color to black. gl.uniform4f( colorLocation, 0, 0, 0, 1); function drawOneBlackPixel( gl, x, y ) { // Fills the buffer with a single point? gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ x, y, 0, y, x, 0, x, 0, 0, y, 0, 0]), gl.STATIC_DRAW ); // Draw one point. gl.drawArrays( gl.POINTS, 0, 1 ); } // These tests are supposed to be x,y coordinates from top left. drawOneBlackPixel( gl, 0, 0 ); drawOneBlackPixel( gl, 1, 1 ); drawOneBlackPixel( gl, 2, 2 ); drawOneBlackPixel( gl, 3, 3 ); drawOneBlackPixel( gl, 4, 4 ); drawOneBlackPixel( gl, 5, 5 ); drawOneBlackPixel( gl, 6, 6 ); drawOneBlackPixel( gl, 7, 7 ); drawOneBlackPixel( gl, 10, 5 ); drawOneBlackPixel( gl, 15, 5 ); drawOneBlackPixel( gl, 20, 5 ); drawOneBlackPixel( gl, 12, 34 ); drawOneBlackPixel( gl, 42, 42 ); 

The problem is that the pixels are not displayed in place. (0,0) is not displayed in the upper left corner. And what should be a diagonal line of pixels is instead "stairstepped" in two-pixel pieces. A working codepen would be ideal.

As a second “bonus” question, I will want to be able to batch process multiple pixels on a single drawArrays call called on requestAnimationFrame . Advice on how best to do this would be greatly appreciated.

PS I read:

How to set pixel color on canvas using WebGL?

but my mistake seems a little lower than this. And please do not advise me to use canvas2D. I need webGL performance and I try to learn from scratch :)

+6
source share
1 answer

A few things:

First you need to add gl_PointSize = 1.0; to your vertex shader to tell webgl the size of your gl.POINT .

Secondly, the coordinate you are passing in is the center of each pixel, not the top left corner of each pixel, so

 function drawOneBlackPixel( gl, x, y ) { // Fills the buffer with a single point? gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ x+0.5, y+0.5]), gl.STATIC_DRAW ); // Draw one point. gl.drawArrays( gl.POINTS, 0, 1 ); } 

- Is this what you want.

The working code is here: http://codepen.io/anon/pen/pgBjBy .

Bonus question: what you need to do is manual memory management gl.buffer (gasp) so that you can do gl.drawArrays( gl.POINTS, 0, x ); to draw x points. For example, if you want to draw 3 points in (0,0) (1,1), (2,2), then you need to do gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.5, 0.5, 1.5, 1.5, 2.5, 2.5], gl.STATIC_DRAW) , and then gl.drawArrays(gl.POINTS, 0, 3);

For example, you can first allocate a Float32Array with a number of 4 points, and whenever you call drawOneBlackPixel , first set Float32Array to [p1x, p1y, 0,0,0,0,0,0] and on the next drawOneBlackPixel it set the array in [p1x, p1y, p2x, p2y, 0,0,0,0] and so on. Of course, you need to handle other things, such as zooming in and copying the Float32Array as needed, and loading the Float32Array onto the GPU as you make changes. The way you handle the erasure of dots also needs to be remembered.

+7
source

All Articles