OpenGL Phong lighting model

1 Introduction

The lighting in the real world is extremely complex and will be affected by many factors, which cannot be simulated by our limited computing power. The main structure of Feng's lighting model consists of three components: environment (Ambient), diffuse reflection ( Diffuse) and specular (Specular) lighting. The image below shows what these lighting components look like.

  • Ambient Lighting: Even in darkness, there is usually still some light in the world (moon, distant lights), so objects are almost never completely dark. To simulate this, we'll use an ambient lighting constant, which will always give objects some color.
  • Diffuse Lighting (Diffuse Lighting): Simulates the directional impact of light sources on objects (Directional Impact). It is the most visually significant component of the Phong lighting model. The closer a part of an object is to a light source, the brighter it will be.
  • Specular Lighting: Simulates the bright spots that appear on shiny objects. The color of the specular light is more towards the color of the light than the color of the object.

2. Ambient lighting

We use a small constant (lighting) color that is added to the final color of the object fragment, so that even if there is no direct light source in the scene it will appear to have some diffuse light.

Adding ambient lighting to a scene is very simple. We multiply the light's color by a small constant ambient factor, multiply by the object's color, and use the final result as the fragment's color:

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}

3. Diffuse lighting

Ambient lighting alone doesn't provide the most interesting results, but diffuse lighting can start to have a noticeable visual impact on objects. Diffuse lighting makes the fragments on the object closer to the light direction get more brightness from the light source.

There is a light source above, and the light from it falls on a fragment of the object. We need to measure at what angle this ray hits this fragment. If the ray of light is perpendicular to the surface of the object, the effect of this beam of light on the object is brighter. To measure the angle between the ray and the fragment, we use something called the normal vector , which is a vector perpendicular to the surface of the fragment (shown here by the yellow arrow), the angle between these two vectors is easily calculated by dot product come out. 

Therefore, the calculation of diffuse lighting requires the following:

  • Normal Vector: A vector perpendicular to the surface of the vertex.
  • Directed Ray: The direction vector that is the vector difference between the light source's position and the fragment's position. To calculate this ray, we need the light's position vector and the fragment's position vector.

The following is the calculation of diffuse lighting.

The light's direction vector lightDir is the vector difference between the light source's position vector and the fragment's position vector. We do a dot product of the norm and lightDir vectors to calculate the actual diffuse influence of the light source on the current fragment. The resulting value is multiplied by the color of the light to get the diffuse component . The larger the angle between the two vectors, the smaller the diffuse component; if the angle between the two vectors is greater than 90 degrees, the result of the dot product will become negative, which will cause the diffuse component to become negative. To do this, use the max function to return the larger of the two parameters, thus guaranteeing that the diffuse component does not become negative.

    //diffuse
    vec3 norm = normalize(outNormal);    //法向量标准化
    vec3 lightDir = normalize(lightPos - FragPos);//光的方向向量是光源位置向量与片段位置向量之间的向量差
    float diff = max(dot(norm, lightDir), 0.0);//点乘
    vec3 diffuse = diff * lightColor;

Computations in the fragment shader are performed in world space coordinates. So, we should convert normal vectors to world space coordinates as well. If the model matrix performs a non-uniform scaling, the vertex changes will cause the normal vectors to no longer be perpendicular to the surface , and the lighting will be broken. The trick to fix this behavior is to use a custom model matrix for normal vectors. This matrix is ​​called the normal matrix .

Use the following method:

outNormal = mat3(transpose(inverse(model))) * normal;

4. Specular lighting

Like diffuse lighting, specular lighting also depends on the direction vector of the light and the normal vector of the object, but it also depends on the viewing direction, ie from which direction the player is looking at the fragment. Specular lighting depends on the reflective properties of the surface. If we think of the surface of an object as a mirror, then the place where the specular light is strongest is where we see light reflected from the surface. You can see the effect in the image below:

We calculate the reflection vector (R) by flipping the direction of the incoming light according to the normal vector . Then we calculate the angle difference between the reflection vector and the viewing direction , the smaller the angle between them, the more the effect of specular light. The resulting effect is that when we look in the direction of the reflection of the incident light on the surface, we will see a little highlight. 

    //specular
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;
  • First, we define a Specular Intensity variable to give the specular highlight a medium brightness color so that it doesn't have an excessive impact.
  • Calculate the view direction vector (viewDir) and the corresponding reflection vector along the normal axis (reflectDir)
  • Calculate the dot product of the view direction and the reflection direction (and make sure it's not negative), then raise it to the power of 32. This 32 is the reflectivity of the highlight (Shininess)

The higher the reflectivity of the object, the stronger the ability to reflect light, the less it scatters, and the smaller the highlight point will be. In the image below, you can see the visual effects of different levels of reflectivity:

The last thing left is to add it to the ambient and diffuse components, and multiply the result by the object's color:

vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);

5. Examples

#include "myopenglwidget.h"
#include <QMatrix4x4>
#include <QTime>
#include <QTimer>
#include <math.h>
#include <QDebug>

float vertices[] = {
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,

    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
};

GLuint indices[] = {
    0, 1, 3,
    1, 2, 3
};


GLuint VBO, VAO,EBO,lightVAO;
GLuint shaderProgram;

QVector3D lightPos(1.2f,1.0f,2.0f);
QVector3D lightColor(1.0f,1.0f,1.0f);
QVector3D objectColor(1.0f,0.5f,0.31f);

QTimer *timer;
QTime gtime;

QVector<QVector3D> cubePositions = {
  QVector3D( 0.0f,  0.0f,  0.0f),
  QVector3D( 2.0f,  5.0f, -15.0f),
  QVector3D(-1.5f, -2.2f, -2.5f),
  QVector3D(-3.8f, -2.0f, -12.3f),
  QVector3D( 2.4f, -0.4f, -3.5f),
  QVector3D(-1.7f,  3.0f, -7.5f),
  QVector3D( 1.3f, -2.0f, -2.5f),
  QVector3D( 1.5f,  2.0f, -2.5f),
  QVector3D( 1.5f,  0.2f, -1.5f),
  QVector3D(-1.3f,  1.0f, -1.5f)
};

float fov = 45.0f;

MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
    cameraPos = QVector3D( 0.0f,  0.0f,  5.0f);//摄像机位置
    cameraTarget = QVector3D( 0.0f,  0.0f,  0.0f);//摄像机看到的位置
    cameraDirection = QVector3D(cameraPos - cameraTarget);//摄像机的方向
    cameraDirection.normalize();

    up = QVector3D(0.0f,  1.0f,  0.0f);
    cameraRight = QVector3D::crossProduct(up,cameraDirection);//两个向量叉乘的结果会同时垂直于两向量,因此我们会得到指向x轴正方向的那个向量
    cameraRight.normalize();

    cameraUp = QVector3D::crossProduct(cameraDirection,cameraRight);
    cameraFront = QVector3D( 0.0f,  0.0f,  -1.0f);

    timer = new QTimer();
    timer->start(50);
    gtime.start();
    connect(timer,&QTimer::timeout,[=]{
        update();
    });

    setFocusPolicy(Qt::StrongFocus);
    //setMouseTracking(true);
}

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    m_program = new QOpenGLShaderProgram();
    m_program->addShaderFromSourceFile(QOpenGLShader::Vertex,":/shapes.vert");
    m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/shapes.frag");
    m_program->link();
    qDebug()<<m_program->log();

    m_lightProgram = new QOpenGLShaderProgram();
    m_lightProgram->addShaderFromSourceFile(QOpenGLShader::Vertex,":/light.vert");
    m_lightProgram->addShaderFromSourceFile(QOpenGLShader::Fragment,":/light.frag");
    m_lightProgram->link();


    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);//绑定VAO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把顶点数据复制到缓冲的内存中GL_STATIC_DRAW :数据不会或几乎不会改变。

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
    glEnableVertexAttribArray(0);

    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);


    glGenVertexArrays(1, &lightVAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(lightVAO);//绑定VAO
    glBindBuffer(GL_ARRAY_BUFFER, VBO);//顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把顶点数据复制到缓冲的内存中GL_STATIC_DRAW :数据不会或几乎不会改变。

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);//解绑VAO

    m_program->bind();
    m_program->setUniformValue("objColor",objectColor);
    m_program->setUniformValue("lightColor",lightColor);
    m_program->setUniformValue("lightPos",lightPos);
    m_program->setUniformValue("viewPos",cameraPos);

    m_lightProgram->bind();
    m_lightProgram->setUniformValue("lightColor",lightColor);
}

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    QMatrix4x4 model;
    QMatrix4x4 view;
    float time = gtime.elapsed()/50.0;
    //int time = QTime::currentTime().msec();

    QMatrix4x4 projection;
    projection.perspective(fov,(float)( width())/(height()),0.1,100);

    view.lookAt(cameraPos,cameraPos + cameraFront,up);

    m_program->bind();
    m_program->setUniformValue("projection",projection);
    m_program->setUniformValue("view",view);

    glBindVertexArray(VAO);//绑定VAO

    model.rotate(time,1.0f,5.0f,0.5f);
    m_program->setUniformValue("model",model);
    glDrawArrays(GL_TRIANGLES,0,36);

    m_lightProgram->bind();
    m_lightProgram->setUniformValue("projection",projection);
    m_lightProgram->setUniformValue("view",view);

    model.setToIdentity();
    model.translate(lightPos);
    model.rotate(1.0f,1.0f,5.0f,0.5f);
    model.scale(0.2);
    m_lightProgram->setUniformValue("model",model);
    glBindVertexArray(lightVAO);//绑定VAO
    glDrawArrays(GL_TRIANGLES,0,36);

