学习链接: 基础光照 - LearnOpenGL CN

环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。

漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。

镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

如下图所示:

本章demo如下所示(已上传至群文件):

可以看到同一个物体坐标,随着我们摄影机(观察者)位置不同,反射过来的颜色也会大大不同.

1.环境光照(Ambient)

环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。

为了模拟该场景,我们修改上章的被照物的片元shader:

// lightColor: 光源颜色
// objectColor: 自身物体颜色
const char *fragmentShaderSource = GET_GLSTR(out vec4 FragColor;uniform vec3 objectColor;uniform vec3 lightColor;void main(){vec3 ambient =  lightColor * 0.1;FragColor = vec4(ambient * objectColor, 1.0);}
);

然后修改背景颜色为黑色,效果如下所示:

可以看到橘黄色的物体变成这个颜色.

2.漫反射光照(Diffuse)

漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。

在之前向量复习文章中,我们知道两个单位向量的夹角越小,它们点乘的结果越倾向于1。当两个向量的夹角为90度的时候,点乘会变为0。

所以我们需要获取到立方体的每个平面下的每个顶点的法向量。

2.1 法向量

法向量是垂直于顶点平面的单位向量,我们以立方体的正对面为例,如下图所示:

可以看到(0,0,-1)法向量垂直于立方体正前面,并且该向量到该平面的每个向量的长度都是相等的.

那么我们是如何求出该法向量(0,0,-1)的?

如何求出法向量

如下图所示:

所以代码如下所示:

QVector3D normal ;
normal = QVector3D::crossProduct(QVector3D(-0.5,-0.5,-0.5) - QVector3D(0.5f, -0.5f, -0.5f) , QVector3D( 0.5f,  0.5f, -0.5f) -QVector3D(0.5f, -0.5f, -0.5f)  ).normalized();

更新后的顶点数据数组可以在这里找到.

接下来我们修改被照物的顶点shader:

// aPos: 顶点坐标
// model: 模型矩阵 对一个物体如何进行摆放等,其本质就是平移、旋转和缩放
// view: 观察矩阵让物体位于观察者哪个地方
// projection: 投影矩阵
// Normal: 传递给片段着色器用的法向量
const char *vertexShaderSource = GET_GLSTR(layout (location = 0) in vec3 aPos;layout (location = 1) in vec3 aNormal;uniform mat4 model;uniform mat4 view;uniform mat4 projection;out vec3 Normal;void main(){// 注意乘法要从右向左读gl_Position = projection * view * model * vec4(aPos, 1.0)  ;Normal = aNormal;}
);

这里我们把外部传递进来的aNormal法向量转发给片段着色器进行计算颜色.

然后在外部修改顶点属性:

program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float));
program->enableAttributeArray(0);
program->enableAttributeArray(1);

2.2 计算漫反射光照

首先修改被照物的片段着色器.添加成员:

in vec3 Normal;
uniform vec3 lightPos;

Normal是被照物的顶点着色器转发过来的每个顶点的法向量,而lightPos则是灯(光源)的位置向量.

现在我们的片段着色器有了法向量和光源的位置向量、但是没有物体自身的位置、所以继续修改被照物的顶点shader,添加:

out vec3 FragPos;
void main()
{...FragPos = vec3(model * vec4(aPos, 1.0));...
}

最后,在片段着色器中添加相应的输入变量:

in vec3 FragPos;

现在我们的片段着色器所需要的变量都有了,接下来我们就可以在片段着色器中计算光照了.

最终片段着色器代码如下所示:

#version 330 core
out vec4 FragColor;in vec3 Normal;
in vec3 FragPos;  uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;void main()
{// ambientfloat ambientStrength = 0.1;vec3 ambient = ambientStrength * lightColor;// 下面计算漫反射 vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * lightColor;vec3 result = (ambient + diffuse) * objectColor;FragColor = vec4(result, 1.0);
}

其中ambient是环境光分量、diffuse是漫反射分量、然后把结果乘以物体的颜色,来获得片段最后的输出颜色。最终呈现的效果如下所示:

效果如下所示(位于光照和被照物前面时):

可以看到颜色确实变了,但是在物体上体现的光照并不对呀.

这是因为被照物的法向量只是方向向量,不是一个法线矩阵,由于没有w分量,所以平移对于法向量没有用,假如我们对物体进行了不等比缩放,也会对法向量进行影响(不再垂直于表面):

那么我们获取一个法线矩阵即等于即等于观察变换矩阵逆的转置,为什么是这样的可以去阅读这个文章。

所以修改被照物的顶点代码:

Normal = mat3(transpose(inverse(model))) * aNormal;

transpose(inverse(model))表示获取的是观察矩阵的逆矩阵的转置。

修改后重新运行代码如下所示:

可以看到这次光照终于正常了(白色的矩形是光照,位于所有被照物的前方)

3.镜面光照

需要获取观察者(摄影机)位置,由于入射角等于反射角,所以观察者与反射角的夹角θ越小,那么反射光越强,如下图所示:

修改被照物的片段着色器添加:

        // 下面计算镜面光照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) * objectColor;FragColor = vec4(result, 1.0);

其中viewPos是摄影机位置.

而reflect函数定义如下所示:

genType reflect (genType I, genType N);
// I是入射光的方向,N是反射平面的法线,返回值是反射光的方向。I – 2 * dot(N, I) * N。
// N必须是单位向量。

所以reflectDir是反射光R向量.

然后执行“float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);”,计算观察者与反射向量的点乘(角度越小cos值越大),所以spec能够充分表达观察者与反射角的夹角θ关系.

