使用GL.points在WebGL中绘制单个像素

时间:2016-02-16 22:24:24

标签: javascript webgl

我正在研究Tavares WebGL tutorial,并陷入困境。

我想使用GL.points绘制单个像素。我的阵列显然有些问题。请参阅FF或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 );

问题是像素不能很好地渲染。 (0,0)不会在左上角绘画。应该是对角线的像素是两个像素块中的“阶梯式”。一个有效的Codepen将是理想的。

作为第二个“奖励”问题,我接下来希望能够在drawArrays调用的requestAnimationFrame调用中批量处理多个像素。关于如何做到这一点的建议将非常感激。

P.S。我读过:

How can I set the color of a pixel on a canvas using WebGL?

但我的错误似乎比这更低级。请不要建议我使用canvas2D。我想要webGL的表现,我想从头开始学习:)

1 个答案:

答案 0 :(得分:7)

有几件事:

首先,您需要将gl_PointSize = 1.0;添加到顶点着色器,以告知webgl gl.POINT的大小。

其次,您传入的坐标是每个像素的中心,而不是每个像素的左上角,因此,

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 );
}

是你想要的。

在此工作Codepen:http://codepen.io/anon/pen/pgBjBy

奖金问题:你需要做的是gl.buffer(喘气)的手动记忆管理,这样你就可以gl.drawArrays( gl.POINTS, 0, x );绘制x点。例如,如果要在(0,0)(1,1),(2,2)处绘制3个点,则需要执行gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.5, 0.5, 1.5, 1.5, 2.5, 2.5], gl.STATIC_DRAW)然后gl.drawArrays(gl.POINTS, 0, 3);

例如,您可以先为空格分配一个Float32Array,然后在调用drawOneBlackPixel时,首先将Float32Array设置为[p1x,p1y,0,0,0,0,0,0]在下一个drawOneBlackPixel,它会将数组设置为[p1x,p1y,p2x,p2y,0,0,0,0],依此类推。当然,您必须处理其他内容,例如根据需要增长和复制Float32Array,并在进行更改时将Float32Array上传到GPU。如何处理&#34;擦除&#34;积分也是值得记住的。