OpenGL的基础光照知识

光照模型

常见的光照模型:ADS模型

  • A:环境光反射(ambient reflection):模拟低级光照,影响场景中的所有物体。
  • D:漫反射(diffuse reflection):根据光线的入射角度调整物体亮度。
  • S:镜面反射(specular reflection):展示物体的光泽,通过在物体表面上,光线直接地反射到我们的眼睛的位置,策略性地放置适当大小的高光来实现。

光源

  • 全局光(通常称为“全局环境光”,因为它仅包含环境光组件);
  • 定向光(或“远距离光”);
  • 位置光(或“点光源”);
  • 聚光灯。

全局环境光照模拟了现实世界中的一种光线现象——光线经过很多次反射,其光源和方向都已经无法确定。

float globalAmbient[4] = {
    
     0.6f, 0.6f, 0.6f, 1.0f };

定向光或远距离光也没有源位置,但它具有方向。
建模定向光需要指定其方向(以向量形式)及其环境、漫反射和镜面特征(通过设定 RGBA 值)。指向 z 轴负方向的红色定向光可以指定如下:

float dirLightAmbient[4] = {
    
     0.1f, 0.0f, 0.0f, 1.0f };

float dirLightDiffuse[4] = {
    
     1.0f, 0.0f, 0.0f, 1.0f };

float dirLightSpecular[4] = {
    
     1.0f, 0.0f, 0.0f, 1.0f };

float dirLightDirection[3] = {
    
     0.0f, 0.0f, -1.0f };

位置光在 3D 场景中具有特定位置,用以体现靠近场景的光源,位置光的效果取决于照射角度;但是,它没有方向,因为它对场景中的每个顶点的光照方向都不同。位置光还可以包含衰减因子,以模拟它们的强度随距离减小的程度。

float posLightAmbient[4] = {
    
     0.1f, 0.0f, 0.0f, 1.0f };

float posLightDiffuse[4] = {
    
     1.0f, 0.0f, 0.0f, 1.0f };

float posLightSpecular[4] = {
    
     1.0f,0.0f, 0.0f, 1.0f };

float posLightLocation[3] = {
    
     5.0f, 2.0f, -3.0f };

位于(5,2,−3)向下照射 z 轴负方向的红色聚光灯可以表示为:

float spotLightAmbient[4] = {
    
     0.1f, 0.0f, 0.0f, 1.0f };

float spotLightDiffuse[4] = {
    
     1.0f, 0.0f, 0.0f, 1.0f };

float spotLightSpecular[4] = {
    
     1.0f,0.0f, 0.0f, 1.0f };

float spotLightLocation[3] = {
    
     5.0f, 2.0f, -3.0f };

float spotLightDirection[3] = {
    
     0.0f, 0.0f, -1.0f };

float spotLightCutoff = 20.0f;

float spotLightExponent = 10.0f;

材质

这可以通过将每个对象视为“由某种材质制成”来建模。

ADS光照计算

环境光
漫反射
镜面反射

实现ADS光照

面片着色

对模型中每个多边形的一个顶点进行光照计算,然后以每个多边形或每个三角形为基础,将计算结果的光照值复制到相邻的像素中。

Gouraud着色 双线性光强插值法

  • 确定每个顶点的颜色,并进行光照相关计算。
  • 允许正常的栅格化过程在插入像素时对颜色也进行插值(同时也对光照进行插值)。在 OpenGL 中,这表示大多数光照计算都是在顶点着色器中完成的,片段着色器仅传递并展示自动插值的光照后的颜色。
    Gouraud 着色容易受到其他伪影影响。如果镜面高光整个范围都在模型中的一个三角形
    内——高光范围内一个模型顶点也没有,那么它可能不会被渲染出来。
源代码

Phong着色

光照计算是按像素而非顶点完成的,其中 N 和 L 在顶点着色器中进行计算,并在栅格化期间插值。
Phong着色有着比Gouraud着色更真实的效果,但这是建立在增大性能消耗的基础上的

Blinn-Phong 反射模型

Phong 着色中消耗最大的计算之一是求反射向量 R。
使用L+V得到角平分线向量,再将其点乘法向量N,得到夹角,夹角的值的2倍和V与反射角的夹角相等。
这样计算优化了性能,在图形质量上几乎与 Phong 着色相同。

法向量的逆转置矩阵

为什么法向量,从模型坐标变化到视图坐标,需要MV矩阵的逆装置矩阵才能正确得到结果?
这是因为变化对象从点变为了向量

结合光照与纹理

片段着色器中完全将材质特性去除,之后使用纹理取样所得纹理颜色代替材质的 ADS 值
适用于金属或“闪亮”的表面

fragColor = textureColor * ( ambientLight + diffuseLight ) + specularLight

对于织物或未上漆的木材(甚至一小部分金属,如黄金),其镜面高光部分都应当包含物体表面颜色

fragColor = textureColor * ( ambientLight + diffuseLight + specularLight )

物体本身具有 ADS 材质,并伴有纹理图像,如使用纹理为银质物体表面添加一些氧化痕迹。

textureColor = texture(sampler, texCoord)

lightColor = (ambLight * ambMaterial) + (diffLight * diffMaterial) + specLight

fragColor = 0.5 * textureColor + 0.5 * lightColor

补充说明

flat关键字:面片着色的环面是通过在顶点着色器和片段着色器中将 flat 插值限定符添加到相应的法向量属性声明中得到的。这样会使得光栅着色器不对所限定的变量进行插值,而是直接将相同的值赋给每个片段

分布式光

习题

光随着鼠标移动 观察Gouraud伪影

鼠标指针变化光照角度

	renderingProgram = Utils::createShaderProgram("./GouraudShaders/vertShader.glsl", "./GouraudShaders/fragShader.glsl");//先改成Gouraud Shader

cpp如何获得鼠标指针变量

使用GLFW库的鼠标回调函数来获得指针坐标变量

float mouseX, mouseY;

// 在主循环之前设置鼠标回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
    
    
	// xpos和ypos是鼠标在窗口内的坐标
	// 你可以将这些值传递给着色器或用于计算光源位置
	mouseX = static_cast<float>(xpos);
	mouseY = static_cast<float>(ypos);

	// 例如,将鼠标位置传递给着色器
	// glUniform2f(glGetUniformLocation(shaderProgram, "lightPos"), mouseX, mouseY);
	}

//main()
    // 设置鼠标回调函数
    glfwSetCursorPosCallback(window, mouse_callback);

//disply()
	amt = mouseX;
	rMat = glm::rotate(glm::mat4(1.0f), toRadians(amt), glm::vec3(0.0f, 0.0f, 1.0f));

//


观察Phong和Gouraud的区别

确实Phong在高光处明显效果更好

混合2个不同位置的位置光

位置光改为聚光灯