Is instanced drawing (aka geometry instancing) possible in Codea?

I originally asked this on the thread about vertex culling, but I thought I’d move the question to its own thread as it was slightly off-topic.

Is instanced drawing (aka geometry instancing) possible in Codea? Many 3D projects (such as my blast-em-up) rely on repeatedly drawing the same model in different positions and orientations; if instancing were possible, then the data for the mesh would only have to be sent to the GPU once, with potentially large performance savings.

According to the iOS Dev docs, instanced drawing (or geometry instancing) is supported on iOS, although the implementation is different on OpenGL 3.0 than it is on 2.0 (on 2.0 you need to enable instancing in the shader with #extension GL_EXT_draw_instanced : enable. More info in these threads on Stack Exchange: 1 and 2 )

Below you can see a short example of a shader (7.6) and how the binding would work (7.5) from the iOS dev docs. I don’t know Objective C, so I can’t tell whether a binding like the one in 7.5 would be possible in Codea. Is it just a case of setting up some custom mesh buffers, or is it more involved than that?

A second part to the question is, are Open GL 3.0 shaders supported by Codea, if the device hardware supports 3.0? (ie iPad Air onwards)

Thanks in advance.

From iOS dev docs :

With the instanced arrays strategy, you store per-instance information in a vertex array attribute. Your vertex shader can then access that attribute to make use of per-instance information. Call the glVertexAttribDivisor function to specify how that attribute advances as OpenGL ES draws each instance. Listing 7-5 demonstrates setting up a vertex array for instanced drawing, and Listing 7-6 shows the corresponding shader.

Listing 7-5 Using a vertex attribute for per-instance information

#define kMyInstanceDataAttrib 5
 
glGenBuffers(1, &_instBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _instBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(instData), instData, GL_STATIC_DRAW);
glEnableVertexAttribArray(kMyInstanceDataAttrib);
glVertexAttribPointer(kMyInstanceDataAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribDivisor(kMyInstanceDataAttrib, 1);

Listing 7-6 OpenGL ES 3.0 vertex shader using instanced arrays

#version 300 es
 
layout(location = 0) in vec4 position;
layout(location = 5) in vec2 inOffset;
 
uniform mat4 modelViewProjectionMatrix;
 
void main()
{
    vec4 offset = vec4(inOffset, 0.0, 0.0)
    gl_Position = modelViewProjectionMatrix * (position + offset);
}

Instanced drawing is available in the core OpenGL ES 3.0 API and in OpenGL ES 2.0 through the EXT_draw_instanced and EXT_instanced_arrays extensions.

I can’t test this because I only have an iPad 3, but I note that you can use #extension in Codea shaders, eg code from an earlier forum thread

#extension GL_EXT_shader_framebuffer_fetch : require

void main() {
    gl_FragColor = 1.0 - gl_LastFragData[0];
}

So maybe you can test this yourself

Instancing apparently is possible on iPad 3 (according to the stack exchange threads and the dev docs), it just uses a slightly different syntax and requires the extension. But I don’t know whether it’s possible to translate the binding code given in 7.5 above into Codea’s Mesh API. The first 3 lines just seem to be setting up a mesh buffer, so that should be possible, but I’m not sure what is happening with the last 3 lines (of listing 7.5)

This is the relevant part of the stack exchange post :

Apparently, there is a possibility to use instanced rendering in OpenGL ES 2.0 by using the GL_EXT_draw_instanced extension. This extension provides one with two additional draw commands for instanced drawing (glDrawElementsInstancedEXT and glDrawArraysInstancedEXT). When using the extension, one has to enable it in the shader #extension GL_EXT_draw_instanced : enable and use gl_InstanceIDEXT instead of gl_InstanceID.

That makes it sound like you need to use a different draw command (ie the extension is not just something internal to the shader, but has a corresponding command outside the shader). So that doesn’t sound good, in terms of the likelihood of getting this running in Codea.

But I’m wondering whether you could roll your own, just using shaders?

The uniform model-view-projection matrix would need to be an array, like this, with an entry for each instance (from the same stack exchange thread):
uniform mat4 mvpMatrix[600]; (for up to 600 instances, presumably? I’ve never seen a uniform defined as an array before… Only per-vertex attributes… wonder whether this works?)

And then a for loop in the vertex shader to step through each instance.

You’d need some way of calculating the model-view-projection for each instance,[1] and also some way of dealing with a varying number of instances (ie it looks like the size of the array has to be declared in the shader), perhaps by setting scale to 0, like you can do to “delete” a rect on a 2d mesh.


  1. Probably just by translating and rotating as per usual, and then loading the modelMatrix() into the array, instead of issuing a draw command? ↩︎

Open GL 3.0 doesn’t seem to be supported in Codea:

ERROR: 0:1: ‘’ : version ‘300’ is not supported

ERROR: 0:1: ‘’ : syntax error: #version

And then a for loop in the vertex shader to step through each instance.

I don’t think you could do that. The vertex shader is called once per vertex, you want to call it several times per vertex. That’s not possible.

So something like this, which calls gl_position multiple times, wouldn’t work?
[edit: added comments]:

    // uniform mat4 modelViewProjection;
    uniform mat4 mvpMatrix[100]; //an array of model view projection matrices, one for each instance
    uniform int instances; //number of instances currently active

    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    
    void main()
    {
        vColor = color;
        vTexCoord = texCoord;
        for (int i=1; i<=instances;i++){gl_Position = mvpMatrix[i] * position;}
    }

It seems that what I’m describing is called pseudo-instancing (ie it’s not official hardware instancing, but could have some of its benefits). Looking at how others do it, they create multiple copies of the model in one mesh. They then have a per-vertex attribute which describes which instance the vertex belongs to. Some implementations hijack the w value of the normal, or the alpha value of the vertex color, to act as the instance pointer (so no for loop in the vertex shader). It would look something like this:

    // uniform mat4 modelViewProjection;
    uniform mat4 mvpMatrix[100]; //an array of model view projection matrices, one for each instance

    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    attribute int instance; //which instance of the model does this vertex belong to?

    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;

    void main()
    {
        vColor = color;
        
        vTexCoord = texCoord;
        gl_Position = mvpMatrix[instance] * position;
    }

This would be very fast, I think, for simple models that have lots of instances, such as a 3D particle system (it would be the 3D equivalent of having each particle as a rect on one big mesh, and then just having one draw command for the entire particle system).

I’m not so sure how efficient this pseudo-instancing would be for complex models though. I wonder if it would be possible to just make copies of the models’ vertices? You’d probably have to copy normals and texCoords too. I bet that it would still be faster (only one draw call), but that it would use more memory.

@Simeon can I request instanced rendering as a feature? Is it feasible do you think? (I’m not sure about the etiquette of adding feature requests to the issue tracker, whether it should be discussed on the forum first etc)

Specifically, it’s the OpenGL ES 2.0 EXT_draw_instanced and EXT_instanced_arrays extensions that I think would be an awesome addition. Codea is an incredibly environment at the moment for developing 3d games and other applications. Adding support for the instanced drawing extensions to the mesh api would be an awesome bonus.