Beginner OpenGL Question: Drawing without geom objects?

Howdy everyone,

I’m currently just getting into OpenGL/Cinder, and I’m trying to work through and translate the beginning shader examples of the OpenGL SuperBible into Cinder (the OGSB uses its own framework which I’d like to avoid). The OpenGL-Cinder tutorial and most of the example code I’ve been able to work through assume that you’re going to pipe in vertex data and execute draw calls with objects from the geom namespace. My issue is that the beginning of the OGSB starts with a very simple no-input shader that just places a vertex, instantiates a VAO, and calls drawArrays(). I haven’t been successful recreating this process in Cinder (probably because I’m a beginner). So how exactly do you create a minimal shader example in Cinder without the use of geom objects? Below is my code attempting to recreate the first OGSB example which simply creates a vertex, passes it to the fragment, and then enlarges the size…hopefully I’m close?

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;
using namespace std;

class CinderOpenGLSuperBibleApp : public App {
public:
    void setup() override;
    void draw() override;
    
    CameraPersp camera;
    gl::GlslProgRef mGlsl;
};
void CinderOpenGLSuperBibleApp::setup()
{
    camera.lookAt(vec3(0,0,0),vec3(0));
    mGlsl = gl::GlslProg::create(gl::GlslProg::Format()
                    .vertex(CI_GLSL( 150,
                                    void main(void){
                                        gl_Position = vec4(0.0,0.0,0.5,1.0);
                                        }))
                    .fragment(CI_GLSL( 150,
                                      out vec4 color;
                                      void main(void){
                                          color = vec4(0.0,0.8,1.0,1.0);
                                        })));
    //I know this part is wrong, but I'm going to leave it in here...
    auto vao = gl::Vao::create();
    vao->bind();
    gl::enableDepthWrite();
    gl::enableDepthRead();
}

void CinderOpenGLSuperBibleApp::draw()
{
    gl::clear( Color( 0, 0, 0) );
    gl::setMatrices(camera);
    
    gl::pointSize(40.0f);
    gl::drawArrays(GL_POINTS, 0, 1);
    
}

CINDER_APP( CinderOpenGLSuperBibleApp, RendererGl)

took a quick glance at the code…
2 issues:

  1. you have not bind any data to the vao. therefore it doesn’t contain any vertex data.
  2. you are creating the vao with auto keyword which makes it only exists in the setup scope, you need to declare it in the definition part to make it available in the draw function.

the pseudo code would be something look like this:

//make a vbo that holds the vertex data
auto vert = vector<vec4>();
vert.push_back(vec4(0,0,0,1));
auto position = gl::Vbo::create( GL_ARRAY_BUFFER, vert.size() * sizeof(vec4), vert.data(), GL_STATIC_DRAW );    

//use m prefix so that you know its scope should go beyond the setup function
mVao = gl::Vao::create();
mVao->bind();
position->bind();
//bind the position vbo to the vao
gl::vertexAttribPointer( PositionIndex, 4, GL_FLOAT, GL_FALSE, 0, NULL ); 
//define this position index somewhere else as you gonna need this in your shader eventually
gl::enableVertexAttribArray( PositionIndex );
position->unbind();    

i just typed in the browser so the code might not work directly.

-seph

Note that @seph’s code isn’t necessarily the best way to do things in Cinder, although he answered your question adequately.

The easiest way to output a single vertex would be:
gl::begin( GL_POINTS ); gl::vertex( vec3( 0, 0, 0 ) ); gl::end();
This, too, is just a convenient way provided by Cinder, it’s not the most performant.

-Paul

1 Like

Thank you both for the replies! I think the issue here is that I’m trying to learn about OpenGL at the same time as trying to understand how Cinder works, and I can’t quite get it all sorted out. @paul.houx I took a peek at some of the source and it looks like Cinder uses GLFW under the hood – maybe I should learn the rudements of using that with the Superbible Code examples before using Cinder?

@seph, I can’t quite get your solution to work, but I think that’s because I dont quite know how the gl::drawArrays() function works with the data that you’ve bound to the Vao. I’m going to copy/paste my code again, just in case I’ve screwed something up. The way that it currently is written, I’m just getting a blank screen.

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"
using namespace ci;
using namespace ci::app;
using namespace std;

