Tutorial1: Rendering shapes with glDrawRangeElements, VAO, VBO, shaders (C++ / freeGLUT)

From OpenGL.org
Jump to: navigation, search

Overview

This is just a short tutorial about drawing primitives in OpenGL 3.3 without using deprecated functionality. We'll just draw 1 quad (as 2 triangles) and 1 triangle.

You can use any C++ compiler on any OS.

The code demonstrates a few key features :

All geometry is uploaded to IBO/VBO. We also wrap these up in a VAO. Rendering is done with glDrawRangeElements instead of the typical glDrawArrays or glDrawElements. The shaders are GL 3.3 specific although they can easily be ported to 3.2 or 3.1 or 3.0 or 2.1 and 2.0.

We are using freeGLUT which is similar to GLUT except it is open source and also up to date.

We'll be calling glutInitContextVersion(3, 3) and glutInitContextFlags(GLUT_CORE_PROFILE).

You can download from http://freeglut.sourceforge.net

We'll be using GLEW which will get all the GL function pointers for us. If you are new to GL, read about it at http://www.opengl.org/wiki/Getting_started#Accessing_OpenGL_functions

Download GLEW from http://glew.sourceforge.net

The Code

The entire code is a single C++ file. Copy it to your computer.

As for the shaders, there are 4 files. Copy them to your computer. Name them Shader1.vert, Shader1.frag, Shader2.vert, Shader2.frag.

Some things to note are that I don't know what would happen if you call glutInitContextVersion(3, 3) on a computer that doesn't support GL 3.3. The documentation doesn't say anything http://freeglut.sourceforge.net/docs/api.php

We are also using __glutCreateWindowWithExit so that we could clean up when the program exits, but it doesn't seem to work.

So we use __glutCreateWindowWithExit to create the window and it also sets up the GL 3.3 context. Then we init GLEW and this is what gets the function pointers. We then just get the GL version and run a dummy code that checks extensions. We then load all shaders, create the geometry and render the scene.

The vertex format for the quad is VC which means vertex and color. The vertices are float x, y, z. The color is unsigned int and RGBA. The shader outputs the RGBA color. You'll see a nicely colored quad.

The vertex format for the triangle is VNT which means vertex, normal and texcoord. We aren't using texcoords in this example. The normals are used for a simple lighting.

//In this example, we will use a few IBO/VBO and a few VAO.
//We will use glDrawRangeElements to render some shapes.
//We will use a couple of simple shaders. GL 3.3 shaders.
//We will create a 3.3 forward context with freeGLUT.
//
//We are using GLEW to get function pointers.
//
//As for the VBO, we will use an interleaved vertex format.
//Vertex, texcoords and normals.
//Vertex and color.
//
//As for the IBO, we will use 16 bit unsigned integers.
//
//http://freeglut.sourceforge.net
//http://glew.sourceforge.net
 
 
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
 
using namespace std;
 
 
 
 
#define BUFFER_OFFSET(i) ((void*)(i))
 
 
 
 
 
//Vertex, tex0
//
//SIZE : 4+4+4 +4+4 = 4*6 = 20 bytes
//It's better to make it multiple of 32
//32-20 = 12 bytes (of garbage should be added)
//12/4 = 3 floats should be added
struct TVertex_VT
{
        float   x, y, z;
        float   s0, t0;
        float   padding[3];
};
 
//Vertex, normal, tex0
//
//SIZE : 4+4+4 +4+4+4 +4+4 = 4*8 = 32 bytes
struct TVertex_VNT
{
        float   x, y, z;
        float   nx, ny, nz;
        float   s0, t0;
};
 
//Vertex, color
//
//SIZE : 4+4+4 +4 = 4*4 = 16 bytes
//It's better to make it multiple of 32
//32-16 = 16 bytes (of garbage should be added)
//16/4 = 4 floats should be added
struct TVertex_VC
{
        float   x, y, z;
        unsigned int    color;
        float   padding[4];
};
 
 
 
 
//Globals
 
