Beginner Thoughts and Questions

Hello, Cinderists!
Young grasshopper here.

I’ve been using Cinder for a few weeks and I’m trying to wrap my head around it, I’m quite enjoying it as Cinder looks promising. I’m learning Cinder to improve my C++ skills and learn graphics programming from a low-level standpoint. I’m quite familiar with 3D graphics by using Houdini as an everyday tool, so I’m not totally green.

A the moment I’m trying to understand the Instancing workflow with ‘per-isntance’ attributes. I’m rewriting the samples included with cinder and reading a lot of materials from the web, but a general workflow seems hard to grasp. I have a few questions and an example that would be great if someone could answer.

Questions:

  1. Is there some kind of an hierarchy to better understand the relationship between vertBatch, Batch, Vbo or VboMesh? How are they connected? Is one a container for the other? Is the Vbo a component of the Batch?
  2. In Houdini I’m used to doing geometry manipulation using VEX. Am I assuming correct that the vertex shader in OpenGL is like a VEX wrangle in Houdini? Would I animate using the vertex shader?

Example:
I have re-written the ‘Instanced Teapots’ sample to pass more than one attribute to the instances. Could someone, please, check the code if I’m doing it correctly? The part that seems unclear is if I have to initialize multiple VBO’s to pass multiple attributes for a Batch? Couldn’t I just append multiple attributes to a single VBO?

The code runs and does what I want, but if someone more experienced would look at it, he might find some issues worth pointing out.

const int NUM_INSTANCES_X = 20;
const int NUM_INSTANCES_Y = 20;
const float DRAW_SCALE = 200;
const float camPosY = 50.0f;

void instancingApp::setup()
{
	mCam.lookAt(vec3(0, camPosY, 0), vec3(0));

	mGlsl = gl::GlslProg::create(loadAsset("shader.vert"), loadAsset("shader.frag"));

	gl::VboMeshRef mesh = gl::VboMesh::create(geom::Torus());

	std::vector<vec3> positions;
	std::vector<float> myColor;
	int loopNum = 0;
	for (size_t posX = 0; posX < NUM_INSTANCES_X; ++posX)
	{
		for (size_t posY = 0; posY < NUM_INSTANCES_Y; ++posY)
		{
			float ratio = loopNum / ((float)NUM_INSTANCES_X * (float)NUM_INSTANCES_Y - 1);
			myColor.push_back(ratio);
			loopNum++;
			
			float instanceX = posX / (float)NUM_INSTANCES_X - 0.5f;
			float instanceY = posY / (float)NUM_INSTANCES_Y - 0.5f;
			vec3 xPos = vec3(DRAW_SCALE, 0, 0) * instanceX;
			vec3 yPos = vec3(0, 0, DRAW_SCALE) * instanceY;
			positions.push_back(xPos + yPos);
		}
		loopNum++;
	}
	
	// POSITIONS
	mInstanceDataVbo = gl::Vbo::create(GL_ARRAY_BUFFER, positions.size() * sizeof(vec3), positions.data(), GL_DYNAMIC_DRAW);

	geom::BufferLayout instanceDataLayout;
	instanceDataLayout.append(geom::Attrib::CUSTOM_0, 3, 0, 0, 1 );
	
	mesh->appendVbo(instanceDataLayout, mInstanceDataVbo);

	// COLORS BASED ON LOOP ID
	mInstanceColorVbo = gl::Vbo::create(GL_ARRAY_BUFFER, myColor.size() * sizeof(float), myColor.data(), GL_DYNAMIC_DRAW);

	geom::BufferLayout instanceColorLayout;
	instanceColorLayout.append(geom::Attrib::CUSTOM_1, 1, 0, 0, 1);  // It works, but I'm not really sure if I'm doing it correct.

	mesh->appendVbo(instanceColorLayout, mInstanceColorVbo);

	// Create Batch with Attributes
	mBatch = gl::Batch::create(mesh, mGlsl, { {geom::Attrib::CUSTOM_0, "vInstancePosition"}, { geom::Attrib::CUSTOM_1, "fMyColor" } }); 

	gl::enableDepthWrite();
	gl::enableDepthRead();

}

void instancingApp::resize()
{
	mCam.setPerspective(60, getWindowAspectRatio(), 1, 1000);
	gl::setMatrices(mCam);
}

void instancingApp::draw()
{
	gl::clear( Color( 0.2, 0.2, 0.2 ) ); 

	gl::setMatrices(mCam);
	mBatch->drawInstanced(NUM_INSTANCES_X * NUM_INSTANCES_Y); 	
}

Thank you for looking into this subject.

Hi and welcome to Cinder.

I started with Cinder and am now sometimes doing stuff with Houdini so maybe able to answer some questions…

I believe the structure would be something like this:

  • Vbo(*)                
    
  • +                          ->  VboMesh
    
  • Vao(stores primitive data)     +         -> Batch
    
  •                                glslProg    
    

So (multiple) vbo(s) binded with a Vao which defines the indices and the primitive generates a VboMesh, a VboMesh binded with a glslProg generates a Batch. I haven’t used vertBatch much but I think its the same idea.

