Modelo de iluminación OpenGL Phong

1. Introducción

La iluminación en el mundo real es extremadamente compleja y se verá afectada por muchos factores, que no pueden ser simulados por nuestro poder de cómputo limitado.La estructura principal del modelo de iluminación de Feng consta de tres componentes: medio ambiente (ambiente), reflexión difusa (difusa) y iluminación especular (especular) . La siguiente imagen muestra cómo se ven estos componentes de iluminación.

  • Iluminación ambiental: incluso en la oscuridad, suele haber algo de luz en el mundo (luna, luces lejanas), por lo que los objetos casi nunca están completamente oscuros. Para simular esto, usaremos una constante de iluminación ambiental, que siempre le dará algo de color a los objetos.
  • Iluminación difusa (Diffuse Lighting): simula el impacto direccional de las fuentes de luz en los objetos (Impacto direccional). Es el componente visualmente más significativo del modelo de iluminación Phong. Cuanto más cerca esté una parte de un objeto de una fuente de luz, más brillante será.
  • Iluminación especular: simula los puntos brillantes que aparecen en los objetos brillantes. El color de la luz especular se acerca más al color de la luz que al color del objeto.

2. Iluminación ambiental

Usamos un pequeño color constante (iluminación) que se agrega al color final del fragmento del objeto, de modo que incluso si no hay una fuente de luz directa en la escena, parecerá tener algo de luz difusa.

Agregar iluminación ambiental a una escena es muy simple. Multiplicamos el color de la luz por un pequeño factor ambiental constante, multiplicamos por el color del objeto y usamos el resultado final como el color del fragmento:

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

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

3. Iluminación difusa

La iluminación ambiental por sí sola no brinda los resultados más interesantes, pero la iluminación difusa puede comenzar a tener un impacto visual notable en los objetos. La iluminación difusa hace que los fragmentos del objeto más cerca de la dirección de la luz obtengan más brillo de la fuente de luz.

Hay una fuente de luz arriba, y la luz de esta cae sobre un fragmento del objeto. Necesitamos medir en qué ángulo este rayo golpea este fragmento. Si el rayo de luz es perpendicular a la superficie del objeto, el efecto de este rayo de luz sobre el objeto es más brillante. Para medir el ángulo entre el rayo y el fragmento, usamos algo llamado vector normal , que es un vector perpendicular a la superficie del fragmento (que se muestra aquí con la flecha amarilla), el ángulo entre estos dos vectores se calcula fácilmente mediante un punto sale el producto. 

Por lo tanto, el cálculo de la iluminación difusa requiere lo siguiente:

  • Vector Normal: Un vector perpendicular a la superficie del vértice.
  • Rayo dirigido: El vector de dirección que es la diferencia de vectores entre la posición de la fuente de luz y la posición del fragmento. Para calcular este rayo, necesitamos el vector de posición de la luz y el vector de posición del fragmento.

El siguiente es el cálculo de la iluminación difusa.

El vector de dirección de la luz lightDir es la diferencia de vectores entre el vector de posición de la fuente de luz y el vector de posición del fragmento. Hacemos un producto escalar de los vectores norm y lightDir para calcular la influencia difusa real de la fuente de luz en el fragmento actual. El valor resultante se multiplica por el color de la luz para obtener la componente difusa . Cuanto mayor sea el ángulo entre los dos vectores, menor será la componente difusa; si el ángulo entre los dos vectores es mayor de 90 grados, el resultado del producto escalar se volverá negativo, lo que hará que la componente difusa se vuelva negativa. Para hacer esto, use la función max para devolver el mayor de los dos parámetros, garantizando así que la componente difusa no se vuelva negativa.

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

Los cálculos en el sombreador de fragmentos se realizan en coordenadas del espacio mundial. Por lo tanto, también deberíamos convertir los vectores normales en coordenadas del espacio mundial. Si la matriz del modelo realiza un escalado no uniforme, los cambios de vértice harán que los vectores normales ya no sean perpendiculares a la superficie y la iluminación se interrumpirá. El truco para corregir este comportamiento es usar una matriz de modelo personalizada para vectores normales. Esta matriz se llama matriz normal .

Utilice el siguiente método:

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

4. Iluminación especular

Al igual que la iluminación difusa, la iluminación especular también depende del vector de dirección de la luz y del vector normal del objeto, pero también depende de la dirección de visualización, es decir, desde qué dirección el jugador mira el fragmento. La iluminación especular depende de las propiedades reflectantes de la superficie. Si pensamos en la superficie de un objeto como un espejo, entonces el lugar donde la luz especular es más fuerte es donde vemos la luz reflejada desde la superficie. Puedes ver el efecto en la siguiente imagen:

Calculamos el vector de reflexión (R) cambiando la dirección de la luz entrante de acuerdo con el vector normal . Luego calculamos la diferencia de ángulo entre el vector de reflexión y la dirección de visualización , cuanto menor sea el ángulo entre ellos, mayor será el efecto de la luz especular. El efecto resultante es que cuando miramos en la dirección del reflejo de la luz incidente sobre la superficie, veremos un pequeño resalte. 

    //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;
  • En primer lugar, definimos una variable de Intensidad especular para dar al brillo especular un color de brillo medio para que no tenga un impacto excesivo.
  • Calcule el vector de dirección de la vista (viewDir) y el vector de reflexión correspondiente a lo largo del eje normal (reflectDir)
  • Calcula el producto escalar de la dirección de la vista y la dirección del reflejo (y asegúrate de que no sea negativo), luego elévalo a la potencia de 32. Este 32 es la reflectividad del resaltado (Brillo)

Cuanto mayor sea la reflectividad del objeto, mayor será la capacidad de reflejar la luz, menos se dispersa y más pequeño será el punto destacado. En la imagen a continuación, puede ver los efectos visuales de diferentes niveles de reflectividad:

Lo último que queda es agregarlo a los componentes ambientales y difusos, y multiplicar el resultado por el color del objeto:

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

5. Ejemplos

#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();
}

Sombreadores de vértices y fragmentos

#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. Código fuente completo

implementación de qt+opengl

https://download.csdn.net/download/wzz953200463/87893447 icono-predeterminado.png?t=N4P3https://download.csdn.net/download/wzz953200463/87893447

Supongo que te gusta

Origin blog.csdn.net/wzz953200463/article/details/131139368
Recomendado
Clasificación