//    foreach(auto pos , cubePositions)
//    {
//        model.setToIdentity();
//        model.translate(pos);
//        //model.rotate(time,1.0f,5.0f,3.0f);
//        m_program->setUniformValue("model",model);
//        glDrawArrays(GL_TRIANGLES,0,36);
//    }

}

void MyOpenGLWidget::resizeGL(int w, int h)
{

}

void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    qDebug()<<event->key();
    cameraSpeed = 2.5 * 100 / 1000.0;
    switch (event->key()) {
    case Qt::Key_W:{
        cameraPos += cameraSpeed * cameraFront;
    }
        break;
    case Qt::Key_S:{
        cameraPos -= cameraSpeed * cameraFront;
    }
        break;
    case Qt::Key_A:{
        cameraPos -= cameraSpeed * cameraRight;
    }
        break;
    case Qt::Key_D:{
        cameraPos += cameraSpeed * cameraRight;
    }
        break;
    default:
        break;

    }
    update();
}
float PI = 3.1415926;
QPoint deltaPos;
void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    static float yaw = -90;
    static float pitch = 0;
    static QPoint lastPos(width()/2,height()/2);
    auto currentPos = event->pos();
    deltaPos = currentPos-lastPos;
    lastPos=currentPos;
    float sensitivity = 0.1f;
    deltaPos *= sensitivity;
    yaw += deltaPos.x();
    pitch -= deltaPos.y();
    if(pitch > 89.0f) pitch = 89.0f;
    if(pitch < -89.0f) pitch = -89.0f;
    cameraFront.setX(cos(yaw*PI/180.0) * cos(pitch *PI/180));
    cameraFront.setY(sin(pitch*PI/180));
    cameraFront.setZ(sin(yaw*PI/180) * cos(pitch *PI/180));
    cameraFront.normalize();
    update();
}

void MyOpenGLWidget::wheelEvent(QWheelEvent *event)
{
    if(fov >= 1.0f && fov <= 75.0f)
        fov -= event->angleDelta().y()/120;
    if(fov <= 1.0f)
        fov = 1.0f;
    if(fov >= 75.0f)
        fov = 75.0f;

    update();
}

Vertex and Fragment Shaders

#version 330 core

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 outNormal;
out vec3 FragPos;

void main()
{
    outNormal = mat3(transpose(inverse(model))) * normal;
    FragPos = vec3(model * vec4(position,1.0));
    gl_Position = projection * view * model * vec4(position,1.0);
}




#version 330 core

out vec4 color;
uniform vec3 objColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 viewPos;

in vec3 outNormal;
in vec3 FragPos;

void main()
{
    //ambinet
    float ambinetStrength = 0.1;
    vec3 ambient = ambinetStrength * lightColor;

    //diffuse
    vec3 norm = normalize(outNormal);
    vec3 lightDir = normalize(lightPos - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    //specular
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;

    vec3 result = (ambient + diffuse + specular) * objColor;

    color = vec4(result,1.0f);
}

6. Complete source code

qt+opengl implementation

https://download.csdn.net/download/wzz953200463/87893447icon-default.png?t=N4P3https://download.csdn.net/download/wzz953200463/87893447

Guess you like

Origin blog.csdn.net/wzz953200463/article/details/131139368