Unexpected behavior of instancing

I was trying to understand a bit better how instancing works, in particular how to use instances ids in shaders, and I encountered something which I consider an idiosyncratic behavior.
Here’s the code

  import org.openrndr.application
  import org.openrndr.color.ColorRGBa
  import org.openrndr.draw.*
  import org.openrndr.extensions.Screenshots
  import org.openrndr.extra.noise.uniform
  import org.openrndr.math.Vector2
  import org.openrndr.shape.Circle
  import kotlin.math.*
  
  
  fun main() = application {
      configure {
          width = 1000
          height = 1000
      }
  
      program {
          val nContours = 20
  
          val circles = List(nContours){ Circle(Vector2.uniform(drawer.bounds), Double.uniform(100.0, 200.0)) }
  
          val bufferLengths = shaderStorageBuffer( shaderStorageFormat {
              member("length", BufferMemberType.FLOAT, nContours)
          })
  
          bufferLengths.put {
              for (i in 0 until nContours) {
                          write((exp( 0.05 * circles[i].radius)).toFloat())
                      }
              }
          val rt = renderTarget(width, height){
              colorBuffer()
              depthBuffer()
          }
  
          val buffer = colorBuffer(width, height)
  
          backgroundColor = ColorRGBa.BLACK
          extend(Screenshots())
          extend {
  
              drawer.fill = null
              drawer.stroke = ColorRGBa.WHITE
              drawer.strokeWeight = 2.0
  
              drawer.isolatedWithTarget(rt) {
                  drawer.clear(ColorRGBa.BLACK)
                  drawer.shadeStyle = shadeStyle {
                      fragmentTransform = """ 
                      x_stroke.rgb = vec3(1000.0/b_l.length[c_instance], 0.0, 1.0);
                  """.trimIndent()
                      buffer("l", bufferLengths)
                  }
  
  
                  drawer.circles(circles)
              }
  
              rt.colorBuffer(0).copyTo(buffer)
  
              drawer.isolatedWithTarget(rt) {
                  drawer.clear(ColorRGBa.BLACK)
                  drawer.shadeStyle = shadeStyle {
                      fragmentTransform = """      
                      x_stroke.rgb = vec3(100.0/b_l.length[c_instance], 0.0, 1.0);
                  """.trimIndent()
                      buffer("l", bufferLengths)
                  }
  
                  drawer.contours(circles.map { it.contour })
              }
  
              if (mouse.position.x < width * 0.5){
                  drawer.image(buffer)
              }
              else {
                  drawer.image(rt.colorBuffer(0))
              }
  
          }
      }
  }

In the first isolated render, I basically batch draw a list of circles via .circles, and access the buffer with the c_instance id, getting this (expected) result

On the other hand, in the second isolated render, I batch draw the circles’ contours via .contours, but it in this case it seems the c_instance id is always 0, hence all the circles have the same color

Notice that the same behavior appears if one batch draws the circles’ shapes via .shapes.
Is there something I’m missing on how c_instance behaves?

After simplifying the program to this

import org.openrndr.application
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.noise.uniform
import org.openrndr.math.Vector2
import org.openrndr.shape.Circle

fun main() = application {
    program {
        val circles = List(20) { Circle(Vector2.uniform(drawer.bounds), 40.0) }

        val style = shadeStyle {
            fragmentTransform = "x_fill.rgb = vec3(c_instance / 20.0, 0.0, 1.0);"
        }

        extend {
            drawer.shadeStyle = style
            drawer.stroke = null

            if (mouse.position.x < width * 0.5) {
                drawer.circles(circles)
            } else {
                drawer.contours(circles.map { it.contour })
            }
        }
    }
}

I control+clicked drawer.circles and drawer.contours and I was reminded that the first one uses batched mode, while the second method just iterates and draws the contours one by one, without setting any instance ID. I’ll ask if it would be possible to set the instance ID in the second case. It would be useful (and maybe more predictable :slight_smile: ).

1 Like

Oooh, I see. Yeah, setting the instance ID would make sense. On the other hand, if .contours doesn’t use batched mode, then I would say that my approach using an SBBO is an overkill from the performance point of view, since one can pass the given parameter in an ad hoc loop, which is what I wanted to avoid. I guess that also .shapes just draws the shapes in the list one by one rather than batching.

I just realized that also .lineSegments seems not to set c_instance…