//A quad
GLushort        pindex_quad[6];
TVertex_VC      pvertex_quad[4];
 
//A triangle
GLushort                pindex_triangle[3];
TVertex_VNT             pvertex_triangle[3];
 
//1 VAO for the quad
//1 VAO for the triangle
GLuint VAOID[2];
//1 IBO for the quad (Index Buffer Object)
//1 IBO for the triangle
GLuint IBOID[2];
//1 IBO for the quad (Vertex Buffer Object)
//1 IBO for the triangle
GLuint VBOID[2];
 
//1 shader for the quad
//1 shader for the triangle
GLuint  ShaderProgram[2];
GLuint  VertexShader[2];
GLuint  FragmentShader[2];
 
int ProjectionModelviewMatrix_Loc[2];           //The location of ProjectionModelviewMatrix in the shaders
 
// loadFile - loads text file into char* fname
// allocates memory - so need to delete after use
// size of file returned in fSize
std::string loadFile(const char *fname)
{
        std::ifstream file(fname);
        if(!file.is_open())
        {
                cout << "Unable to open file " << fname << endl;
                exit(1);
        }
 
        std::stringstream fileData;
        fileData << file.rdbuf();
        file.close();
 
        return fileData.str();
}
 
 
// printShaderInfoLog
// From OpenGL Shading Language 3rd Edition, p215-216
// Display (hopefully) useful error messages if shader fails to compile
void printShaderInfoLog(GLint shader)
{
        int infoLogLen = 0;
        int charsWritten = 0;
        GLchar *infoLog;
 
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen);
 
        if (infoLogLen > 0)
        {
                infoLog = new GLchar[infoLogLen];
                // error check for fail to allocate memory omitted
                glGetShaderInfoLog(shader, infoLogLen, &charsWritten, infoLog);
                cout << "InfoLog : " << endl << infoLog << endl;
                delete [] infoLog;
        }
}
 
 
void InitGLStates()
{
        glShadeModel(GL_SMOOTH);
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glReadBuffer(GL_BACK);
        glDrawBuffer(GL_BACK);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        glDepthMask(TRUE);
        glDisable(GL_STENCIL_TEST);
        glStencilMask(0xFFFFFFFF);
        glStencilFunc(GL_EQUAL, 0x00000000, 0x00000001);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);
        glClearColor(1.0, 0.0, 0.0, 0.0);
        glClearDepth(1.0);
        glClearStencil(0);
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
        glDisable(GL_DITHER);
        glActiveTexture(GL_TEXTURE0);
}
 
 
 
int LoadShader(const char *pfilePath_vs, const char *pfilePath_fs, bool bindTexCoord0, bool bindNormal, bool bindColor, GLuint &shaderProgram, GLuint &vertexShader, GLuint &fragmentShader)
{
        shaderProgram=0;
        vertexShader=0;
        fragmentShader=0;
 
        // load shaders & get length of each
        int vlen;
        int flen;
        std::string vertexShaderString = loadFile(pfilePath_vs);
        std::string fragmentShaderString = loadFile(pfilePath_fs);
        vlen = vertexShaderString.length();
        flen = fragmentShaderString.length();
 
        if(vertexShaderString.empty())
        {
                return -1;
        }
 
        if(fragmentShaderString.empty())
        {
                return -1;
        }
 
        vertexShader = glCreateShader(GL_VERTEX_SHADER);
        fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);    
 
        const char *vertexShaderCStr = vertexShaderString.c_str();
        const char *fragmentShaderCStr = fragmentShaderString.c_str();
        glShaderSource(vertexShader, 1, (const GLchar **)&vertexShaderCStr, &vlen);
        glShaderSource(fragmentShader, 1, (const GLchar **)&fragmentShaderCStr, &flen);
 
        GLint compiled;
 
        glCompileShader(vertexShader);
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compiled);
        if(compiled==FALSE)
        {
                cout << "Vertex shader not compiled." << endl;
                printShaderInfoLog(vertexShader);
 
                glDeleteShader(vertexShader);
                vertexShader=0;
                glDeleteShader(fragmentShader);
                fragmentShader=0;
 
                return -1;
        }
 
        glCompileShader(fragmentShader);
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compiled);
        if(compiled==FALSE)
        {
                cout << "Fragment shader not compiled." << endl;
                printShaderInfoLog(fragmentShader);
 
                glDeleteShader(vertexShader);
                vertexShader=0;
                glDeleteShader(fragmentShader);
                fragmentShader=0;
 
                return -1;
        }
 
        shaderProgram = glCreateProgram();
 
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
 
        glBindAttribLocation(shaderProgram, 0, "InVertex");
 
        if(bindTexCoord0)
                glBindAttribLocation(shaderProgram, 1, "InTexCoord0");
 
        if(bindNormal)
                glBindAttribLocation(shaderProgram, 2, "InNormal");
 
        if(bindColor)
                glBindAttribLocation(shaderProgram, 3, "InColor");
 
        glLinkProgram(shaderProgram);
 
        GLint IsLinked;
        glGetProgramiv(shaderProgram, GL_LINK_STATUS, (GLint *)&IsLinked);
        if(IsLinked==FALSE)
        {
                cout << "Failed to link shader." << endl;
 
                GLint maxLength;
                glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &maxLength);
                if(maxLength>0)
                {
                        char *pLinkInfoLog = new char[maxLength];
                        glGetProgramInfoLog(shaderProgram, maxLength, &maxLength, pLinkInfoLog);
                        cout << pLinkInfoLog << endl;
                        delete [] pLinkInfoLog;
                }
 
                glDetachShader(shaderProgram, vertexShader);
                glDetachShader(shaderProgram, fragmentShader);
                glDeleteShader(vertexShader);
                vertexShader=0;
                glDeleteShader(fragmentShader);
                fragmentShader=0;
                glDeleteProgram(shaderProgram);
                shaderProgram=0;
 
                return -1;
        }
 
        return 1;               //Success
}
 
