How to do it…

Let's get started by following these steps:

  1. Again, we will use the previous example and add a point light near the spinning cube. Open up renderwindow.h and add another variable called vbo_normals to the file:
QOpenGLBuffer* vbo_uvs;
QOpenGLBuffer* vbo_normals;
QOpenGLTexture* texture;
  1. Open renderwindow.cpp and add another array called normals to the initializeGL() function:
GLfloat normals[] = {
0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f
};
  1. Initialize the vbo_normals VBO in initializeGL() by adding the following code:
vbo_normals = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
vbo_normals->create();
vbo_normals->setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo_normals->bind();
vbo_normals->allocate(normals, sizeof(normals) * sizeof(GLfloat));
  1. Since the shader we will be writing this time will be much longer than what we used in the previous examples, let's move the shader code over to text files and load them into the program by calling addShaderFromSourceFile():
shaderProgram = new QOpenGLShaderProgram(this);
shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, qApp->applicationDirPath() + "/vertex.txt");
shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, qApp->applicationDirPath() + "/fragment.txt");
shaderProgram->link();
  1. Once you are done with that, add the following code to the paintEvent() function to pass the normals VBO over to the shader:
vbo_normals->bind();
shaderProgram->bindAttributeLocation("normalAttr", 2);
shaderProgram->enableAttributeArray(2);
shaderProgram->setAttributeBuffer(2, GL_FLOAT, 0, 3);
  1. Let's open up the two text files we just created that contain the shader code. First, we need to make some changes to the vertex shader, like so:
#version 330 core
layout(location = 0) in vec3 posAttr;
layout(location = 1) in vec2 uvAttr;
layout(location = 2) in vec3 normalAttr;
uniform mat4 matrix;
out vec3 fragPos;
out vec2 fragUV;
out vec3 fragNormal;

void main() {
fragPos = posAttr;
fragUV = uvAttr;
fragNormal = normalAttr;
gl_Position = matrix * vec4(posAttr, 1.0);
}
  1. We will also make some changes to the fragment shader. We will create a function called calcPointLight() in the shader code:
#version 330 core
in vec3 fragPos;
in vec2 fragUV;
in vec3 fragNormal;
uniform sampler2D tex;
out vec4 col;

vec3 calcPointLight() {
vec4 texCol = texture(tex, fragUV);
vec3 lightPos = vec3(1.0, 2.0, 1.5);
vec3 lightDir = normalize(lightPos - fragPos);
vec4 lightColor = vec4(1.0, 1.0, 1.0, 1.0);
float lightIntensity = 1.0;
  1. Continuing from the preceding code, we calculated the lighting using calcPointLight() and output the resulting fragment to the col variable, as follows:
    // Diffuse
float diffuseStrength = 1.0;
float diff = clamp(dot(fragNormal, lightDir), 0.0, 1.0);
vec4 diffuse = diffuseStrength * diff * texCol * lightColor * lightIntensity;
return diffuse;
}

void main() {
vec3 finalColor = calcPointLight();
col = vec4(finalColor, 1.0);
}
  1. If you compile and run the program now, you should see the lighting in action: