Here is my OpenGL "Hello, Triangles" program. I tried to avoid all the deprecated functions and put the triangles in a buffer. Even in two different ways.
It is a bit bloated by the shader compile status checking. But this error info is very helpful. It is why I mention the original author whose code got me started.
Maybe the #version 460 in the shaders should be reduced? First I also had compatibility (to access gl_Vertex built-in easily), but then I changed to a real in variable.
There are some subtle differences between the ...pointer20 and ...format46 version I don't really understand.
The official wiki on khronos.org also compares these two methods. In one place they mention glVertexAttribBinding(index, index). Yes, but which index? I tried to optically sort these out with BUF, VB and VAA.
Is this non-deprecated and "modern" OpenGL?
And what do you think of it? Thank you for your interest.
/* OpenGL Hello Triangles: minimal program with shaders and data buffers.
Draws two overlapping (piercing, with depth) triangles.
No _deprecated_ functions.
/* Based on original by Jan Wedekind (shader integration) " */
/* Libs: -lGL -lGLEW -lglut */
#include <stdio.h>
#include <GL/glew.h>
#include <GL/glut.h>
/* GL objects/binds. 'VAA' must match the vertex shader's 'in' location (default 0) */
/* 'VB' is 'vertex buffer binding point index'; 'VAA' is 'vertex sttribute array' */
int BUF;
const int VB = 3, VAA = 0;
int width = 450, height = 300;
/* Two triangles already in final clip space coords */
/* 'xyz' are the middle three; the '6' will be skipped by ...Format() */
/* 'w' (after xyz) is used for color */
float vertices[] = {
6, 1, 1, 0.2, 0,
6, -1,-1, 0.6, 0.1,
6, -1, 1, 0.5, 0.3,
6, 0.3, -.8, .9, 1,
6, 0, 0.9, .1, .9, /* z=0.1 --> near tip of second triangle */
6, -.5, -.8, .9, .8,
};
/* Same two, but the extra '6' is after the actual vertex data. This way ...Pointer() can also use this array (version II)*/
float vertices_2[] = {
1, 1, 0.2, 0, 6,
-1,-1, 0.6, 0.1, 6,
-1, 1, 0.5, 0.3, 6,
0.3, -.8, .9, 1, 6,
0, 0.9, 0.1, .9, 6,
-.5, -.8, .9, .8, 6,
};
/* Shaders as strings. Ugly but practical */
/* Vertex shader. Invents some color from the 'w' column
xyz position is just passed on (no perspective) */
const char *vertex_src = "#version 460\n"
" in vec4 Vertex; \n\
out vec4 Vcolor; \n\
void main() { \n\
gl_Position = vec4(Vertex.xyz, 1); \n\
Vcolor = vec4(Vertex.y, .4, Vertex.w, 0.5); \n\
} \n\
";
/* Fragment shader. Applies the color (interpolated) */
const char *fragment_src = "#version 460\n"
" in vec4 Vcolor; \n\
void main() { \n\
gl_FragColor = Vcolor; \n\
} \n\
";
/* Shader compile/link status feedback */
char buffer[1024];
int param;
void
shader_status(int shader) {
glGetShaderiv(shader, GL_COMPILE_STATUS, ¶m);
if (param)
return;
glGetShaderInfoLog(shader, 1024, NULL, buffer);
fprintf(stderr, "Shader compile status:\n%s\n", buffer);
}
void
program_status(int prog) {
glGetProgramiv(prog, GL_LINK_STATUS, ¶m);
if (param)
return;
glGetProgramInfoLog(prog, 1024, NULL, buffer);
fprintf(stderr, "Program link status:\n%s\n", buffer);
}
/* Shader object from string; compile and check */
int
make_shader(const char *src, int sh_typ) {
int shader = glCreateShader(sh_typ);
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);
shader_status(shader);
return shader;
}
/* Program object from a vertex and a fragment shader; link and check */
int
make_program(int vert, int frag) {
int prog = glCreateProgram();
glAttachShader(prog, vert);
glAttachShader(prog, frag);
glLinkProgram(prog);
program_status(prog);
return prog;
}
/* Prepare Data 2nd version: Traditional with BindBuffer(), VertexAttribPointer() */
void
init_bufs_pointer20(void) {
glGenBuffers(1, &BUF);
glBindBuffer(GL_ARRAY_BUFFER, BUF);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_2), vertices_2, GL_STATIC_DRAW);
glVertexAttribPointer(VAA, 4, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void *)0); //..., stride, pointer)
glEnableVertexAttribArray(VAA);
}
/* Prepare data: New with BindVertexBuffer(), VertexAttribFormat(), NamedBufferData() */
/* VertexAttribBinding() is done outside (= final step) */
void
init_bufs_format46(void) {
glCreateBuffers(1, &BUF);
glNamedBufferData(BUF, sizeof vertices, vertices, GL_STATIC_DRAW);
glBindVertexBuffer(VB, BUF, 0, 5*sizeof(float)); //..., offset, stride)
glVertexAttribFormat(VAA, 4, GL_FLOAT, GL_FALSE, 1*sizeof(float)); // ..., reloffset) -> skip first float
glEnableVertexAttribArray(VAA);
}
/* glut callbacks */
void
display(void) {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 2*3);
glutSwapBuffers();
}
void
reshape(int width, int height) {
glViewport(0, 0, width, height);
}
int
main(int argc, char** argv) {
/* Utils for GL */
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(width, height);
glutCreateWindow("mini");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glewInit();
/* Install shaders */
int VERT_SH = make_shader(vertex_src, GL_VERTEX_SHADER);
int FRAG_SH = make_shader(fragment_src, GL_FRAGMENT_SHADER);
int PROG = make_program(VERT_SH, FRAG_SH);
glUseProgram(PROG);
/* Define triangle data ("vertex array organization")*/
init_bufs_format46();
/* Activate attribs-buffer combo for drawing */
glVertexAttribBinding(VAA, VB);
/* Alternate data method */
//init_bufs_pointer20();
glClearColor(0,0,0,0);
/* Blend _or_ Cover ? And if yes, how ?*/
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glBlendEquation(GL_FUNC_SUBTRACT);
glEnable(GL_DEPTH_TEST);
/* invert depth */
//glDepthFunc(GL_GREATER);
//glClearDepth(0);
glutMainLoop();
return 0;
}
And this is how it looks. Minimal color and minimal 3D. Much more fancy than a single unicolored triangle.

