Skip to content

Mesh instancing supportΒ #15

@MartinSStewart

Description

@MartinSStewart

Support for instancing, aka being able to draw the same mesh many times with different uniforms, would be very useful for my project.

Here's an example of my current work around to be able to efficiently draw many instances of a single mesh (I've excluded some details but this is the general idea).

type alias ChipTileVertex =
    { position : Vec3
    , instanceIndex : Float
    }

type alias ChipTileUniforms =
    { perspective : Mat4
    , camera : Mat4
    , time : Second
    , instanceOffset0 : Vec3
    , instanceOffset1 : Vec3
    , instanceOffset2 : Vec3
    , instanceOffset3 : Vec3
    , instanceOffset4 : Vec3
    , instanceOffset5 : Vec3
    , instanceOffset6 : Vec3
    , instanceOffset7 : Vec3
    , instanceOffset8 : Vec3
    , instanceOffset9 : Vec3
    , instanceOffset10 : Vec3
    , instanceOffset11 : Vec3
    , instanceOffset12 : Vec3
    , instanceOffset13 : Vec3
    , instanceOffset14 : Vec3
    , instanceOffset15 : Vec3
    , instanceOffset16 : Vec3
    , instanceOffset17 : Vec3
    , instanceOffset18 : Vec3
    , instanceOffset19 : Vec3
    }

chipTileUniforms : InGameModel -> Size Int -> Array Point2d -> ChipTileUniforms
chipTileUniforms inGameModel windowSize particles =
    let
        getOffset index =
            Array.get index particles
                |> Maybe.map
                    (\position ->
                        Vec3.vec3 (Point2d.xCoordinate position) (Point2d.yCoordinate position) 0
                    )
                |> Maybe.withDefault (Vec3.vec3 -999999 -999999 0)
    in
    { perspective = perspective windowSize
    , camera = cameraMatrix inGameModel
    , time = inGameModel.inGameTime
    , instanceOffset0 = getOffset 0
    , instanceOffset1 = getOffset 1
    , instanceOffset2 = getOffset 2
    , instanceOffset3 = getOffset 3
    , instanceOffset4 = getOffset 4
    , instanceOffset5 = getOffset 5
    , instanceOffset6 = getOffset 6
    , instanceOffset7 = getOffset 7
    , instanceOffset8 = getOffset 8
    , instanceOffset9 = getOffset 9
    , instanceOffset10 = getOffset 10
    , instanceOffset11 = getOffset 11
    , instanceOffset12 = getOffset 12
    , instanceOffset13 = getOffset 13
    , instanceOffset14 = getOffset 14
    , instanceOffset15 = getOffset 15
    , instanceOffset16 = getOffset 16
    , instanceOffset17 = getOffset 17
    , instanceOffset18 = getOffset 18
    , instanceOffset19 = getOffset 19
    }

chipTileVS : Shader ChipTileVertex ChipTileUniforms { vcolor : Vec4 }
chipTileVS =
    [glsl|
        attribute vec3 position;
        attribute float instanceIndex;
        uniform mat4 perspective;
        uniform mat4 camera;
        uniform vec3 instanceOffset0;
        uniform vec3 instanceOffset1;
        uniform vec3 instanceOffset2;
        uniform vec3 instanceOffset3;
        uniform vec3 instanceOffset4;
        uniform vec3 instanceOffset5;
        uniform vec3 instanceOffset6;
        uniform vec3 instanceOffset7;
        uniform vec3 instanceOffset8;
        uniform vec3 instanceOffset9;
        uniform vec3 instanceOffset10;
        uniform vec3 instanceOffset11;
        uniform vec3 instanceOffset12;
        uniform vec3 instanceOffset13;
        uniform vec3 instanceOffset14;
        uniform vec3 instanceOffset15;
        uniform vec3 instanceOffset16;
        uniform vec3 instanceOffset17;
        uniform vec3 instanceOffset18;
        uniform vec3 instanceOffset19;
        uniform float time;
        varying vec4 vcolor;

        void main () {
            mediump int index = int(instanceIndex);
            mediump vec3 offset =
                (instanceOffset0 * float(index == 0)) +
                (instanceOffset1 * float(index == 1)) +
                (instanceOffset2 * float(index == 2)) +
                (instanceOffset3 * float(index == 3)) +
                (instanceOffset4 * float(index == 4)) +
                (instanceOffset5 * float(index == 5)) +
                (instanceOffset6 * float(index == 6)) +
                (instanceOffset7 * float(index == 7)) +
                (instanceOffset8 * float(index == 8)) +
                (instanceOffset9 * float(index == 9)) +
                (instanceOffset10 * float(index == 10)) +
                (instanceOffset11 * float(index == 11)) +
                (instanceOffset12 * float(index == 12)) +
                (instanceOffset13 * float(index == 13)) +
                (instanceOffset14 * float(index == 14)) +
                (instanceOffset15 * float(index == 15)) +
                (instanceOffset16 * float(index == 16)) +
                (instanceOffset17 * float(index == 17)) +
                (instanceOffset18 * float(index == 18)) +
                (instanceOffset19 * float(index == 19));

            gl_Position = perspective * camera * vec4(position + vec3(offset.xy, 0.0), 1.0);
            vcolor = vec4(0.0, 0.5, 0.0, 0.5);
        }
    |]

It's very repetative code and if I want a new shader that also draws multiple instances, I need to copy paste this code.

Also while it's faster than making one draw call per instance, having to do the following in the vertex shader is probably expensive.

        mediump vec3 offset =
                (instanceOffset0 * float(index == 0)) +
                (instanceOffset1 * float(index == 1)) +
                (instanceOffset2 * float(index == 2)) +
                (instanceOffset3 * float(index == 3)) +
                (instanceOffset4 * float(index == 4)) +
                (instanceOffset5 * float(index == 5)) +
                (instanceOffset6 * float(index == 6)) +
                (instanceOffset7 * float(index == 7)) +
                (instanceOffset8 * float(index == 8)) +
                (instanceOffset9 * float(index == 9)) +
                (instanceOffset10 * float(index == 10)) +
                (instanceOffset11 * float(index == 11)) +
                (instanceOffset12 * float(index == 12)) +
                (instanceOffset13 * float(index == 13)) +
                (instanceOffset14 * float(index == 14)) +
                (instanceOffset15 * float(index == 15)) +
                (instanceOffset16 * float(index == 16)) +
                (instanceOffset17 * float(index == 17)) +
                (instanceOffset18 * float(index == 18)) +
                (instanceOffset19 * float(index == 19));

I admit, I haven't entirely thought out how the elm-webgl API should be modified in order to support instancing. My initial idea is to change the uniform parameter in entity to a list. I'm happy to discuss how this might be organized.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions