1 はじめに
私たちが現実に見る物体の色は、その物体が実際に持つ色ではなく、物体が反射した色です。つまり、物体に吸収されない色が、私たちが認識できる物体の色ということになります。
たとえば、太陽光から見える白色光は、実際にはさまざまな色の組み合わせです。青いおもちゃに白い光を当てると、青いおもちゃは白い光の青を除くすべてのサブカラーを吸収し、吸収されなかった青い光が反射して私たちの目に戻ってくるため、おもちゃは青く見えます。
下の画像は、さまざまな強度で複数の色を反射するコーラルレッドのおもちゃを示しています。
OpenGL で光源を作成するとき、光源に色を与えたいので、光源を白に設定します。光源の色とオブジェクトの色の値を乗算すると、オブジェクトによって反射された色 (つまり、私たちが知覚する色) が得られます。これら 2 つのカラー ベクトルをコンポーネントとして乗算し、その結果が最終的なカラー ベクトルになります。
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);
2. 例
照明シーンを作成する
まずキャストするオブジェクトとしてオブジェクトが必要です。フロントキューブボックスを使用します。3D シーン内の光源の位置を表すオブジェクトも必要です。
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QImage>
#include <QOpenGLShaderProgram>
#include <QVector3D>
#include <QVector>
#include <QKeyEvent>
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 <QMatrix4x4>
#include <QTime>
#include <QTimer>
#include <math.h>
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<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);
}
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()<<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();
}