我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的颜色。换句话说,那些不能被物体所吸收的颜色就是我们能够感知到的物体的颜色。
例如,太阳光能被看见的白光其实是由许多不同的颜色组合而成的。如果我们将白光照在一个蓝色的玩具上,这个蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝色光被反射到我们的眼中,让这个玩具看起来是蓝色的。
下图显示的是一个珊瑚红的玩具,它以不同强度反射了多个颜色。
当我们在OpenGL中创建一个光源时,我们希望给光源一个颜色,我们将光源设置为白色。当我们把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色(也就是我们所感知到的颜色)。我们将这两个颜色向量作分量相乘,结果就是最终的颜色向量了:
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);
创建一个光照场景
首先我们需要一个物体来作为被投光的对象,我们使用前面立方体箱子。我们还需要一个物体来代表光源在3D场景中的位置。
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include
#include
#include
#include
#include
#include
#include
class MyOpenGLWidget : public QOpenGLWidget,public QOpenGLFunctions_3_3_Core
{
public:
MyOpenGLWidget(QWidget *parent = nullptr);
protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int w, int h);
void keyPressEvent(QKeyEvent *event);
private:
QOpenGLShaderProgram *m_program;
QOpenGLShaderProgram *m_lightProgram;
QVector3D cameraPos;
QVector3D cameraTarget;
QVector3D cameraDirection;
QVector3D up;
QVector3D cameraRight;
QVector3D cameraUp;
QVector3D cameraFront;
float cameraSpeed;
};
#endif // MYOPENGLWIDGET_H
#include "myopenglwidget.h"
#include
#include
#include
#include
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
GLuint indices[] = {
0, 1, 3,
1, 2, 3
};
//物体顶点着色器语言
const GLchar* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
"gl_Position = projection * view * model * vec4(position,1.0);\n"
"}\n\0";
//物体片段着色器语言
const GLchar* fragmentShaderSource = "#version 330 core\n"
"out vec4 color;\n"
"uniform vec3 objColor;\n"
"uniform vec3 lightColor;\n"
"void main()\n"
"{\n"
"color = vec4(objColor * lightColor,1.0f);\n"
"}\n\0";
//光源顶点着色器语言
const GLchar* vertexShaderSourceLight = "#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
"gl_Position = projection * view * model * vec4(position,1.0);\n"
"}\n\0";
//光源片段着色器语言
const GLchar* fragmentShaderSourceLight = "#version 330 core\n"
"out vec4 color;\n"
"uniform vec3 lightColor;\n"
"void main()\n"
"{\n"
"color = vec4(lightColor,1.0f);\n"
"}\n\0";
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 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);
}
void MyOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
//物体的着色器程序
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSource);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSource);
m_program->link();
//光源着色器程序
m_lightProgram = new QOpenGLShaderProgram();
m_lightProgram->addShaderFromSourceCode(QOpenGLShader::Vertex,vertexShaderSourceLight);
m_lightProgram->addShaderFromSourceCode(QOpenGLShader::Fragment,fragmentShaderSourceLight);
m_lightProgram->link();
//物体VAO VBO
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, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBindVertexArray(0);//解绑VAO
//光源VAO VBO
glGenVertexArrays(1, &lightVAO);
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, 5 * 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_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;
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);
glBindVertexArray(0);
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);
glBindVertexArray(0);
}
void MyOpenGLWidget::resizeGL(int w, int h)
{
}
void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
qDebug()<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();
}