The simplest, minimalistic, opengl 3.2 cocoa project
Asked Answered
F

2

8

I have used the legacy openGL with cocoa for years, but I'm now struggling to make the transition to openGL 3.2. There are several examples in the internet, but they are all too complex (and many don't even compile any more under XCode 5.1). Could someone write an example of the simplest, minimalistic, minimum cocoa code just to draw a read triangle to a NSOpenGLView? (no fancy shaders, no displayCallbacks, the fewer the code lines, the better).

Firstborn answered 15/3, 2014 at 18:12 Comment(2)
Thing is you will need at the very least 2 basic shaders. I would also consider using GLFW/GLEW instead of NSOpenGLView (using GLFW made things a lot easier for me).Bluster
thank you! I just came up with an example that has a basic vertex shader and a basic fragment shader. It's not at all an example of good coding, but it did help me understand better how all the pieces fitted together...Firstborn
F
7

Here's an answer based on the code in https://github.com/beelsebob/Cocoa-GL-Tutorial I changed these things: (1) The openGL context is created in a custom NSOpenGLView and not directly appended to the window. (2) I all the initialisation in one single function. (3) I deleted all the error verification code. This is not something you should do for a product, but I find it easier to understand the code with less clutter... (look at Cocoa-GL-Tutorial for proper error handling).

The steps (tested with Xcode 5.1):

  1. Make a new cocoa application
  2. Add a Custom View to the app window in the interface builder
  3. Add an Objective-C class, subclassing NSOpenGLView, I called it MyOpenGLView
  4. In the interface builder, select the CustomView, select the Identity Inspector (one of the icons top-right), and in Custom Class select MyOpenGLView
  5. Now, this is the code for MyOpenGLView.h:

    #import <Cocoa/Cocoa.h>
    #import <OpenGL/OpenGL.h>
    #import <OpenGL/gl3.h>

    @interface MyOpenGLView : NSOpenGLView
    {
        GLuint shaderProgram;
        GLuint vertexArrayObject;
        GLuint vertexBuffer;

        GLint positionUniform;
        GLint colourAttribute;
        GLint positionAttribute;
    }
    @end