class CinderOpenGLSuperBibleApp : public App {
public:
void setup() override;
void draw() override;
vector<vec4> vert;
gl::VaoRef mVao;
int pos = 0;
CameraPersp camera;
gl::GlslProgRef mGlsl;
};
void CinderOpenGLSuperBibleApp::setup()
{
// initialization
camera.lookAt(vec3(0,0,0),vec3(0));
vert.push_back(vec4(0,0,0,0));
auto position = gl::Vbo::create(GL_ARRAY_BUFFER, vert.size() * sizeof(vec4), vert.data(), GL_STATIC_DRAW);
mVao = 	gl::Vao::create();

// bind vertex data
mVao->bind();
position->bind();
gl::vertexAttribPointer(pos, 4, GL_FLOAT, GL_FALSE, 0, NULL);
gl::enableVertexAttribArray(pos);
position->unbind();

// make a shader that doesn't accept input, just outputs a single vertex
mGlsl = gl::GlslProg::create(gl::GlslProg::Format()
                             .vertex(CI_GLSL( 150,
                                             void main(void){
                                                 gl_Position = vec4(0.0,0.0,0.5,1.0);
                                             }))
                             .fragment(CI_GLSL( 150,
                                               out vec4 color;
                                               void main(void){
                                                   color = vec4(0.0,0.8,1.0,1.0);
                                               })));
gl::enableDepthWrite();
gl::enableDepthRead();
}

void CinderOpenGLSuperBibleApp::draw()
{
gl::clear( Color( 0, 0, 0) );
gl::setMatrices(camera);

// scale up to make the point visible
gl::pointSize(60.0f);

// send the "dummy" vertex down the pipeline and draw
gl::drawArrays(GL_POINTS, pos, 1);

}

CINDER_APP( CinderOpenGLSuperBibleApp, RendererGl)

Hi,

I would advise to use the information in the OpenGL Super Bible for background theory only. Cinder was designed so that you don’t have to write hundreds of lines of convoluted OpenGL code yourself, saving you countless hours. If you’re like me and still want to know what is going on under the hood, add the Cinder project to your solution so that you can easily browse its source code while developing and debugging.

To learn more about the Cinder classes, have a look at the samples that ship with Cinder. I also maintain a bunch of samples here. Finally, there’s a great tutorial on modern OpenGL here.

With that being said, I’ll have a look at your code tomorrow and will try to make it work.

-Paul

hi,

quick look through your code, it seems you didn’t bind your glsl.
so add this line before exit the setup function should work:

mGlsl->bind();

-seph

Ok,

I looked at your code and here’s what I found:

  • Your camera is positioned at ( 0, 0, 0 ) but its target is also ( 0, 0, 0 ), which results in an invalid view matrix full of NaN’s. Make sure that your camera position and target are always different. One solution would be to set the position to (0, 0, 10).
  • Please don’t use the raw Vao and Vbo classes if you’re just taking your first steps in OpenGL/Cinder. Spend your time learning more about shaders instead. See my first response in this thread for a solution that outputs a single vertex. If you insist on writing the code yourself, have a look at VertBatch::setupBuffers().
  • In draw(), you have forgotten to bind the shader. Before rendering the vertex, add: gl::ScopedGlslProg scpGlsl( mGlsl );

-Paul

Thank you both for the help – I can’t seem to get it to work still, despite binding the shader and moving the camera. I think I’m going to take Paul’s advice here and just read through the SuperBible for theory and not try to directly port its example programs over to Cinder. The only reason why I was messing with the raw vao and vbo classes is because the SuperBible (7th ed) does that, and I wanted to have full understanding of how the pipeline worked…It seems that Cinder’s internal model uses these classes in a more complicated way than I first thought. Anyway, thanks again.

Hi Cynlic,

if you adjust your camera and use the gl::vertex( vec3( 0 ) ); approach to draw a vertex, you should see a white dot on your screen. With the shader enabled, it becomes a blue dot and the position of the vertex is no longer relevant as this will be set in the vertex shader.

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

using namespace ci;
using namespace ci::app;
using namespace std;

class CinderOpenGLSuperBibleApp : public App {
  public:
    void setup() override;
    void draw() override;

  private:
    CameraPersp     mCamera;
    gl::GlslProgRef mGlsl;
};

void CinderOpenGLSuperBibleApp::setup()
{
    // initialization
    mCamera.lookAt( vec3( 0, 0, 10 ), vec3( 0 ) );

    // make a shader that doesn't accept input, just outputs a single vertex
    mGlsl = gl::GlslProg::create(gl::GlslProg::Format()
        .vertex(CI_GLSL( 150,
            void main(void) {
                gl_Position = vec4( 0.0, 0.0, 0.5, 1.0 );
            }
    ) )
    .fragment(CI_GLSL( 150,
        out vec4 color;
        void main(void) {
            color = vec4( 0.0, 0.8, 1.0, 1.0 );
        }
    ) ) );
    
    // gl::enableDepthWrite();
    // gl::enableDepthRead();
}

void CinderOpenGLSuperBibleApp::draw()
{
    gl::clear();
    gl::setMatrices( mCamera );

    // Scale up to make the point visible.
    gl::pointSize( 60.0f );

    // This will construct a temporary Vbo (with compatible Vao).
    gl::begin( GL_POINTS );
    gl::vertex( vec3( 0 ) );
    gl::end();
}

CINDER_APP( CinderOpenGLSuperBibleApp, RendererGl )

-Paul

1 Like