OpenGL on tarkoitettu koneiden kirjoitettavaksi

Kirjoittanut Jarkko Vilhunen 12. Joulukuu 2011 - 1:39.

Värikäs kolmio

Kerran eräänä talvisena päivänä linkkari kertoi toiselle epämiellyttävistä tunteistaan, tässäpä pientä aamuöistä jatketta aiheesta. Tehtävänä on piirtää värikäs kolmio.

GPipe-tulkinta (tyypit piilotettu pelottavuuden vähentämiseksi):

triangle = toGPUStream TriangleStrip $ zip pos color
    where 
        pos = [ sin x:.cos x:.() | x <- map ((pi*2/3) *) [0..2] ]
        color = [ x:.0:.(1-x):.() | x <- map (/ 3) [0..2] ]

shade = fmap (RGB . snd) . rasterizeFrontAndBack . fmap vs
    where 
        vs (x:.y:.(),color) = (x:.y:.0:.1:.(), color)

draw size = draw (shade triangle) clear
    where
        draw  = paintColor NoBlending (RGB $ vec True)  
        clear = newFrameBufferColor (RGB $ vec 0)

Ja sitten likimain sama käyttäen modernia OpenGL:ää.

Verteksivarjostin:

in vec2 pos;
in vec3 color;

out Fragment {
    vec3 color;
} frag;

void main() {
    gl_Position = vec4(pos, 0, 1);
    frag.color = color;
}

Pikselivarjostin:

in Fragment {
    vec3 color;
} frag;

out vec4 color;

void main() {
    color = vec4(frag.color, 1);
}

Ja ohjelmakoodi:

static void compile_shader(GLuint prog, GLenum type, const GLchar *src) {
    GLuint s = glCreateShader(type);
    glShaderSource(s, 1, &src, NULL);
    glCompileShader(s);
    glAttachShader(prog, s);
}

static void init_shader(const GLchar *vs, const GLchar *fs, GLuint *p) {
    *p = glCreateProgram();

    compile_shader(*p, GL_VERTEX_SHADER, vs);
    compile_shader(*p, GL_FRAGMENT_SHADER, fs);

    glLinkProgram(*p);
}

static void init_vbo(GLuint n, GLuint size, GLuint *vbo) {
    glGenBuffers(1, vbo);
    glBindBuffer(GL_ARRAY_BUFFER, *vbo);
    glBufferData(GL_ARRAY_BUFFER, size*n*sizeof(float), NULL, GL_DYNAMIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

static void init_attrib(
        GLuint vbo, 
        GLuint prog,
        GLuint size, 
        GLuint offset, 
        GLuint stride, 
        const char *name) {
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    GLuint attrib = glGetAttribLocation(prog, name);
    glVertexAttribPointer(attrib, size, GL_FLOAT, GL_FALSE, 
            stride*sizeof(float), (GLvoid*)(offset*sizeof(float)));
    glEnableVertexAttribArray(attrib);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

}

typedef struct {
    float pos[2];
    float color[3];
} vertex_t;

void init() {
    GLuint prog;
    GLuint vbo;
    init_shader(vertex_shader, fragment_shader, &prog);
    init_vbo(3, 5, &vbo);
    init_attrib(vbo, prog, 2, 0, 5, "pos");
    init_attrib(vbo, prog, 3, 2, 5, "color");

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    vertex_t* vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
    if (vertices) {
        for (int i = 0; i < 3; ++i) {
            vertex_t *v = &vertices[i];
            float c = i / 3.0f;
            v->pos[0] = sinf(i*M_PI*2.0f/3.0f);
            v->pos[1] = cosf(i*M_PI*2.0f/3.0f);
            v->color[0] = c;
            v->color[1] = 0.0f;
            v->color[2] = 1.0f - c;
        }
        glUnmapBuffer(GL_ARRAY_BUFFER);
    }
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void draw() {    
    glUseProgram(prog);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
}