Hello, Cinderists!
I’m still pretty new to Cinder and I’m working my way through the wonderful samples folder.
Currently I’m trying to blend multiple simple mesh instances using the instanced teapot sample as the base. I’m updating the instance positions over time, but there seems to be a problem with depth testing or the draw function? I wanted to blend all the instances with “Additive” blending, but I’m getting a weird result.
I was hoping for the instances to blend by depth, but it seems that the “draw call id”(?) for each instance stays the same. Is there an easy way to sort the instance draw calls based on depth?
I’m also adding the code to reproduce the effect. Could someone go through the code and point out what I’ve gotten wrong? There are two different approaches. First one is where the animations is done only in the vertex shader. The second one uses the VBO remapping function. For the remapping to work, you must uncomment it in the cpp and in the vertex shader.
Any help will be appreciated. Thank you.
main cpp file
#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
#include "cinder\Utilities.h"
#include <math.h>
using namespace ci;
using namespace ci::app;
using namespace std;
class Blending001App : public App {
public:
void setup() override;
void mouseDown( MouseEvent event ) override;
void update() override;
void draw() override;
void resize();
static void prepare(Settings *settings);
std::vector<ci::vec3> positions;
CameraPersp mCam;
gl::BatchRef mBatch;
gl::GlslProgRef mShader;
gl::VboRef instanceVbo;
int const instances = 72 * 4;
float const maxAngle = M_PI * 6.0;
float const posRadius = 80.0;
float const circleRadius = 24.0;
};
int window_x = 800;
int window_y = 800;
void Blending001App::setup()
{
//Shader building
mShader = gl::GlslProg::create(loadAsset("vert001.vert"), loadAsset("frag001.frag"));
mShader->uniform("numInstances", instances);
//VBO mesh
gl::VboMeshRef mesh = gl::VboMesh::create(geom::Circle().radius(circleRadius).subdivisions(32));
//Calculate instance positions
for (int i = 0; i < instances; i++)
{
float loopRatio = i / (float)instances;
vec3 radius = glm::vec3(posRadius, 0.0, 0.0);
glm::mat4 rotationMat(1);
rotationMat = glm::rotate(rotationMat, loopRatio * maxAngle, glm::vec3(0.0, 0.0, 1.0));
vec3 instancePos = glm::vec3(rotationMat * vec4(radius, 1.0));
positions.push_back(instancePos + vec3(0.0, 0.0, loopRatio));
}
//Same process as instanced teapots sample
instanceVbo = gl::Vbo::create(GL_ARRAY_BUFFER, positions.size() * sizeof(vec3), positions.data(), GL_DYNAMIC_DRAW);
geom::BufferLayout instanceLayout;
instanceLayout.append(geom::Attrib::CUSTOM_0, 3, 0, 0, 1);
mesh->appendVbo(instanceLayout, instanceVbo);
mBatch = gl::Batch::create(mesh, mShader, { {geom::Attrib::CUSTOM_0, "vInstancePosition"} });
gl::enableDepthRead();
gl::enableDepthWrite();
}
void Blending001App::mouseDown( MouseEvent event )
{
}
void Blending001App::resize()
{
mCam.setPerspective(30.0, getWindowAspectRatio(), 300, 700);
gl::setMatrices(mCam);
}
void Blending001App::update()
{
float t = getElapsedSeconds();
//Update camera position
vec3 camPos = vec3(30.0 * cos(t), 30.0 * sin(t), 400.0);
mCam.lookAt(camPos, vec3( 0.0));
//Shader uniform update
mShader->uniform("uTime", t);
// Update positions with mapping
// Uncomment to work with VERSION 2 in vertex shader
//auto ptr = (vec3*)instanceVbo->mapReplace();
//for (auto &pos : positions) {
// pos.z = fract(pos.z + 0.002);
// *ptr++ = pos;
//}
//instanceVbo->unmap();
}
void Blending001App::draw()
{
gl::setMatrices(mCam);
{
gl::ScopedBlendAdditive scpBlend;
gl::clear( Color( 0.0, 0.0, 0.0) );
mBatch->drawInstanced(instances);
}
}
void Blending001App::prepare(Settings *settings)
{
settings->setWindowSize(window_x, window_y);
settings->setFrameRate(60.0f);
settings->setTitle("Blending App");
}
CINDER_APP( Blending001App, RendererGl, &Blending001App::prepare )
vert001.vert
#version 150
uniform mat4 ciModelViewProjection;
uniform mat3 ciNormalMatrix;
uniform float uTime;
in vec4 ciPosition;
in vec3 vInstancePosition; // per-instance position variable
out float ID;
float pi = 3.14159;
void main( void )
{
//Pass through to fragment shader
ID = gl_InstanceID;
//Vertex Animation
vec3 anim = vInstancePosition;
float zPos = fract(anim.z + uTime * 0.3) * 200.0;
anim.z = zPos - 200.0;
//VERSION 1 - Vertex shader animation.
gl_Position = ciModelViewProjection * ( ciPosition + vec4( anim, 0 ) );
//VERSION 2 - Remapping VBO in CPP file animation
//Must uncomment mapping commands in CPP update function to work
//gl_Position = ciModelViewProjection * ( ciPosition + vec4(vInstancePosition*vec3(1.0, 1.0, 200.0)-vec3(0.0, 0.0,200.0), 0.0) );
}
frag001.frag
#version 150
uniform int numInstances;
in float ID;
out vec4 oColor;
vec3 colorA = vec3(1.0, 0.68, 0.0);
vec3 colorB = vec3(0.05, 0.3, 0.8);
void main( void )
{
float ratio = ID / float(numInstances);
vec3 fragCol = mix(colorA, colorB, ratio);
oColor = vec4( fragCol * 0.05 , 1.0);
}