Its similar but VEX also lets you store the outcome data for future use - in that since maybe TransformFeedback is the identical technique. Although technically I’m not sure if VEX runs on the GPU. If you want more freedom I suggest you to look into compute shaders.

Your sample code looks clean and should work. But if you need more than just one per instance data I would suggest encapsulate everything you need into a SSBO and use gl_InstanceID to sample it in the shader, something like this sample is doing.

  • seph

Thanks, Seph-Sensei for replying!

You scratch my back, I scratch yours. If You need any help with Houdini I can certainly try to help You if you need it. :slight_smile:

After reading your reply I did some web surfing and at the moment the structure to me seems like this. Could you read this and point out any major mistakes?
//The structure.

  • Vbo’s are attributes/parameters(floats, integers, vectors of floats, arrays, etc.) that can be passed to a Vao, but I need to describe some sort or data layout before adding it. The layout still seems as a mistery.

  • Cinder has some built-in Vao’s (sphere, cube, torus, etc.) primitives.
    So the Vao is like the vertex arrengment from the “Drawing your first triangle” openGL tutorials?

  • So a VboMesh is a Vao(Geometry description) with Vbo’s(Application generated parameters) and the parameters have a described layout(the memory managment thing in C++?).

  • If I want to draw this data I must add a GlslProg to my VboMesh. The GlslProg is how I attach GLSL shaders. Using my artistic sense in the shader I describe how the Vbo and Vao affect each other. Doing this in the shader is preffered because it runs on the GPU.

  • After all that is done, I have a Cinder Batch, which I can draw.

//End of structure

From a beginner point of view.
What makes this hard to understand is that in the ‘OpenGL in Cinder’ tutorial on libcinder.org does not describe what Cinder does under the hood to draw a Batch. Now the Cinder sample ‘VboMesh’ doesn’t make much sense, because there isn’t a gl::Batch::create() function, but somehow it does draw…I’m not complaining it’s just seems as there is a gap.

Geometry Manipulation.
In Houdini VEX is multi-threaded and it runs on the CPU. Some nodes in Houdini have OpenCL support, those run on the CPU and GPU.

Thanks for pointing out the The Transform Feedback(TF) technique, it keeps popping up in my web searches. I’m also following the OpenGL Super Bible book, the TF section is not far away. Just wondering, is the TF the part where you write textures to the GPU that describe for example particle locations?

Anyway I’m enjoying Cinder. Developing graphics without a GUI is an impressive challenge to overcome.

Ending on another positive note. I rendered my first ever OpenGL animation, I call it “The Sun is a Mole.”

while there are some small misconcepts (and maybe confusion because I didn’t articulate last time).

Vbo essentially stores data just like texture, you can think it as a single-dimensional texture that each channel could be int8/uint8/half/float and the maximum channel is 4. I find Cinder’s way of passing the layout pretty straightforward but if you dont know much about how openGL works then it might be a hassle.

When I say Vao defines the primitive I didn’t mean the primitive type as Houdini describes it - Vao defines how the vertex data are arranged and how each Vbo means in a mesh structure. You could assign a different primitive type when drawing using the same Vao(by binding a different element array buffer). All these mesh structures live on the GPU and is not Cinder specific as they are required/defined by openGL not memory management in cpp - Cinder offers easier wrapper to work with even lower openGL APIs.

OpenGL is a state machine, so when it draws it uses the current GlslProg in the memory - which means you dont “add” a GlslProg to your VboMesh, but making sure correct GlslProg and vbomesh are paired when drawing.

To be honest if you are keen to understand how these openGL structures are defined their specific usage the Cinder forum is not an ideal place - you may post your questions to the OpenGL forum and its fairly easy to translate those concepts into Cinder.

As for the VEX manipulation - if you dont add new primitive/vertex but only manipulate vertex data you can totally do it directly in the shader. But most likely to completely replicate what you do in a Houdini VEX node you would need a hybrid solution for realtime stuff - at least you will need to re-upload indices data to change the topology of your mesh.

I’m not sure what you mean by this - TF basically let you write data directly back into Vbos without any interaction with CPU which is a performance gain compared to having to traditional techniques where you have to map GPU memories back into CPU.

-seph

Hi, Seph!

Thank you for taking your time and writing such a detailed answer. I appreciate it.

Your explanation and suggestions are much clearer now and will help. In Your answer the part about OGL being a state machine seemed very interesting and how you don’t “add” Shaders to meshes, but only pair for the drawing state. Must have thought about it the other way because in regular 3D Apps (Blender for example) everyone adds materials to their models. And a material is basically a shader.

I did visit the OpenGL forums and found some interesting resources there. For example the very first page of the OGL Specification has a great illustration of the OGL “map.” Link. I’ll probably will return to that forum more than a few times.

I’m more interested in making some interactive animations than tacking apart the whole OpenGL structure. It just seems wiser to understand how Cinder sits on top of OGL, so I can I know what are the trade offs when using different approaches and dive deeper if needed.

Thanks again, Seph. I’m now going back to take apart those Cinder samples.