Search code examples
javascriptwebbufferwebglpoint-clouds

WebGL: Failing at drawing points: glDrawArrays: "attempt to access out of range vertices in attribute 1"


I am trying to display points (a pointcloud actually) using WebGL. The past 2 days I spent going through this tutorial and still have a lot of my code based on it.

I am attempting to load a .ply file and then show each coordinate as a coloured point, but get stuck at this error when trying to draw it:

[.WebGLRenderingContext-0x111ff17bc300]GL ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 1

I have of course done some research, but the general answer was that the length of the buffered arrays would be different, which I have made sure is not the case. It's mostly likely a similar noobish mistake though:

function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }
        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }
        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }
        gl.shaderSource(shader, str);
        gl.compileShader(shader);
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }
        return shader;
    }
    var shaderProgram;
    function initShaders() {
        var fragmentShader = getShader(gl, "shader-fs");
        var vertexShader = getShader(gl, "shader-vs");

        shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(shaderProgram);

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
        gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    }

    var mvMatrix = mat4.create();
    var mvMatrixStack = [];
    var pMatrix = mat4.create();

    function mvPushMatrix() {
        var copy = mat4.create();
        mat4.set(mvMatrix, copy);
        mvMatrixStack.push(copy);
    }

    function mvPopMatrix() {
        if (mvMatrixStack.length == 0) {
            throw "Invalid popMatrix!";
        }
        mvMatrix = mvMatrixStack.pop();
    }

    function setMatrixUniforms() {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
    }

    function degToRad(degrees) {
        return degrees * Math.PI / 180;
    }


    var pointcloudVertexPositionBuffer;
    var pointcloudColourBuffer;
    var pointcloudIndexBuffer;
    function handlePoints(pointCoords, pointColours, indices) {
        pointcloudVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudVertexPositionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointCoords), gl.STATIC_DRAW);
        pointcloudVertexPositionBuffer.itemSize = 3;
        pointcloudVertexPositionBuffer.numItems = pointCoords.length / 3;

        pointcloudColourBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudColourBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointColours), gl.STATIC_DRAW);
        pointcloudColourBuffer.itemSize = 3;
        pointcloudColourBuffer.numItems = pointColours.length / 3;

        pointcloudIndexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, pointcloudIndexBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
        pointcloudIndexBuffer.itemSize = 1;
        pointcloudIndexBuffer.numItems = indices.length;
    }

    var coordList = [];
    var colList = [];
    var indexList = [];
    function loadPLY(){
        jQuery.get('some_ascii.ply', function(data) {
            var lines = data.split('\n');
            var coords = false;

            for(var line = 0; line < lines.length; line++){
                if (coords) {
                    var splitString = lines[line].split(" ");
                    coordList.push(parseFloat(splitString[0]), parseFloat(splitString[1]), parseFloat(splitString[2]));
                    colList.push(parseFloat(splitString[3]), parseFloat(splitString[4]), parseFloat(splitString[5]));
                    indexList.push(parseFloat(line));
                }
                else{
                    if (lines[line] == "end_header"){
                        console.log("end header");
                        coords = true;
                    }
                }
            }
            handlePoints(coordList, colList, indexList);
        });
    }

    function drawScene(){
        gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

        mat4.identity(mvMatrix);

        //mat4.translate(mvMatrix, [0, 0, -6]);

        //gl.uniform1i(shaderProgram.samplerUniform, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudColourBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pointcloudColourBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, pointcloudVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

        setMatrixUniforms();
        //gl.drawElements(gl.TRIANGLES, pointcloudIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
        //console.log("1", pointcloudIndexBuffer.numItems);
        //console.log("2", pointcloudColourBuffer.numItems);
        //console.log("3", pointcloudVertexPositionBuffer.numItems);
        gl.drawArrays(gl.POINTS, 0, pointcloudIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
    }


    function tick() {
        requestAnimFrame(tick);
        drawScene();
    }

    function webGLStart(){
        var canvas = document.getElementById("canvas");
        initGL(canvas);
        initShaders();
        loadPLY();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);

        tick();
    }