void CreateGeometry()
{
        //A quad
        pvertex_quad[0].x=-0.8f;
        pvertex_quad[0].y=-0.5f;
        pvertex_quad[0].z=-0.9f;
        pvertex_quad[0].color=0xFFFFFFFF;
 
        pvertex_quad[1].x=0.0f;
        pvertex_quad[1].y=-0.5f;
        pvertex_quad[1].z=-0.9f;
        pvertex_quad[1].color=0xFFFF0000;
 
        pvertex_quad[2].x=-0.8f;
        pvertex_quad[2].y=0.5f;
        pvertex_quad[2].z=-0.9f;
        pvertex_quad[2].color=0xFF00FF00;
 
        pvertex_quad[3].x=0.0f;
        pvertex_quad[3].y=0.5f;
        pvertex_quad[3].z=-0.9f;
        pvertex_quad[3].color=0xFF0000FF;
 
        pindex_quad[0]=0;
        pindex_quad[1]=1;
        pindex_quad[2]=2;
        pindex_quad[3]=2;
        pindex_quad[4]=1;
        pindex_quad[5]=3;
 
        //The triangle
        pvertex_triangle[0].x=0.0f;
        pvertex_triangle[0].y=0.5f;
        pvertex_triangle[0].z=-1.0f;
        pvertex_triangle[0].nx=0.0f;
        pvertex_triangle[0].ny=0.0f;
        pvertex_triangle[0].nz=1.0f;
        pvertex_triangle[0].s0=0.0f;
        pvertex_triangle[0].t0=0.0f;
 
        pvertex_triangle[1].x=0.3f;
        pvertex_triangle[1].y=-0.5f;
        pvertex_triangle[1].z=-1.0f;
        pvertex_triangle[1].nx=0.0f;
        pvertex_triangle[1].ny=0.0f;
        pvertex_triangle[1].nz=1.0f;
        pvertex_triangle[1].s0=1.0f;
        pvertex_triangle[1].t0=0.0f;
 
        pvertex_triangle[2].x=0.8f;
        pvertex_triangle[2].y=0.5f;
        pvertex_triangle[2].z=-1.0f;
        pvertex_triangle[2].nx=0.0f;
        pvertex_triangle[2].ny=0.0f;
        pvertex_triangle[2].nz=1.0f;
        pvertex_triangle[2].s0=0.5f;
        pvertex_triangle[2].t0=1.0f;
 
        pindex_triangle[0]=0;
        pindex_triangle[1]=1;
        pindex_triangle[2]=2;
 
 
        //Create the IBO for the quad
        //16 bit indices
        glGenBuffers(1, &IBOID[0]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBOID[0]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*sizeof(GLushort), pindex_quad, GL_STATIC_DRAW);
 
        GLenum error=glGetError();
 
        //Create the IBO for the triangle
        //16 bit indices
        //We could have actually made one big IBO for both the quad and triangle.
        glGenBuffers(1, &IBOID[1]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBOID[1]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3*sizeof(GLushort), pindex_triangle, GL_STATIC_DRAW);
 
        error=glGetError();
 
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
        error=glGetError();
 
        //Create VBO for the quad
        glGenBuffers(1, &VBOID[0]);
        glBindBuffer(GL_ARRAY_BUFFER, VBOID[0]);
        glBufferData(GL_ARRAY_BUFFER, 4*sizeof(TVertex_VC), pvertex_quad, GL_STATIC_DRAW);
 
        error=glGetError();
 
        //Just testing
        glBindBuffer(GL_ARRAY_BUFFER, 0);
 
        //Create VBO for the triangle
        glGenBuffers(1, &VBOID[1]);
 
        glBindBuffer(GL_ARRAY_BUFFER, VBOID[1]);
        glBufferData(GL_ARRAY_BUFFER, 3*sizeof(TVertex_VNT), pvertex_triangle, GL_STATIC_DRAW);
 
        //Just testing
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        error=glGetError();
 
        //VAO for the quad *********************
        glGenVertexArrays(1, &VAOID[0]);
        glBindVertexArray(VAOID[0]);
 
        //Bind the VBO and setup pointers for the VAO
        glBindBuffer(GL_ARRAY_BUFFER, VBOID[0]);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TVertex_VC), BUFFER_OFFSET(0));
        glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(TVertex_VC), BUFFER_OFFSET(sizeof(float)*3));
        glEnableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
        glEnableVertexAttribArray(3);
 
        //Bind the IBO for the VAO
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBOID[0]);
 
        //Second VAO setup *******************
        //This is for the triangle
        glGenVertexArrays(1, &VAOID[1]);
        glBindVertexArray(VAOID[1]);
 
        //Bind the VBO and setup pointers for the VAO
        glBindBuffer(GL_ARRAY_BUFFER, VBOID[1]);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TVertex_VNT), BUFFER_OFFSET(0));
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TVertex_VNT), BUFFER_OFFSET(sizeof(float)*3));
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(TVertex_VNT), BUFFER_OFFSET(sizeof(float)*6));
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glEnableVertexAttribArray(2);
        glDisableVertexAttribArray(3);
 
        //Bind the IBO for the VAO
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBOID[1]);
 
        //Just testing
        glBindVertexArray(0);
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(3);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
 
