engine icon indicating copy to clipboard operation
engine copied to clipboard

Analogue of QuadRender

Open AlexAPPi opened this issue 3 months ago • 5 comments

The engine is clearly missing a public analogue of QuadRender that would allow rendering not only a single quad, but also arbitrary primitives — points, lines, triangles, and so on. This would provide flexibility for drawing non-standard geometries without the need to manually create and manage custom buffers.

https://github.com/playcanvas/engine/blob/e48af3dd66f0a3e30f377eb386a391975bb250b4/src/scene/graphics/quad-render.js#L152

AlexAPPi avatar Oct 14 '25 18:10 AlexAPPi

@mvaligursky

Example of bypass:

const primitive = {
    type: pc.PRIMITIVE_POINTS,
    base: 0,
    baseVertex: 0,
    count: 0,
    indexed: false
}

export class PointsRenderPass extends pc.RenderPass {

    count = 0;
    shader: pcx.Shader;
    vertexBuffer: pcx.VertexBuffer;

    cullMode = pc.CULLFACE_NONE;
    blendState = pc.BlendState.NOBLEND;
    depthState = pc.DepthState.NODEPTH;
    stencilFront = null;
    stencilBack = null;

    quadRender: pcx.QuadRender;

    constructor(shader: pcx.Shader) {
        super(shader.device);
        this.quadRender = new pc.QuadRender(shader);
    }

    destroy(): void {
        super.destroy();
        this.quadRender.destroy();
    }

    execute() {

        primitive.count = this.count;

        // render state
        const device = this.device as pcx.WebgpuGraphicsDevice;
        device.setBlendState(this.blendState);
        device.setCullMode(this.cullMode);
        device.setDepthState(this.depthState);
        device.setStencilState(this.stencilFront!, this.stencilBack!);

        device.setVertexBuffer(this.vertexBuffer);
        device.setShader(this.quadRender.shader);

        if (device.supportsUniformBuffers) {

            // not using view bind group
            device.setBindGroup(pc.BINDGROUP_VIEW, device.emptyBindGroup);

            // mesh bind group
            const bindGroup = this.quadRender.bindGroup;
            bindGroup.update();
            device.setBindGroup(pc.BINDGROUP_MESH, bindGroup);

            // dynamic uniform buffer bind group
            const uniformBuffer = this.quadRender.uniformBuffer;
            if (uniformBuffer) {
                uniformBuffer.update(_dynamicBindGroup);
                device.setBindGroup(pc.BINDGROUP_MESH_UB, _dynamicBindGroup.bindGroup, _dynamicBindGroup.offsets);
            } else {
                device.setBindGroup(pc.BINDGROUP_MESH_UB, device.emptyBindGroup);
            }
        }

        // @ts-ignore
        device.draw(primitive);
    }
}

AlexAPPi avatar Oct 21 '25 13:10 AlexAPPi

Do you have any ideas on how to perform point rendering directly into a texture on the GPU side, with the ability to ping-pong data between multiple textures for iterative computations?

const vertexCode =
`
    attribute uvec2 aPointPos;

    uniform highp usampler2D uInTexture;

    highp vec2 getPosition(out uvec4 data) {

        vec2 sz = vec2(textureSize(uInTexture, 0));
        vec2 uv = (vec2(aPointPos) + 0.5) / sz;
        highp vec2 xy = uv * 2.0 - 1.0;

        data = texelFetch(uInTexture, ivec2(aPointPos), 0);

        return xy;
    }

    flat varying uvec4 vData;

    void main(void) {

        uvec4 inData;

        highp vec2 pixelCoord = getPosition(inData);
        highp vec4 position = vec4(pixelCoord, 0.0, 1.0);

        vData = inData;

        // some changes
        vData.z = 123u;

        gl_PointSize = 1.0;
        gl_Position = position;
    }
`;

const fragmentCodePS = 
`
    flat varying uvec4 vData;

    void main(void) {
        pcFragColor0 = vData;
    }
`;

AlexAPPi avatar Oct 21 '25 13:10 AlexAPPi

Do you have any ideas on how to perform point rendering directly into a texture on the GPU side, with the ability to ping-pong data between multiple textures for iterative computations?

why not render a quad to that texture and update data per fragment in fragment shader?

mvaligursky avatar Oct 21 '25 15:10 mvaligursky

Do you have any ideas on how to perform point rendering directly into a texture on the GPU side, with the ability to ping-pong data between multiple textures for iterative computations?

why not render a quad to that texture and update data per fragment in fragment shader?

The pixels that need to be updated can be located in completely different areas of the source texture, and the related data resides in a third texture. The update process must occur entirely on the GPU, without CPU involvement, which means a mechanism similar to a compute shader is required. This mechanism would take as input a list of pixel coordinates and output an updated texture. In other words, it’s a GPU-based selective pixel update procedure that modifies specified pixels only, using a list of indices as input and writing equivalent fragment-shader-based FBO approach.

AlexAPPi avatar Oct 21 '25 18:10 AlexAPPi

The engine is clearly missing a public analogue of QuadRender that would allow rendering not only a single quad, but also arbitrary primitives — points, lines, triangles, and so on.

I think MeshInstance is exactly that.

mvaligursky avatar Nov 26 '25 12:11 mvaligursky