webglregl

Cannot plot points in Z direction using regl


I am starting with regl and I am trying to make a small demo for plotting points in all three axis. I have used this link to start with.

In the example above the points were initialized as

const points = d3.range(numPoints).map(i => ({
                x: //Code,
                y: //Code,
                color: [1, 0, 0],
            }));

I modified it to the one below to get a spiral that goes into infinity

const points = d3.range(numPoints).map(i => ({
                x: 200+radius*Math.cos(i*Math.PI/180),
                y: 200+radius*Math.sin(i*Math.PI/180),
                z: i,
                color: [1, 0, 0],
            }));

I modified the vertex shader to account for the additional axis. The following is the code which draws the points

const drawPoints = regl({
                frag:`
                precision highp float;
                varying vec3 fragColor;
                void main() 
                {
                    gl_FragColor = vec4(fragColor, 1);
                }
                `,

                vert:`
                attribute vec3 position;
                attribute vec3 color;
                varying vec3 fragColor;
                uniform float pointWidth;
                uniform float stageWidth;
                uniform float stageHeight;
                uniform float stageDepth;
                vec3 normalizeCoords(vec3 position) 
                {
                    float x = position[0];
                    float y = position[1];
                    float z = position[2];
                    return vec3(2.0 * ((x / stageWidth) - 0.5),-(2.0 * ((y / stageHeight) - 0.5)),1.0 * ((z / stageDepth) - 0.0));
                }
                void main()
                {
                    gl_PointSize = pointWidth;
                    fragColor = color;
                    gl_Position = vec4(normalizeCoords(position), 1.0);
                }
                `,
                attributes:
                {
                    position: points.map(d => [d.x, d.y, d.z]),
                    color: points.map(d => d.color),
                },
                uniforms:
                {
                    pointWidth: regl.prop('pointWidth'),
                    stageWidth: regl.prop('stageWidth'),
                    stageHeight: regl.prop('stageHeight'),
                    stageDepth: regl.prop('stageDepth'),
                },

                count: points.length,
                depth: 
                {
                    enable: true,
                    mask: true,
                    func: 'less',
                    range: [0, 1]
                },
                primitive: 'points',



            });
frameLoop = regl.frame(() => {
        // clear the buffer
        regl.clear({
            // background color (black)
            color: [0, 0, 0, 1],
            depth: 1,
        });

        drawPoints({
            pointWidth,
            stageWidth: width,
            stageHeight: height,
        });

        if (frameLoop) {
            frameLoop.cancel();
        }
    });

But the result of this is a circle which is plotted on the same plane. The third input to the position doesn't seem to have any effect. I tried interchanging the y and z values in the position and I obtained a sine curve. So the value of z is getting assigned properly. Another thing I noted is that if the value of z is zero, nothing is plotted. Any other value of z doesn't seem to produce any effect.


Solution

  • The reason the added z coordinate has no effect is because you currently have no concept of "depth projection" in your rendering pipeline.

    Typically, you'll need to add a "projection matrix" to your rendering pipeline which will account for the z coordinate in vertices, when mapping those 3D vertex positions to your 2D screen.

    You should be able to add this projection fairly simply by using something like the canvas-orbit-camera module. Once you'd added that module to your project, consider making the following adjustments to your code (see comments tagged with [Add]):

    // Your init code ..
    
    // [Add] Register camera middleware with canvas
    const camera = require('canvas-orbit-camera')(canvas)
    
    // Your init code ..
    
    const drawPoints = regl({
        frag:`
        precision highp float;
        varying vec3 fragColor;
        void main() 
        {
            gl_FragColor = vec4(fragColor, 1);
        }
        `,
    
        vert:`
        attribute vec3 position;
        attribute vec3 color;
        varying vec3 fragColor;
        uniform float pointWidth;
        uniform float stageWidth;
        uniform float stageHeight;
        uniform float stageDepth;
    
        uniform mat4 proj; // [Add] Projection matrix uniform
    
        vec3 normalizeCoords(vec3 position) 
        {
            float x = position[0];
            float y = position[1];
            float z = position[2];
            return vec3(2.0 * ((x / stageWidth) - 0.5),-(2.0 * ((y / stageHeight) - 0.5)),1.0 * ((z / stageDepth) - 0.0));
        }
        void main()
        {
            gl_PointSize = pointWidth;
            fragColor = color;
            gl_Position = proj * vec4(normalizeCoords(position), 1.0); // [Add] Multiply vertex by projection matrix
        }
        `,
        attributes:
        {
            position: points.map(d => [d.x, d.y, d.z]),
            color: points.map(d => d.color),
        },
        uniforms:
        {
            pointWidth: regl.prop('pointWidth'),
            stageWidth: regl.prop('stageWidth'),
            stageHeight: regl.prop('stageHeight'),
            stageDepth: regl.prop('stageDepth'),
    
            // [Add] Projection matrix calculation
            proj: ({viewportWidth, viewportHeight}) =>
                mat4.perspective([],
                    Math.PI / 2,
                    viewportWidth / viewportHeight,
                    0.01,
                    1000),
        },
    
        count: points.length,
        depth: 
        {
            enable: true,
            mask: true,
            func: 'less',
            range: [0, 1]
        },
        primitive: 'points',
    });
    
    frameLoop = regl.frame(() => {
        // clear the buffer
        regl.clear({
            // background color (black)
            color: [0, 0, 0, 1],
            depth: 1,
        });
    
        // [Add] Camera re computation
        camera.tick()
    
        drawPoints({
            pointWidth,
            stageWidth: width,
            stageHeight: height,
        });
    
        if (frameLoop) {
            frameLoop.cancel();
        }
    });
    

    Hope this helps!