(本文是LearnOpenGL的学习笔记,教程中文翻译地址https://learnopengl-cn.github.io/)
0.前言
前面学习了LearnOpenGL的入门章节,本文开始学习光照 (Lighting) 的颜色 (Colors)。
1.如何实现
颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成,它们通常被缩写为RGB。我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。换句话说,那些不能被物体所吸收(Absorb)的颜色(被拒绝的颜色)就是我们能够感知到的物体的颜色。
当我们在OpenGL中创建一个光源时,我们给光源一个颜色(如白色)。当我们把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色(也就是我们所感知到的颜色)。
gl_FragColor = vec4(lightColor * objectColor, 1.0);
接下来就是利用利用之前学的Camera+着色器在场景中绘制了两个盒子,一个光源(lamp),一个物体 (lighting)。如果把物体的光源向量改为青色 vec3(0,1,1) ,那么反射的颜色也相应地改变了。
2.实现代码
(项目git链接:https://github.com/gongjianbo/OpenGLwithQtWidgets.git)
我的GLColors类实现效果(MyCamera是移植的教程自定义类,代码也在git项目中):
GLColors类代码:
#ifndef GLCOLORS_H
#define GLCOLORS_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QOpenGLTexture>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QTimerEvent>
#include "MyCamera.h"
//光照颜色
class GLColors : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit GLColors(QWidget *parent = nullptr);
~GLColors();
protected:
//设置OpenGL资源和状态。在第一次调用resizeGL或paintGL之前被调用一次
void initializeGL() override;
//渲染OpenGL场景,每当需要更新小部件时使用
void paintGL() override;
//设置OpenGL视口、投影等,每当尺寸大小改变时调用
void resizeGL(int width, int height) override;
//鼠标操作,重载Qt的事件处理
void keyPressEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private:
void initShader();
private:
//着色器程序
QOpenGLShaderProgram _lightingShader,_lampShader;
//顶点数组对象
QOpenGLVertexArrayObject _lightingVao,_lampVao;
//顶点缓冲
QOpenGLBuffer _vbo;
//view的camera
MyCamera _camera;
};
#endif // GLCOLORS_H
#include "GLColors.h"
#include <QObject>
GLColors::GLColors(QWidget *parent)
: QOpenGLWidget(parent)
{
setFocusPolicy(Qt::ClickFocus); //Widget默认没有焦点
}
GLColors::~GLColors()
{
//通过使相应的上下文成为当前上下文并在该上下文中绑定帧缓冲区对象,
//准备为此小部件呈现OpenGL内容。在调用paintGL()之前会自动调用。
makeCurrent();
_vbo.destroy();
_lightingVao.destroy();
_lampVao.destroy();
//释放上下文。在大多数情况下不需要调用此函数,
//因为小部件将确保在调用paintGL()时正确绑定和释放上下文。
doneCurrent();
}
void GLColors::initializeGL()
{
//为当前上下文初始化OpenGL函数解析
initializeOpenGLFunctions();
initShader();
//VAO,VBO
float vertices[] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
};
_vbo=QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
_vbo.create();
//light vao
_lightingVao.create();
_lightingVao.bind();
_vbo.bind();
_vbo.allocate(vertices,sizeof(vertices));
//setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
int attr = -1;
attr = _lightingShader.attributeLocation("aPos");
//setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
_lightingShader.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 3);
_lightingShader.enableAttributeArray(attr);
_vbo.release();
_lightingVao.release();
//lamp vao
_lampVao.create();
_lampVao.bind();
_vbo.bind();
attr = _lampShader.attributeLocation("aPos");
//setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0)
_lampShader.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 3);
_lampShader.enableAttributeArray(attr);
_vbo.release();
_lampVao.release();
}
void GLColors::paintGL()
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
//清除深度缓冲
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Z缓冲(Z-buffer),深度缓冲(Depth Buffer)。
glEnable(GL_DEPTH_TEST);
//draw lighting
_lightingShader.bind();
_lightingShader.setUniformValue("objectColor",QVector3D(1.0f,0.5f,0.31f));
_lightingShader.setUniformValue("lightColor",QVector3D(1.0f,1.0f,1.0f));
QMatrix4x4 view=_camera.getViewMatrix(); //观察矩阵
//QMatrix4x4 view;
//view.translate(QVector3D(0.0f, 0.0f, -10.0f));
//qDebug()<<"view"<<view;
_lightingShader.setUniformValue("view", view);
QMatrix4x4 projection; //透视投影
projection.perspective(_camera.getZoom(), 1.0f * width() / height(), 0.1f, 100.0f);
_lightingShader.setUniformValue("projection", projection);
QMatrix4x4 model;//模型矩阵
model.rotate(45,QVector3D(1.0f,1.0f,0.0f));
_lightingShader.setUniformValue("model", model);
_lightingVao.bind();
glDrawArrays(GL_TRIANGLES, 0, 36);
_lightingVao.release();
_lightingShader.release();
//draw lamp
_lampShader.bind();
_lampShader.setUniformValue("view", view);
_lampShader.setUniformValue("projection", projection);
model=QMatrix4x4();
model.translate(QVector3D(1.2f, 1.0f, 2.0f));
model.scale(0.2f);
_lampShader.setUniformValue("model", model);
_lampVao.bind();
glDrawArrays(GL_TRIANGLES, 0, 36);
_lampVao.release();
_lampShader.release();
}
void GLColors::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
void GLColors::keyPressEvent(QKeyEvent *event)
{
_camera.keyPress(event->key());
update();
QOpenGLWidget::keyPressEvent(event);
}
void GLColors::mousePressEvent(QMouseEvent *event)
{
_camera.mousePress(event->pos());
update();
QOpenGLWidget::mousePressEvent(event);
}
void GLColors::mouseReleaseEvent(QMouseEvent *event)
{
_camera.mouseRelease(event->pos());
update();
QOpenGLWidget::mouseReleaseEvent(event);
}
void GLColors::mouseMoveEvent(QMouseEvent *event)
{
_camera.mouseMove(event->pos());
update();
QOpenGLWidget::mouseMoveEvent(event);
}
void GLColors::wheelEvent(QWheelEvent *event)
{
_camera.mouseWheel(event->delta());
update();
QOpenGLWidget::wheelEvent(event);
}
void GLColors::initShader()
{
//lingting shader
//in输入,out输出,uniform从cpu向gpu发送
const char *lighting_vertex=R"(#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
})";
const char *lighting_fragment=R"(#version 330 core
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
gl_FragColor = vec4(lightColor * objectColor, 1.0);
})";
//将source编译为指定类型的着色器,并添加到此着色器程序
if(!_lightingShader.addCacheableShaderFromSourceCode(
QOpenGLShader::Vertex,lighting_vertex)){
qDebug()<<"compiler vertex error"<<_lightingShader.log();
}
if(!_lightingShader.addCacheableShaderFromSourceCode(
QOpenGLShader::Fragment,lighting_fragment)){
qDebug()<<"compiler fragment error"<<_lightingShader.log();
}
//使用addShader()将添加到该程序的着色器链接在一起。
if(!_lightingShader.link()){
qDebug()<<"link shaderprogram error"<<_lightingShader.log();
}
//lamp shader
const char *lamp_vertex=R"(#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0f);
})";
const char *lamp_fragment=R"(#version 330 core
void main()
{
gl_FragColor = vec4(1.0);
})"; // set alle 4 vector values to 1.0
if(!_lampShader.addCacheableShaderFromSourceCode(
QOpenGLShader::Vertex,lamp_vertex)){
qDebug()<<"compiler vertex error"<<_lampShader.log();
}
if(!_lampShader.addCacheableShaderFromSourceCode(
QOpenGLShader::Fragment,lamp_fragment)){
qDebug()<<"compiler fragment error"<<_lampShader.log();
}
if(!_lampShader.link()){
qDebug()<<"link shaderprogram error"<<_lampShader.log();
}
}
3.参考
LearnOpenGL:https://learnopengl-cn.github.io/02%20Lighting/01%20Colors/
博客(Qt+OpenGL):https://www.jianshu.com/p/e7e9097d2748
博客(Qt+OpenGL):https://blog.csdn.net/z136411501/article/details/80111754