Solution

  • I looked through your code again and I noticed a couple things:

    If you are drawing gl.POINT you need to include gl_PointSize = 8.0; in your vertex shader, else the points wouldnt be drawn.

    Your color data is incorrect. Negative colors does not make sense.

    Your drawArrays call is incorrect, should be gl.drawArrays(gl.POINTS, 0, pointcloudIndexBuffer.numItems);

    I disabled perspective because it is hard to tell without a camera to know if the things are being drawn correctly, but out of view.

    The Cannot read property 'itemSize' of undefined you mentioned is because it is attempting to draw the scene before your .ply data is parsed. To fix, only start drawing when parsing is complete.

    So here is my edited version which, at least, draws (you have to change the src):

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <input type="file" name="file" id="file">
    <script type="text/javascript" src="aaa/glMatrix-0.9.5.min.js"></script>
    <script type="text/javascript" src="aaa/webgl-utils.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    
    <script id="shader-fs" type="x-shader/x-fragment">
        precision mediump float;
    
        varying vec4 vColor;
    
        void main(void) {
            //gl_FragColor = vColor;
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    </script>
    
    <script id="shader-vs" type="x-shader/x-vertex">
        attribute vec3 aVertexPosition;
        attribute vec4 aVertexColor;
    
        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;
    
        varying vec4 vColor;
    
        void main(void) {
            gl_PointSize = 8.0;
            //gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
            gl_Position = vec4(aVertexPosition, 1.0);
            vColor = aVertexColor;
        }
    
    </script>
    
    
    <script type="text/javascript">
        var gl;
        function initGL(canvas) {
            try {
                gl = canvas.getContext("experimental-webgl");
                gl.viewportWidth = canvas.width;
                gl.viewportHeight = canvas.height;
            } catch (e) {
            }
            if (!gl) {
                alert("Could not initialise WebGL, sorry :-(");
            }
        }
        function getShader(gl, id) {
            var shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }
            var str = "";
            var k = shaderScript.firstChild;
            while (k) {
                if (k.nodeType == 3) {
                    str += k.textContent;
                }
                k = k.nextSibling;
            }
            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                return null;
            }
            gl.shaderSource(shader, str);
            gl.compileShader(shader);
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
            }
            return shader;
        }
        var shaderProgram;
        function initShaders() {
            var fragmentShader = getShader(gl, "shader-fs");
            var vertexShader = getShader(gl, "shader-vs");
    
            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);
    
            if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                alert("Could not initialise shaders");
            }
    
            gl.useProgram(shaderProgram);
    
            shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
            gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
    
            shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
            gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);
    
            shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
            shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    
            console.log(shaderProgram);
        }
    
        var mvMatrix = mat4.create();
        var mvMatrixStack = [];
        var pMatrix = mat4.create();
    
        function mvPushMatrix() {
            var copy = mat4.create();
            mat4.set(mvMatrix, copy);
            mvMatrixStack.push(copy);
        }
    
        function mvPopMatrix() {
            if (mvMatrixStack.length == 0) {
                throw "Invalid popMatrix!";
            }
            mvMatrix = mvMatrixStack.pop();
        }
    
        function setMatrixUniforms() {
            gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
            gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        }
    
        function degToRad(degrees) {
            return degrees * Math.PI / 180;
        }
    
    
        var pointcloudVertexPositionBuffer;
        var pointcloudColourBuffer;
        var pointcloudIndexBuffer;
        function handlePoints(pointCoords, pointColours, indices) {
            pointcloudVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudVertexPositionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointCoords), gl.STATIC_DRAW);
            pointcloudVertexPositionBuffer.itemSize = 3;
            pointcloudVertexPositionBuffer.numItems = pointCoords.length / 3;
    
            pointcloudColourBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudColourBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pointColours), gl.STATIC_DRAW);
            pointcloudColourBuffer.itemSize = 3;
            pointcloudColourBuffer.numItems = pointColours.length / 3;
    
            pointcloudIndexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, pointcloudIndexBuffer);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
            pointcloudIndexBuffer.itemSize = 1;
            pointcloudIndexBuffer.numItems = indices.length;
        }
    
        var coordList = [];
        var colList = [];
        var indexList = [];
        function loadPLY(){
            jQuery.get('aaa/untitled.ply', function(data) {
                var lines = data.split('\n');
                var coords = false;
    
                for(var line = 0; line < lines.length; line++){
                    if (coords) {
                        var splitString = lines[line].split(" ");
                        if (splitString.length === 6) {
                            coordList.push(parseFloat(splitString[0]), parseFloat(splitString[1]), parseFloat(splitString[2]));
                            colList.push(parseFloat(splitString[3]), parseFloat(splitString[4]), parseFloat(splitString[5]));
                            indexList.push(parseFloat(line));
                        }
                        else{
                            coords = false;
                        }
                    }
                    else{
                        if (lines[line].indexOf("end_header")===0){
                            console.log("end header");
                            coords = true;
                        }
                    }
                }
                handlePoints(coordList, colList, indexList);
                tick();
            });
    
        }
    
        function drawScene(){
            gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
            mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
    
            mat4.identity(mvMatrix);
    
            //mat4.translate(mvMatrix, [0, 0, -6]);
    
            //gl.uniform1i(shaderProgram.samplerUniform, 0);
    
            gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudColourBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, pointcloudColourBuffer.itemSize, gl.FLOAT, false, 0, 0);
    
            gl.bindBuffer(gl.ARRAY_BUFFER, pointcloudVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pointcloudVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    
            setMatrixUniforms();
            //gl.drawElements(gl.TRIANGLES, pointcloudIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
            //console.log("1", pointcloudIndexBuffer.numItems);
            //console.log("2", pointcloudColourBuffer.numItems);
            //console.log("3", pointcloudVertexPositionBuffer.numItems);
            gl.drawArrays(gl.POINTS, 0, pointcloudIndexBuffer.numItems);
        }
    
    
        function tick() {
            requestAnimFrame(tick);
            drawScene();
        }
    
        function webGLStart(){
            var canvas = document.getElementById("canvas");
            initGL(canvas);
            initShaders();
            loadPLY();
    
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.enable(gl.DEPTH_TEST);
    
        }
    
    
    </script>
    </head>
    
    <style type="text/css">
        #loadingtext {
            position:absolute;
            top:250px;
            left:150px;
            font-size:2em;
            color: white;
        }
    </style>
    
    <body onload="webGLStart();">
    <canvas id="canvas" style="border: none;" width="500" height="500"></canvas>
    </body>
    
    </html>
    

    enter image description here