开发者

webgl readpixels is always returning 0,0,0,0

I am trying to do picking in WebGl. I have two shapes rendered along with different texture mapped on each. I am trying to grab pixel on certain co-ordinates. Here is the example.

var pixelValues = new Uint8Array(4);
gl.readPixels(10, 35, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixelValues);
console.log(pixelValues);

But pixelValues always contain [0,0,0,0].开发者_如何学Go What am I doing wrong? Do I need to do something related to framebuffer?


You don't need preserveDrawingBuffer: true to call readPixels. What you need is to call readPixels before exiting the current event.

The spec says if you call any function that affects the canvas (gl.clear, gl.drawXXX) then the browser will clear the canvas after the next composite operation. When that composite operation happens is up to the browser. It could be after it processes several mouse events or keyboard events or click events. The order is undefined. What is defined is that it won't do it until the current event exits so

render
read

const gl = document.querySelector("canvas").getContext("webgl");

render();
read();  // read in same event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
<canvas></canvas>

works where as

render
setTimeout(read, 1000);  // some other event

does not work

const gl = document.querySelector("canvas").getContext("webgl");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
<canvas></canvas>

Note that since it's the composite operation (the browser actually drawing the canvas on the page with the rest of the HTML) that triggers the clear, if the canvas is not on the page then it's not composited and won't be cleared.

In other words the case that didn't work above does work here

// create an offscreen canvas. Because it's offscreen it won't be composited
// and therefore will not be cleared.
const gl = document.createElement("canvas").getContext("webgl");

render();
setTimeout(read, 1000);  // read in other event

