美文网首页
OpenGL学习12——光线投射器

OpenGL学习12——光线投射器

作者: 蓬篙人 | 来源:发表于2021-06-26 17:06 被阅读0次

光线投射器(Light Casters)

目前我们的渲染场景都只是应用单一来源的光源,现实世界中往往存在多种类型的光源。一个投射光线到物体上的光源我们称为光线投射器(light caster)。下面我们讨论几种光线投射器。

1. 定向光源

  • 当一个光源建模为无限远,则称为定向光源(directional light),因为它的光线拥有相同的方向(平行),且独立于光源的位置。下图展示一种定向光源——太阳光:(图片取自书中
    定向光源——太阳光
  • 因为光的方向矢量保持不变,因此对于场景中的每个对象的光源计算都是相似的。我们可以通过定义光的方向矢量而不是位置矢量来建模定向光源。这样对于定向光源,在着色器中我们无需通过位置矢量计算光的方向矢量而是直接使用。
struct Light{
    // vec3 position;  // 对于方向光源,不需要位置矢量
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
...
void main()
{
    vec3 lightDir = normalize(-light.direction);  // 直接取反
    // 下面直接使用lightDir计算扩散光和镜面光
    ...
}
  • 为了展示定向光源的效果,我使用坐标系统章节中产生多个立方体的方法,关键代码如下:
for (unsigned int i = 0; i < 10; i++)
{
    glm::mat4 model = glm::mat4(1.0f);
    model = glm::translate(model, cubePositions[i]);
    float angle = 20.0f * i;
    model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
    objectShader.setMat4("model", model);
    glDrawArrays(GL_TRIANGLES, 0, 36);
}
  • 代码中直接设置光源的方向矢量。
objectShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);
  • 渲染效果


    定向光源效果

2. 点光源

  • 点光源(point light)是在世界空间中指定位置向所有方向照射的光源,光线会随距离变暗(如灯光)。(图片取自书中
    点光源
  • 光线随照射距离逐渐降低强度的过程我们通常称为衰减(attenuation)。最简单的模拟衰减过程我们可以使用线性等式,但是线性等式的效果并不逼真。现实世界中,光的亮度在近距离内会快速衰减,超过一定距离之后衰减会变得缓慢很多。下面我们使用前人提出的公式来进行衰减模拟:
    F_{att}=\frac{1.0}{K_{c}+K_{l} *d+K_{q}*d^{2}}
    其中,d代表片元到光源的距离。要计算衰减值我们定义了(可配置的)3项:常数项K_{c},线性项K_{l}和二次项K_{q}
      1. 常数项一般设置为1.0,主要用于保证分母不小于1。
      1. 线性项与距离相乘,以线性的方式降低光的强度。
      1. 二次项与距离象限相乘,使光源进行二次衰减。与线性项相比,二次项在近距离的影响很小,但随着距离增加影响逐渐变大。
  • 上面公式的衰减效果。(图片取自书中
    衰减
  • 下面给出上述公式各项的常用值设置
距离 常数项 线性项 二次项
7 1.0 0.7 1.8
13 1.0 0.35 0.44
20 1.0 0.22 0.20
32 1.0 0.14 0.07
50 1.0 0.09 0.032
65 1.0 0.07 0.017
100 1.0 0.045 0.0075
160 1.0 0.027 0.0028
200 1.0 0.022 0.0019
325 1.0 0.014 0.0007
600 1.0 0.007 0.0002
3250 1.0 0.0014 0.00007
  • 要实现光的衰减,我们在片元着色器添加公式中对应的3个项,并将原先定向光源时的方向矢量修改回位置矢量。
struct Light{
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;

    float constant;
    float linear;
    float quadratic;
};
  • 在片元着色器中完成片元与光源距离和衰减值的计算
// 使用GLSL内置函数length
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
  • 将计算后的衰减值分别乘以3个光分量值
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
  • 代码中设置各项的值(对照上方的表格,我们大概想覆盖范围为50)
objectShader.setFloat("light.constant", 1.0f);
objectShader.setFloat("light.linear", 0.09f);
objectShader.setFloat("light.quadratic", 0.032f);
  • 渲染效果


    点光源效果

3. 聚光灯(spotlight)

  • 聚光灯是处于场景中某个位置向指定方向投射光线的光源。结果就是只有在聚光灯特定半径范围的物体才会被照亮。
  • 在OpenGL中,聚光灯由其世界空间的位置方向和一个指定其半径的截断角度(cutoff angle)所定义。见下图展示:(图片取自书中
    聚光灯
      1. LightDir:从片元指向光源位置的矢量。
      1. SpotDir:聚光灯指向的矢量。
      1. Phi\phi:截断角度,指定了聚光灯的照射半径,处于该角度外的物体将不会被照亮。
      1. Theta\thetaLightDirSpotDir矢量之间的角度。
  • 闪光灯(flashlight)是一种处于观察者位置的聚光灯,一般指向观察者视角的正前方。闪光灯的位置和方向随着观察者的位置和朝向的变化而变化。下面我们使用闪光灯这种类型的聚光灯来展示场景的渲染效果。
  • 对于处于聚光灯下物体的片元颜色的计算,我们需要聚光灯的位置矢量,方向矢量和cutoff角度,我们在片元着色器的Light结构添加。
struct Light{
    vec3 position;
    vec3 direction;
    float cutOff;
    ...
};
  • 对于聚光灯,我们需要计算\theta角度并与cutoff\phi角度值进行比较,判断片元是否处于聚光灯的光线椎体内。注意,下面的计算使用的是\theta\phicos值进行比较。
float theta = dot(lightDir, normalize(-light.direction));
if(theta > light.cutOff)
{
    // 完成光源计算
}
else
{
    FragColor = vec4(light.ambient * vec3(texture(material.diffuse, TexCoords)), 1.0);
}
  • 代码中设置聚光灯参数的值。
objectShader.setVec3("light.position", camera.Position);
objectShader.setVec3("light.direction", camera.Front);
objectShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
  • 渲染效果


    聚光灯效果

4. 聚光灯边缘平滑

  • 从上面的渲染效果可以看出边缘界限十分清晰,现实中,聚光灯的边缘应该是逐步缓慢变暗的。要平滑聚光灯的边缘,我们需模拟一个具有内外光线椎体的聚光灯,光线从内锥体到外椎体之间逐渐变暗。
  • 对于具有内外光锥的聚光灯,我们需要再定义一个cos值来代表光线方向矢量和外光锥之间的角度。如果片元处于内外光锥之间,它的强度值应该处于1.0到0.0之间,如果处于内光锥内则为1.0,处于外光锥外则为0.0。要计算这样的值我们可以使用如下公式:
    I=\frac{\theta -\gamma}{\epsilon}
    其中,\epsilon是内光锥截断角度\phi和外光锥截断角度\gamma之差(\epsilon =\phi -\gamma)。而I就是当前片元的聚光灯强度值。
  • 对于上述公式,很难想象各个参数如何影响最终的渲染效果,下面我们给出一些样本值。
\theta \theta角度 \phi(内cutoff) \phi角度 \gamma(外cutoff) \gamma角度 \epsilon I
0.87 30 0.91 25 0.82 35 0.91 - 0.82 =
0.09
0.87 - 0.82 /
0.09 = 0.56
0.9 26 0.91 25 0.82 35 0.91 - 0.82 =
0.09
0.9 - 0.82 /
0.09 = 0.89
0.97 14 0.91 25 0.82 35 0.91 - 0.82 =
0.09
0.97 - 0.82 /
0.09 = 1.67
0.83 34 0.91 25 0.82 35 0.91 - 0.82 =
0.09
0.83 - 0.82 /
0.09 = 0.11
0.64 50 0.91 25 0.82 35 0.91 - 0.82 =
0.09
0.64 - 0.82 /
0.09 = -2.0
0.966 15 0.9978 12.5 0.953 17.5 0.996 - 0.953 =
0.0448
0.966 - 0.953 /
0.0448 = 0.29
  • 从上面的表格我们大致可以推测出聚光灯的强度值,当处于聚光灯外时值为负,当处于内光锥内时值大于1.0,而当处于内外光锥之间,取值则处于1.0到0.0之间。在片元着色器的计算中,我们使用clamp函数,将强度值限制到0.0和1.0之间,这样我们可以取消前面的if-else判断,将强度值直接应用于光分量。
struct Light{
    vec3 position;
    vec3 direction;
    float cutOff;
    float outerCutOff;
    ...
};

int main()
{
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); 

    ...

    diffuse *= intensity;
    specular *= intensity;
    ...
}
  • 代码中设置cutoff值
objectShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
objectShader.setFloat("light.outerCutOff", glm::cos(glm::radians(17.5f)));
  • 渲染效果


    聚光灯边缘平滑效果

相关文章

  • OpenGL学习12——光线投射器

    光线投射器(Light Casters) 目前我们的渲染场景都只是应用单一来源的光源,现实世界中往往存在多种类型的...

  • OpenGL学习8——光线颜色

    光线颜色 现实世界我们看到的物体颜色并不是物体实际拥有的颜色,而是物体反射的颜色,即光线照射到物体反射到我们眼睛被...

  • 视频特效学习02- OpenGL渲染基础

    学习目标: 1.OpenGL渲染架构(掌握) OpenGL数据传递的3种方式(理解) OpenGL提供的着色器(掌...

  • 着色器

    着色器 OpenGL ES着色器语言之变量和数据类型(一) 我的OpenGL ES学习之路(一):GLSL着色器语言

  • Opengl es shading language

    着色器语言学习推荐:3.0文档,对应opengl es 3.0(默认1.0 对应opengl es 2.0) 概述...

  • OpenGL学习之路(2.0):OpenGL 基础渲染

    学习目标: OpenGL 渲染结构 如何使用7种OpenGL基础图元 如何使用储存着色器 如何使用Uniform属...

  • 20181018

    投射儿子疾病自愈,投射儿子晚上12点前睡觉,投射儿子学习认真,投射儿子健康、投射儿子心情愉快、投射儿子有正确的坐姿...

  • OpenGL 在Mac上搭建OpenGL环境

    最近开始学习OpenGL,工欲善其事必先利其器,先从搭建OpenGL环境开始。 代码下载地址 https://gi...

  • 场景视图光线投射

    //create a ray to get where we clicked in the scene view ...

  • 使用GLSL语言自定义着色器案例

      之前我们学习中,不管是使用OpenGL,还是OpenGL ES下的GLKit加载图片的时候,我们使用的着色器都...

网友评论

      本文标题:OpenGL学习12——光线投射器

      本文链接:https://www.haomeiwen.com/subject/wjyzyltx.html