And this is the code for MyOpenGLView.m:


    #import "MyOpenGLView.h"

    @implementation MyOpenGLView

    - (id)initWithFrame:(NSRect)frame
    {
        // 1. Create a context with opengl pixel format
        NSOpenGLPixelFormatAttribute pixelFormatAttributes[] =
        {
            NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
            NSOpenGLPFAColorSize    , 24                           ,
            NSOpenGLPFAAlphaSize    , 8                            ,
            NSOpenGLPFADoubleBuffer ,
            NSOpenGLPFAAccelerated  ,
            NSOpenGLPFANoRecovery   ,
            0
        };
        NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes];
        self = [super initWithFrame:frame pixelFormat:pixelFormat];

        // 2. Make the context current
        [[self openGLContext] makeCurrentContext];

        // 3. Define and compile vertex and fragment shaders
        GLuint  vs;
        GLuint  fs;
        const char    *vss="#version 150\n\
        uniform vec2 p;\
        in vec4 position;\
        in vec4 colour;\
        out vec4 colourV;\
        void main (void)\
        {\
        colourV = colour;\
        gl_Position = vec4(p, 0.0, 0.0) + position;\
        }";
        const char    *fss="#version 150\n\
        in vec4 colourV;\
        out vec4 fragColour;\
        void main(void)\
        {\
        fragColour = colourV;\
        }";
        vs = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs, 1, &vss, NULL);
        glCompileShader(vs);
        fs = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs, 1, &fss, NULL);
        glCompileShader(fs);
        printf("vs: %i, fs: %i\n",vs,fs);

        // 4. Attach the shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vs);
        glAttachShader(shaderProgram, fs);
        glBindFragDataLocation(shaderProgram, 0, "fragColour");
        glLinkProgram(shaderProgram);

        // 5. Get pointers to uniforms and attributes
        positionUniform = glGetUniformLocation(shaderProgram, "p");
        colourAttribute = glGetAttribLocation(shaderProgram, "colour");
        positionAttribute = glGetAttribLocation(shaderProgram, "position");
        glDeleteShader(vs);
        glDeleteShader(fs);
        printf("positionUniform: %i, colourAttribute: %i, positionAttribute: %i\n",positionUniform,colourAttribute,positionAttribute);

        // 6. Upload vertices (1st four values in a row) and colours (following four values)
        GLfloat vertexData[]= { -0.5,-0.5,0.0,1.0,   1.0,0.0,0.0,1.0,
                                -0.5, 0.5,0.0,1.0,   0.0,1.0,0.0,1.0,
                                 0.5, 0.5,0.0,1.0,   0.0,0.0,1.0,1.0,
                                 0.5,-0.5,0.0,1.0,   1.0,1.0,1.0,1.0};
        glGenVertexArrays(1, &vertexArrayObject);
        glBindVertexArray(vertexArrayObject);

        glGenBuffers(1, &vertexBuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, 4*8*sizeof(GLfloat), vertexData, GL_STATIC_DRAW);

        glEnableVertexAttribArray((GLuint)positionAttribute);
        glEnableVertexAttribArray((GLuint)colourAttribute  );
        glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), 0);
        glVertexAttribPointer((GLuint)colourAttribute  , 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (char*)0+4*sizeof(GLfloat));

        return self;
    }

    - (void)drawRect:(NSRect)dirtyRect
    {
        [super drawRect:dirtyRect];

        glClearColor(0.0, 0.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(shaderProgram);
        GLfloat p[]={0,0};
        glUniform2fv(positionUniform, 1, (const GLfloat *)&p);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

        [[self openGLContext] flushBuffer];
    }

    @end

Firstborn answered 19/3, 2014 at 10:33 Comment(1)
For some reason the initWithFrame function doesn't seem to get called at all. It started working when I did as nik's answer suggested.Trella
T
7

https://mcmap.net/q/1282240/-the-simplest-minimalistic-opengl-3-2-cocoa-project

In xcode 6.3.2 I got the example running afterreplacement of

(id)initWithFrame:(NSRect)frame
with
(void)awakeFromNib

and replacement of

self = [super initWithFrame:frame pixelFormat:pixelFormat];
with
super.pixelFormat=pixelFormat;

and deletion of
return self;

or written out in detail:

#import "MyOpenGLView.h"

@implementation MyOpenGLView


- (void)awakeFromNib
{
    NSLog(@"...was here");
    // 1. Create a context with opengl pixel format
    NSOpenGLPixelFormatAttribute pixelFormatAttributes[] =
    {
        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
        NSOpenGLPFAColorSize    , 24                           ,
        NSOpenGLPFAAlphaSize    , 8                            ,
        NSOpenGLPFADoubleBuffer ,
        NSOpenGLPFAAccelerated  ,
        NSOpenGLPFANoRecovery   ,
        0
    };
    NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes];

    super.pixelFormat=pixelFormat;

    // 2. Make the context current
    [[self openGLContext] makeCurrentContext];

    // 3. Define and compile vertex and fragment shaders
    GLuint  vs;
    GLuint  fs;
    const char    *vss="#version 150\n\
    uniform vec2 p;\
    in vec4 position;\
    in vec4 colour;\
    out vec4 colourV;\
    void main (void)\
    {\
    colourV = colour;\
    gl_Position = vec4(p, 0.0, 0.0) + position;\
    }";
    const char    *fss="#version 150\n\
    in vec4 colourV;\
    out vec4 fragColour;\
    void main(void)\
    {\
    fragColour = colourV;\
    }";
    vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vs, 1, &vss, NULL);
    glCompileShader(vs);
    fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fs, 1, &fss, NULL);
    glCompileShader(fs);
    printf("vs: %i, fs: %i\n",vs,fs);

    // 4. Attach the shaders
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vs);
    glAttachShader(shaderProgram, fs);
    glBindFragDataLocation(shaderProgram, 0, "fragColour");
    glLinkProgram(shaderProgram);

    // 5. Get pointers to uniforms and attributes
    positionUniform = glGetUniformLocation(shaderProgram, "p");
    colourAttribute = glGetAttribLocation(shaderProgram, "colour");
    positionAttribute = glGetAttribLocation(shaderProgram, "position");
    glDeleteShader(vs);
    glDeleteShader(fs);
    printf("positionUniform: %i, colourAttribute: %i, positionAttribute: %i\n",positionUniform,colourAttribute,positionAttribute);

    // 6. Upload vertices (1st four values in a row) and colours (following four values)
    GLfloat vertexData[]= { -0.5,-0.5,0.0,1.0,   1.0,0.0,0.0,1.0,
        -0.5, 0.5,0.0,1.0,   0.0,1.0,0.0,1.0,
        0.5, 0.5,0.0,1.0,   0.0,0.0,1.0,1.0,
        0.5,-0.5,0.0,1.0,   1.0,1.0,1.0,1.0};
    glGenVertexArrays(1, &vertexArrayObject);
    glBindVertexArray(vertexArrayObject);

    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, 4*8*sizeof(GLfloat), vertexData, GL_STATIC_DRAW);

    glEnableVertexAttribArray((GLuint)positionAttribute);
    glEnableVertexAttribArray((GLuint)colourAttribute  );
    glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), 0);
    glVertexAttribPointer((GLuint)colourAttribute  , 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (char*)0+4*sizeof(GLfloat));

}


- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.

    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(shaderProgram);
    GLfloat p[]={0,0};
    glUniform2fv(positionUniform, 1, (const GLfloat *)&p);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    [[self openGLContext] flushBuffer];
}

@end
Triolein answered 6/6, 2015 at 23:43 Comment(1)
Can you edit to include a link to the other answer, along with the full result of your changes?Oratory

© 2022 - 2024 — McMap. All rights reserved.