function render() {
  gl.clearColor(.25, .5, .75, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

function read() {
  const pixel = new Uint8Array(4);
  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
  log(pixel);
}

function log(...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}

Now, if you want to call readPixels in some other event, like when the user clicks an element, then you have at least 2 options

  1. Set preserveDrawingBuffer: true

  2. Render again in your event

    screenshotElement.addEventListener('click', event => {
      render();  
      gl.readPixels(...);
    });
    


According to WebGL specs, you need to call getContext setting the preserveDrawingBuffer flag, like:

var ctx = canvas.getContext("webgl", {preserveDrawingBuffer: true});

if you plan to read the pixels after exiting the event where the GL context is rendered. This prevents the drawing buffer (color, depth, stencil) from being cleared after they are draw to screen. Keep in mind that settings this may cause a performance penalty.

Alternatively, you can read the pixels before they are presented, which should also work.


I have two shapes rendered along with different texture mapped on each.

gl.readPixels(10, 35, 1, 1, ...);

I'm sure you thought gl.readPixels starts reading pixels from the top left corner, but it doesn't. gl.readPixels starts reading them from the lower left corner.

webgl readpixels is always returning 0,0,0,0

From the WebGLRenderingContext.readPixels() documentation:

Parameters

x

A GLint specifying the first horizontal pixel that is read from the lower left corner of a rectangular block of pixels.

y

A GLint specifying the first vertical pixel that is read from the lower left corner of a rectangular block of pixels.

webgl readpixels is always returning 0,0,0,0

<!DOCTYPE html>

<body>

    <head>
        <title>Pick object by click. WebGL, JavaScript</title>

        <script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/gl-matrix-min.js"></script>

        <style>
            #renderCanvas {
                position: absolute;
            }

            #outputEN {
                position: absolute;
                top: 210px;
                left: 20px;
            }

            #outputRU {
                position: absolute;
                top: 235px;
                left: 20px;
            }

            #outputCH {
                position: absolute;
                top: 260px;
                left: 20px;
            }

            #outputPinyin {
                position: absolute;
                top: 285px;
                left: 20px;
            }
        </style>
    </head>

    <body>
        <div>
            <canvas id="renderCanvas" width="300" height="300"></canvas>
            <span id="outputEN">Click on any object or outside</span>
            <span id="outputRU">Кликните на любой объект или мимо</span>
            <span id="outputCH">单击任何对象或外部</span>
            <span id="outputPinyin">Dān jí rènhé duìxiàng huò wàibù</span>
        </div>

        <script id="vertexShader" type="x-shader/x-vertex">
            attribute vec2 aPosition;
            uniform mat4 uMvpMatrix;

            void main() {
                gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
            }
        </script>

        <script id="fragmentShader" type="x-shader/x-fragment">
            precision mediump float;
            uniform vec3 uColor;
            uniform bool uClick;
            uniform vec3 uPickColor;

            void main() {
                if (!uClick) {
                    gl_FragColor = vec4(uColor, 1.0);
                } else {
                    gl_FragColor = vec4(uPickColor, 1.0);
                }
            }
        </script>

        <script>
            const gl = document.getElementById("renderCanvas").getContext("webgl");

            const outputEN = document.getElementById("outputEN");
            const outputRU = document.getElementById("outputRU");

            const vShader = gl.createShader(gl.VERTEX_SHADER);
            const vSrc = document.getElementById("vertexShader").firstChild.textContent;
            gl.shaderSource(vShader, vSrc);
            gl.compileShader(vShader);
            let ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
            if (!ok) {
                console.log("vert: " + gl.getShaderInfoLog(vShader));
            };

            const fShader = gl.createShader(gl.FRAGMENT_SHADER);
            const fSrc = document.getElementById("fragmentShader").firstChild.textContent;
            gl.shaderSource(fShader, fSrc);
            gl.compileShader(fShader);
            ok = gl.getShaderParameter(fShader, gl.COMPILE_STATUS);
            if (!ok) {
                console.log("frag: " + gl.getShaderInfoLog(fShader));
            };

            const program = gl.createProgram();
            gl.attachShader(program, vShader);
            gl.attachShader(program, fShader);
            gl.linkProgram(program);
            ok = gl.getProgramParameter(program, gl.LINK_STATUS);
            if (!ok) {
                console.log("link: " + gl.getProgramInfoLog(program));
            };
            gl.useProgram(program);

            const vertPositions = [
                -0.5, -0.5,
                -0.5, 0.5,
                0.5, -0.5,
                0.5, 0.5
            ];
            const vertPosBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertPositions), gl.STATIC_DRAW);
            const aPositionLocation = gl.getAttribLocation(program, "aPosition");
            gl.vertexAttribPointer(aPositionLocation, 2, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(aPositionLocation);

            const modelMatrix = glMatrix.mat4.create();
            const mvpMatrix = glMatrix.mat4.create();

            const projMatrix = glMatrix.mat4.create();
            glMatrix.mat4.ortho(projMatrix, -0.5, 2.5, 2.5, -0.5, 10, -10);

            const viewMatrix = glMatrix.mat4.create();
            glMatrix.mat4.lookAt(viewMatrix, [0, 0, 10], [0, 0, 0], [0, 1, 0]);

            const projViewMatrix = glMatrix.mat4.create();
            glMatrix.mat4.mul(projViewMatrix, projMatrix, viewMatrix);

            const uMvpMatrixLocation = gl.getUniformLocation(program, "uMvpMatrix");
            const uColorLocation = gl.getUniformLocation(program, "uColor");
            const uClickLocation = gl.getUniformLocation(program, "uClick");
            const uPickColorLocation = gl.getUniformLocation(program, "uPickColor");

            gl.uniform1i(uClickLocation, 0);

            const firstObj = {
                pos: glMatrix.vec3.fromValues(0, 0, 0),
                scale: glMatrix.vec3.fromValues(0.7, 0.7, 1),
                color: glMatrix.vec3.fromValues(0.50, 0.84, 0.22)
            };

            const secondObj = {
                pos: glMatrix.vec3.fromValues(1, 0, 0),
                scale: glMatrix.vec3.fromValues(0.7, 0.7, 1),
                color: glMatrix.vec3.fromValues(0.07, 0.59, 0.09)
            };

            const thirdObj = {
                pos: glMatrix.vec3.fromValues(2, 0, 0),
                scale: glMatrix.vec3.fromValues(0.7, 0.7, 1),
                color: glMatrix.vec3.fromValues(0.12, 0.88, 0.48)
            };

            const fourthObj = {
                pos: glMatrix.vec3.fromValues(0, 1, 0),
                scale: glMatrix.vec3.fromValues(0.7, 0.7, 1),
                color: glMatrix.vec3.fromValues(0.65, 0.37, 0.07)
            };

            const pickColors = {
                first: glMatrix.vec3.fromValues(255, 0, 0),
                second: glMatrix.vec3.fromValues(0, 255, 0),
                third: glMatrix.vec3.fromValues(0, 0, 255),
                fourth: glMatrix.vec3.fromValues(255, 255, 0)
            };

            gl.canvas.onmousedown = (e) => {
                // Get coordinates of mouse pick
                const rect = gl.canvas.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;
                const pickX = mouseX;
                const pickY = rect.bottom - rect.top - mouseY - 1;
                // console.log("mouse pick coords:", pickX, pickY);

                // Set the click flag and color id
                gl.uniform1i(uClickLocation, 1);
                gl.clearColor(0, 0, 0, 1);
                gl.clear(gl.COLOR_BUFFER_BIT);

                // Draw objects for picking
                gl.uniform3fv(uPickColorLocation, pickColors.first);
                glMatrix.mat4.fromTranslation(modelMatrix, firstObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, firstObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                gl.uniform3fv(uPickColorLocation, pickColors.second);
                glMatrix.mat4.fromTranslation(modelMatrix, secondObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, secondObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                gl.uniform3fv(uPickColorLocation, pickColors.third);
                glMatrix.mat4.fromTranslation(modelMatrix, thirdObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, thirdObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                gl.uniform3fv(uPickColorLocation, pickColors.fourth);
                glMatrix.mat4.fromTranslation(modelMatrix, fourthObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, fourthObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                const pixels = new Uint8Array(4);
                gl.readPixels(pickX, pickY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
                // console.log("pick color:", pixels[0], pixels[1], pixels[2], pixels[3]);
                const pickResult = glMatrix.vec3.fromValues(pixels[0], pixels[1],
                    pixels[2]);

                let messageEN = "";
                let messageRU = "";
                let messageCH = "";
                let messagePinyin = "";
                if (glMatrix.vec3.exactEquals(pickResult, pickColors.first)) {
                    messageEN = "First object";
                    messageRU = "Первый объект";
                    messageCH = "第一个对象";
                    messagePinyin = "Dì yī gè duìxiàng";
                } else if (glMatrix.vec3.exactEquals(pickResult, pickColors.second)) {
                    messageEN = "Second object";
                    messageRU = "Второй объект";
                    messageCH = "第二个对象";
                    messagePinyin = "Dì èr gè duìxiàng";
                } else if (glMatrix.vec3.exactEquals(pickResult, pickColors.third)) {
                    messageEN = "Third object";
                    messageRU = "Третий объект";
                    messageCH = "第三个对象";
                    messagePinyin = "Dì sān gè duìxiàng";
                } else if (glMatrix.vec3.exactEquals(pickResult, pickColors.fourth)) {
                    messageEN = "Fourth object";
                    messageRU = "Четвёртый объект";
                    messageCH = "第四个对象";
                    messagePinyin = "Dì sì gè duìxiàng";
                } else {
                    messageEN = "You didn't click on the objects";
                    messageRU = "Вы не кликнули по объектам";
                    messageCH = "你没有点击对象";
                    messagePinyin = "Nǐ méiyǒu diǎnjī duìxiàng";
                }
                console.log(messageEN);
                outputEN.innerText = messageEN;
                outputRU.innerText = messageRU;
                outputCH.innerText = messageCH;
                outputPinyin.innerText = messagePinyin;

                gl.uniform1i(uClickLocation, 0);
                draw();
            };

            function draw() {
                gl.clearColor(0.9, 0.9, 0.95, 1);
                gl.clear(gl.COLOR_BUFFER_BIT);

                glMatrix.mat4.fromTranslation(modelMatrix, firstObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, firstObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.uniform3fv(uColorLocation, firstObj.color);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                glMatrix.mat4.fromTranslation(modelMatrix, secondObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, secondObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.uniform3fv(uColorLocation, secondObj.color);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                glMatrix.mat4.fromTranslation(modelMatrix, thirdObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, thirdObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.uniform3fv(uColorLocation, thirdObj.color);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

                glMatrix.mat4.fromTranslation(modelMatrix, fourthObj.pos);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, fourthObj.scale);
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                gl.uniform3fv(uColorLocation, fourthObj.color);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
            }

            draw();
        </script>
    </body>

</body>

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