void display()
{
        float projectionModelviewMatrix[16];
 
        //Just set it to identity matrix
        memset(projectionModelviewMatrix, 0, sizeof(float)*16);
        projectionModelviewMatrix[0]=1.0;
        projectionModelviewMatrix[5]=1.0;
        projectionModelviewMatrix[10]=1.0;
        projectionModelviewMatrix[15]=1.0;
 
        //Clear all the buffers
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
        //Bind the shader that we want to use
        glUseProgram(ShaderProgram[0]);
        //Setup all uniforms for your shader
        glUniformMatrix4fv(ProjectionModelviewMatrix_Loc[0], 1, FALSE, projectionModelviewMatrix);
        //Bind the VAO
        glBindVertexArray(VAOID[0]);
        //At this point, we would bind textures but we aren't using textures in this example
 
        //Draw command
        //The first to last vertex is 0 to 3
        //6 indices will be used to render the 2 triangles. This make our quad.
        //The last parameter is the start address in the IBO => zero
        glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL);
 
        //Bind the shader that we want to use
        glUseProgram(ShaderProgram[1]);
        //Setup all uniforms for your shader
        glUniformMatrix4fv(ProjectionModelviewMatrix_Loc[1], 1, FALSE, projectionModelviewMatrix);
        //Bind the VAO
        glBindVertexArray(VAOID[1]);
        //At this point, we would bind textures but we aren't using textures in this example
 
        //Draw command
        //The first to last vertex is 0 to 3
        //3 indices will be used to render 1 triangle.
        //The last parameter is the start address in the IBO => zero
        glDrawRangeElements(GL_TRIANGLES, 0, 3, 3, GL_UNSIGNED_SHORT, NULL);
 
        glutSwapBuffers();
}
 
void reshape(int w, int h)
{
        glViewport(0, 0, w, h);
}
 
void ExitFunction(int value)
{
        cout<<"Exit called."<<endl;
 
        glBindVertexArray(0);
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(3);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
        glUseProgram(0);
 
        glDeleteBuffers(1, &IBOID[0]);
        glDeleteBuffers(1, &IBOID[1]);
        glDeleteBuffers(1, &VBOID[0]);
        glDeleteBuffers(1, &IBOID[1]);
        glDeleteVertexArrays(1, &VAOID[0]);
        glDeleteVertexArrays(1, &VAOID[1]);
 
        glDetachShader(ShaderProgram[0], VertexShader[0]);
        glDetachShader(ShaderProgram[0], FragmentShader[0]);
        glDeleteShader(VertexShader[0]);
        glDeleteShader(FragmentShader[0]);
        glDeleteProgram(ShaderProgram[0]);
 
        glDetachShader(ShaderProgram[1], VertexShader[1]);
        glDetachShader(ShaderProgram[1], FragmentShader[1]);
        glDeleteShader(VertexShader[1]);
        glDeleteShader(FragmentShader[1]);
        glDeleteProgram(ShaderProgram[1]);
}
 
