Tinting a TriMesh (what exactly does ci::gl::color() do?)

I’m trying to figure out how to tint a TriMesh, and this has led me down a bit of an opengl rabbithole and I’m hoping the community here can help me sort a few things out.

The very first thing I tried was to just bind a stock shader, set the color, and tint my TriMesh:

			auto shaderDef = ci::gl::ShaderDef().lambert().color();
			ci::gl::ScopedGlslProg scopedShader(ci::gl::getStockShader(shaderDef));
			ci::gl::ScopedColor color(ci::Color(1, 0, 0));

Now this, may have worked if my TriMesh was textured, but upon further inspection, it doesn’t use a texture – it looks like the model has per-vertex data embedded into it with the colors.

I did a bit of digging and found that draw( const TriMesh &mesh ) calls draw( const geom::Source &source ). This looks fairly straightforward – it checks for a bound shader, determines attributes needed by the shader, and sets those attributes. It calls ctx->setDefaultShaderVars() which has this snippet:

		const auto &attribs = glslProg->getActiveAttributes();
		for( const auto &attrib : attribs ) {
			switch( attrib.getSemantic() ) {
				case geom::Attrib::COLOR: {
					ColorA c = ctx->getCurrentColor();
					gl::vertexAttrib4f( attrib.getLocation(), c.r, c.g, c.b, c.a );

Okay, got it – colors from the 3d model are being loaded directly into the vertex attributes, and this is why none of my calls to ci::gl::color() will affect it – whatever I do outside of this scope will get overwritten when the vertex attributes are assigned.

It seems to me that the solution is to write my own custom vertex shader that tints the vertex color on a per-vertex basis:

		uniform mat4	ciModelViewProjection;
		in vec4			ciPosition;
		in vec4			ciColor;
                in vec4             tintColor;
		out vec4		outColor;
		void main( void ) {
			gl_Position	= ciModelViewProjection * ciPosition;
			outColor = mix(ciColor, tintColor, 0.5);

and a corresponding fragment shader that uses outColor.

So I can just create a vec4 uniform to provide the tint color, and… what a minute, can’t I do that with ci::gl::color()? If the vertex attributes are loaded into the vertex shader using the ciColor uniform, where is the context’s current color set by `gl::color’? Is that already being loaded as a uniform somewhere that I can access?

Going down this rabbithole has made me realize that while I know there is a difference between the color vertex attribute and the context’s color, I’m not actually exactly sure how they’re different and how they work with opengl. So this is where I send up the signal flare and hope that someone can help me wrap my head around this!

thanks all!

Generally speaking whenever I’m doing anything beyond a test draw call I’ll use a custom shader to remove any ambiguity. Colours are treated as a bit of a special case in cinder as you’ve found, which is super helpful when just trying to get something on the screen but pretty quickly you want do have strict control over your attributes and whatnot, at least in my experience.

Rather than reading through the code and trying to reason about the code path, I’d suggest using something like RenderDoc to attach to your app, then you can issue a few different draw calls which you can then step through in renderdoc and see the exact attribute state of each vertex as well as uniforms and internal state. This way you can get a better understanding of what data is coming from where (and when) and how it contributes to your final pixel.

One small (unrelated) thing, TriMeshes live on the CPU side, so every time you gl::draw them, you’re reuploading potentially unchanged data. If you’re not modifying the vertex data it’s better to store it in a gl::VboMesh on the GPU side and redraw that, it will be a lot faster.

1 Like

Makes sense! RenderDoc looks like a great tool; I’ll check it out!

I feel confident I can put together a custom shader to make this work, but it’s clear to me now that Cinder is doing some stuff I don’t quite understand with respect to how color works… are there any other existing threads / docs that I should read to better understand this?