Where do I learn how to actually make stuff in opengl

#1

I am learning OpenGL, but how do I actually make stuff with it? What I mean by this is where do I look to see how to “structure” my OpenGL code. For example how would I make a thing where I create objects, or how I make a project system. I know where to learn OpenGL, but not how to create things with it.

#2

creating structured code is not specific to opengl. a good point to start for you would be reading about “design patterns” (https://en.wikipedia.org/wiki/Software_design_pattern)

concerning opengl, it is pretty obvoius that you need classes (assuming you code in c++ or any other object-orientated language) for at least nodes and triangles/quads (maybe also lines).

a very simple structure could look like written below.
in addition to the classes mentioned above there is a model class which holds all nodes, trias and quads
in a std::vector container. objects can be created with the createNode, createTriangle and createQuad
functions. that way to create objects is the “factory method” design pattern.

#include<GL/gl.h>
#include<vector>

class Node {
public:
        Node(double x, double y, double z) { xyz[0] = x; xyz[1] = y; xyz[2] = z; }
        void draw() { glBegin(GL_POINTS); glVertex3dv(xyz); glEnd(); }
        void drawVertex() { glVertex3dv(xyz); }
    
protected:
    double xyz[3];
};

class Shell {
    public:
        Shell() {}
        virtual void draw() {}
        
protected:
    std::vector<Node*> nodes;
};

class Triangle : public Shell {
    public:
        Triangle(Node *n1, Node *n2, Node *n3) {
          nodes.push_back(n1);
          nodes.push_back(n2);
          nodes.push_back(n3);
        }
        void draw() {
            glBegin(GL_TRIANGLES);
             nodes[0]->drawVertex();
             nodes[1]->drawVertex();
             nodes[2]->drawVertex();
            glEnd();
        }
};

class Quad : public Shell {
    public:
        Quad(Node *n1, Node *n2, Node *n3, Node *n4) {
          nodes.push_back(n1);
          nodes.push_back(n2);
          nodes.push_back(n3);
          nodes.push_back(n4);
        }
        void draw() {
            glBegin(GL_QUADS);
             nodes[0]->drawVertex();
             nodes[1]->drawVertex();
             nodes[2]->drawVertex();
             nodes[3]->drawVertex();
            glEnd();
        }
};

class Model {
    public:
        Model() {}
        void draw() {
            for(unsigned int i = 0; i < shells.size(); i ++) {
                shells[i]->draw();
            }
        }
        Node *createNode(double x, double y, double z) {
            Node *n = new Node(x, y, z);
            nodes.push_back(n);
            return n;
        }
        Triangle *createTriangle(Node *n1, Node *n2, Node *n3) {
            Triangle *t = new Triangle(n1, n2, n3);
            shells.push_back(t);
            return t;
        }
        Quad *createQuad(Node *n1, Node *n2, Node *n3, Node *n4) {
            Quad *q = new Quad(n1, n2, n3, n4);
            shells.push_back(q);
            return q;
        }
        /* TODO: add init(), load(), save() functions */

protected:
    std::vector<Node*> nodes;
    std::vector<Shell*> shells;
};

int main(int argc, char *argv[]) {
    Model *model = new Model;
    Node *n1 = model->createNode(-1.0, -1.0, 0.0);
    Node *n2 = model->createNode( 1.0, -1.0, 0.0);
    Node *n3 = model->createNode( 1.0,  1.0, 0.0);
    Node *n4 = model->createNode(-1.0,  1.0, 0.0);
    Quad *quad = model->createQuad(n1, n2, n3, n4);
}

#3

Design your classes around what is convenient (and efficient) for OpenGL. Don’t needlessly split draw calls to fit some “textbook” OOP structure.

Personally, I’d suggest avoiding OOP altogether if you think you might need other people to look at your code. If you post some code using user-defined classes, no-one else can know what the code is actually doing without first reading the code for the methods being used. So you can’t just post “relevant” functions, you have to post a github (etc) link, and hope that someone is willing to actually check out the repo, compile it, run it, debug it. Which is somewhat less likely than someone simply looking over some posted code and commenting on it.

Modern OpenGL revolves around draw calls (glDrawArrays, glDrawElements as well as more complex “multi” and/or “indexed” variants of them). Forget that glBegin and glEnd exist (in a core profile or in OpenGL ES, they don’t exist). Aside from the call itself, you have the setup: glUseProgram, uniforms, VAOs, textures, buffers, etc.

For a small number of objects, you can basically equate “objects” with draw calls and their associated setup. For a larger number of objects, you need to think about ways to render multiple objects with a single draw call, which means using the same set of attribute arrays, textures, buffers, uniforms etc for all of the objects. If you have uniforms which need to have different values for different objects, you have to replace them e.g. by storing the different values in a uniform array (or SSBO) which is indexed by an integer attribute, gl_InstanceID, gl_VertexID/num_vertices_per_object, or whatever.

From an efficiency perspective, probably the most important thing is not to fall into the trap of trying to make the system more flexible than it actually needs to be. If you allow for the entire rendering state (program, buffers, attributes, textures) changing on every draw call, that’s going to have a significant performance cost.

#4

I’d suggest looking at someone else’s code

Open Source Game Engines

#5

Personally, I’d suggest avoiding OOP altogether if you think you might need other people to look at your code.

that is the absurdest advice i’ve ever read. your code may have 10.000 lines, and you choose a programming language based on the chance that you may have a problem in 20 of those lines?

of course you should always pick a language which allows you to program 10.000 lines efficiently and structured. if you have a problem with some open gl stuff, you can still extract those 20 lines from your code and change them so they can be compiled as simple C, because C++ uses exactly the same open gl headers and libs as C.

and personally, i recommend that anybody who wants to program at least tries to understand OOP basics.
for application development in linux and windows, Qt is the best choice for GUI libs in my opinition. which
means you have to code in C++. python seems to be quite popular these days, and it is object oriented too (though you can use it without its OOP capabilities). and then there is still java.

#6
  1. I’m referring to people like the OP who are still very much in the learning phase. If you’re not confident about being able to solve your own problems, “red book” style code is much easier for other people to understand than if you shoehorn everything into OO structures for the sake of it.

  2. It’s not about language. Using C++ is fine, using std classes (or common libraries such as GLM) is fine. Just don’t write code where you don’t understand any of it until you understand all of it.

And C++ probably isn’t that language until you’re fluent in both C++ and OpenGL. I’ve seen far too much code from novice programmers which uses OOP for its own sake to the detriment of legibility.

And also to the detriment of efficiency; e.g. requiring one draw call per object because that makes for a “neat” OO structure. But that’s a separate issue.

You can, and you should. But if the code uses classes for absolutely everything, that might take as long as writing the program from scratch.

If you’re writing code primarily in order to learn OpenGL, you’d do well to stick to “red book” style. By all means use C++, std::vector instead of bare arrays, and GLM for vectors and matrices. Just don’t force people to be continually switching their focus away from the “actual” code and taking excursions into class methods or helper functions.