int main (int argc, char* argv[])
{
        int i;
        int NumberOfExtensions;
        int OpenGLVersion[2];
 
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        //We want to make a GL 3.3 context
        glutInitContextVersion(3, 3);
        glutInitContextFlags(GLUT_CORE_PROFILE);
        glutInitWindowPosition(100, 50);
        glutInitWindowSize(600, 600);
        __glutCreateWindowWithExit("GL 3.3 Test", ExitFunction);
 
        //Currently, GLEW uses glGetString(GL_EXTENSIONS) which is not legal code
        //in GL 3.3, therefore GLEW would fail if we don't set this to TRUE.
        //GLEW will avoid looking for extensions and will just get function pointers for all GL functions.
        glewExperimental=TRUE;
        GLenum err=glewInit();
        if(err!=GLEW_OK)
        {
                //Problem: glewInit failed, something is seriously wrong.
                cout<<"glewInit failed, aborting."<<endl;
                exit(1);
        }
 
        //The old way of getting the GL version
        //This will give you something like 3.3.2895 WinXP SSE
        //where the 3.3 would be the version number and the rest is vendor
        //dependent information.
        //In this case, 2895 is a build number.
        //Then the OS : WinXP
        //Then CPU features such as SSE
        cout<<"OpenGL version = "<<glGetString(GL_VERSION)<<endl;
 
        //This is the new way for getting the GL version.
        //It returns integers. Much better than the old glGetString(GL_VERSION).
        glGetIntegerv(GL_MAJOR_VERSION, &OpenGLVersion[0]);
        glGetIntegerv(GL_MINOR_VERSION, &OpenGLVersion[1]);
        cout<<"OpenGL major version = "<<OpenGLVersion[0]<<endl;
        cout<<"OpenGL minor version = "<<OpenGLVersion[1]<<endl<<endl;
 
        //The old method to get the extension list is obsolete.
        //You must use glGetIntegerv and glGetStringi
        glGetIntegerv(GL_NUM_EXTENSIONS, &NumberOfExtensions);
 
        //We don't need any extensions. Useless code.
        for(i=0; i<NumberOfExtensions; i++)
        {
                const GLubyte *ccc=glGetStringi(GL_EXTENSIONS, i);
        }
 
        InitGLStates();
 
        if(LoadShader("Shader1.vert", "Shader1.frag", false, false, true, ShaderProgram[0], VertexShader[0], FragmentShader[0])==-1)
        {
                exit(1);
        }
        else
        {
                ProjectionModelviewMatrix_Loc[0]=glGetUniformLocation(ShaderProgram[0], "ProjectionModelviewMatrix");
        }
 
        if(LoadShader("Shader2.vert", "Shader2.frag", true, true, false, ShaderProgram[1], VertexShader[1], FragmentShader[1])==-1)
        {
                exit(1);
        }
        else
        {
                ProjectionModelviewMatrix_Loc[1]=glGetUniformLocation(ShaderProgram[1], "ProjectionModelviewMatrix");
        }
 
        CreateGeometry();
 
        glutDisplayFunc(display);
        glutReshapeFunc(reshape);
 
        glutMainLoop();
        return 0;
}

Shader1.vert : notice that we have 1 uniform called ProjectionModelviewMatrix. We set this to identity in our C++ code.

The color has the smooth qualifier which will instruct the GPU to do bilinear interpolation across the polygon.

InVertex and InColor are attributes which are setup with a call to glBindAttribLocation. After that, you must link the shader and check for success (glLinkProgram and glGetProgramiv(shaderProgram, GL_LINK_STATUS, (GLint *)&IsLinked))

//[VERTEX SHADER]
#version 330
 
in vec3 InVertex;
in vec4 InColor;
 
 
smooth out vec4 Color;
 
uniform mat4 ProjectionModelviewMatrix;
 
 
void main()
{
        gl_Position = ProjectionModelviewMatrix * vec4(InVertex, 1.0);
        Color = InColor;
}

Shader1.frag : here we have the input Color. We just write it out to FragColor. FragColor is our own output variable which GL automatically bind to output 0, therefore we don't need to set it up from our C++ side of the code.

//[FRAGMENT SHADER]
#version 330
 
 
smooth in vec4 Color;
 
 
out vec4 FragColor;
 
 
void main()
{
        FragColor = Color;
}

Shader2.vert

//[VERTEX SHADER]
#version 330
 
in vec3 InVertex;
in vec3 InNormal;
 
 
smooth out vec3 LightVector0;
smooth out vec3 EyeNormal;
 
uniform mat4 ProjectionModelviewMatrix;
 
 
void main()
{
        gl_Position = ProjectionModelviewMatrix * vec4(InVertex, 1.0);
 
        LightVector0 = vec3(1.0, 1.0, 1.0);
        EyeNormal = InNormal;
}

Shader2.frag

//[FRAGMENT SHADER]
#version 330
 
smooth in vec3 LightVector0;
smooth in vec3 EyeNormal;
 
out vec4 FragColor;
 
 
void main()
{
        vec3 eyeNormal;
        vec3 lightVector;
        float dotProduct;
 
        eyeNormal = normalize(EyeNormal);
        lightVector = normalize(LightVector0);
 
        dotProduct = dot(eyeNormal, lightVector);
 
        FragColor = vec4(dotProduct);
}