最后就是乘上光的颜色和镜面强度系数.最终片段着色器源码如下所示:

// lightColor: 光源颜色
// objectColor: 自身物体颜色
const char *fragmentShaderSource = GET_GLSTR(out vec4 FragColor;in vec3 Normal;in vec3 FragPos;uniform vec3 viewPos;uniform vec3 lightPos;uniform vec3 lightColor;uniform vec3 objectColor;void main(){// ambientfloat ambientStrength = 0.1;vec3 ambient = ambientStrength * lightColor;// 下面计算漫反射vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = diff * lightColor;// 下面计算镜面光照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) * objectColor;FragColor = vec4(result, 1.0);});

运行效果如下所示(位于光照和被照物背面时):

位于光照和被照物前面时:

16.opengl-qt 基础光照相关推荐

  1. OpenGL 基础光照ColorsBasic Lighting

    OpenGL 基础光照ColorsBasic Lighting 基础光照ColorsBasic Lighting简介 环境光照 漫反射光照 法向量 计算漫反射光照 最后一件事 镜面光照 基础光照Col ...

  2. Vulkan_Shader_Day02—光照(基础光照_Phong Lighting Model)

    基础光照 现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的.因此OpenGL的光照使用的是简化的模型,对现实的情况进行近似,这样处理起来会更容易一些,而且看起来 ...

  3. Qt基础与Qt on Android入门-安晓辉-专题视频课程

    Qt基础与Qt on Android入门-66107人已学习 课程介绍         本课程起始于基础的开发环境搭建和Qt Creator介绍,帮助初学者入门:着力于Qt的内在机制,由浅入深介绍信号 ...

  4. QT/C++从新手到老手系列之QT基础篇-李浩林-专题视频课程

    QT/C++从新手到老手系列之QT基础篇-1620人已学习 课程介绍         本系列课程励志于带领你学习QT5/C++,从开发环境(QTCreator和VS2013两种)搭建到实际项目实战,从 ...

  5. OpenGL ES 3. 光照-散射光

    大家好,接下来将为大家介绍OpenGL ES 3. 光照-散射光. OpenGL ES 3.0 中采用的光照模型相对现实世界进行了很大的简化,将光照分成了 3 种组成元素(也可以称为 3 个通道),包 ...

  6. QT基础之一文介绍QPainter绘制基础图形(画笔画刷设置,填充铺展渐变效果)

    更多参见 QT基础与实例应用目录 代码链接 GitHub链接 :QPainterSimpleExample 介绍 结合实例介绍如何利用QPainter绘制各种图形,可绘制不同形状,使用不同画笔颜色.画 ...

  7. LearnOpenGL-光照-2.基础光照

    本人刚学OpenGL不久且自学,文中定有代码.术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject 文章目录 基础光照 ...

  8. Qt学习之Qt基础入门(下)

    1. 前言 前两篇博客简单的阐述了一下Qt的入门用法,这篇博客继续跟着视频学习. Qt入门系列: Qt学习之C++基础 Qt学习之Qt安装 Qt学习之Qt基础入门(上) Qt学习之Qt基础入门(中) ...

  9. Qt学习之Qt基础入门(中)

    1. 前言 上一篇博客,总结了Qt的一些基础用法,这篇博客继续跟视频学习Qt的常用方法 Qt入门系列: Qt学习之C++基础 Qt学习之Qt安装 Qt学习之Qt基础入门(上) Qt学习之Qt基础入门( ...

最新文章

  1. 酸了!会这个技能的 AI 工程师年薪至少35W起!
  2. iOS 中KVC、KVO、NSNotification、delegate 总结及区别
  3. Parallel Programming-使用CancellationTokenSource调度并行运行的Task
  4. TCP/IP / SYN 攻击以及解决办法
  5. 一 在应用中升级u-boot、内核以及文件系统
  6. Java并发篇_进程线程
  7. 解决Maven工程中报 Missing artifact jdk.tools:jdk.tools:
  8. 转:SQL的内连接与外连接
  9. 《Qt 数据库详解》博客系列文章
  10. eclipse中选中字段,其他相同字段被覆盖的颜色修改
  11. java 金字塔 2的幂_三角形数(金字塔三角形数量公式)
  12. python openpyxl删除excel特定行数据遇到的问题
  13. post请求https安全证书问题2.0
  14. mysql pam 配置_pam_mysql 安装配置总结 (结合vsftpd)
  15. Flink入门(一)(Java和scala)
  16. 腾讯AI Lab披露可信AI研究进展,解读20余项原创工作
  17. DirectX FAQ 翻译(Graphics 部分)
  18. “数据科学家”或许不再性感,但“数据团队”的产业化才刚开始 | 专访领英全球数据科学团队负责人
  19. 如何将所有电子邮件地址合并到一个Outlook.com收件箱中
  20. FHQ Treap学习记录(详解)

热门文章

  1. day027 jQuery第二天
  2. 小学计算机绘图体会,辅导小学生电脑绘画的几点做法
  3. 生鲜配送系统有哪些功能?搭建生鲜配送系统有哪些好处?
  4. ARCH模型和GARCH模型
  5. FortiGate命令行
  6. MT【108】线面角最小
  7. Supervised pre-trainning有监督预训练
  8. [附源码]计算机毕业设计springboot基于微信小程序的网络办公系统
  9. 《算法零基础100讲》(第30讲) 概率与统计
  10. APICloud和海马玩模拟器结合调试手机页面