OpenGL 入门 10:光源

2023-11-18

点光源

点光源的强度需要随着距离增加而减少,至于减少的系数公式大致如下:

在这里d代表了片段距光源的距离。接下来为了计算衰减值,我们定义3个(可配置的)项:常数项Kc、一次项Kl和二次项Kq

  • 常数项通常保持为1.0,它的主要作用是保证分母永远不会比1小,否则的话在某些距离上它反而会增加强度,这肯定不是我们想要的效果。

  • 一次项会与距离值相乘,以线性的方式减少强度。

  • 二次项会与距离的平方相乘,让光源以二次递减的方式减少强度。二次项在距离比较小的时候影响会比一次项小很多,但当距离值比较大的时候它就会比一次项更大了。

聚光灯

聚光是位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。这样的结果就是只有在聚光方向的特定半径内的物体才会被照亮,其它的物体都会保持黑暗。

  • LightDir:从片段指向光源的向量。

  • SpotDir:聚光所指向的方向。

  • Phiϕ:指定了聚光半径的切光角。落在这个角度之外的物体都不会被这个聚光所照亮。

  • ThetaθLightDir向量和SpotDir向量之间的夹角。在聚光内部的话θ值应该比ϕ值小。

平滑/软化边缘

然而上述方法中的聚光灯存在明显的硬边缘。因此首先将当前照亮范围的圆锥视为内圆锥,再在其外面套一个外圆锥,两个圆锥的部分通过插值的方式算出其的光照衰减。

为了创建一个外圆锥,我们只需要再定义一个余弦值来代表聚光方向向量和外圆锥向量(等于它的半径)的夹角。然后,如果一个片段处于内外圆锥之间,将会给它计算出一个0.0到1.0之间的强度值。如果片段在内圆锥之内,它的强度就是1.0,如果在外圆锥之外强度值就是0.0。

我们可以用下面这个公式来计算这个值:

这里ϵ(Epsilon)是内(ϕ)和外圆锥(γ)之间的余弦值差(ϵ=ϕ−γ)。最终的I值就是在当前片段聚光的强度。

平行光、点光源、聚光灯FragmentShader

#version 330 core
in vec3 Normal;
in vec3 WorldPos;
in vec2 TexCoords;
out vec4 FragColor;

uniform vec3 viewPos;

struct Material {
    sampler2D emission;
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
}; 

uniform Material material;

struct DirLight {
    vec3 direction;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};  
uniform DirLight dirLight;

struct PointLight {
    vec3 position;

    float constant;
    float linear;
    float quadratic;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};  
#define NR_POINT_LIGHTS 4
uniform PointLight pointLights[NR_POINT_LIGHTS];

struct SpotLight {
    vec3  position;
    vec3  direction;
    float cutOff;
    float outerCutOff;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform SpotLight spotLight;

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 worldPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 worldPos, vec3 viewDir);

void main()
{
    // 属性
    vec3 normal = normalize(Normal);
    vec3 viewDir = normalize(viewPos - WorldPos);

    vec3 result = CalcDirLight(dirLight, normal, viewDir);
    for(int i = 0; i < NR_POINT_LIGHTS; i++)
        result += CalcPointLight(pointLights[i], normal, WorldPos, viewDir);    
    result += CalcSpotLight(spotLight, normal, WorldPos, viewDir);  

    FragColor = vec4(result, 1.0);
}

vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
{
    vec3 lightDir = normalize(-light.direction);
    // 漫反射着色
    float diff = max(dot(normal, lightDir), 0.0);
    // 镜面光着色
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // 合并结果
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));

    return (ambient + diffuse + specular);
}

vec3 CalcPointLight(PointLight light, vec3 normal, vec3 worldPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - worldPos);
    // 漫反射着色
    float diff = max(dot(normal, lightDir), 0.0);
    // 镜面光着色
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // 衰减
    float distance    = length(light.position - worldPos);
    float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));    
    // 合并结果
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    ambient  *= attenuation;
    diffuse  *= attenuation;
    specular *= attenuation;

    return (ambient + diffuse + specular);
}

vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 worldPos, vec3 viewDir)
{
    vec3 lightDir = normalize(light.position - worldPos);
    // 漫反射着色
    float diff = max(dot(normal, lightDir), 0.0);
    // 镜面光着色
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    // 合并结果
    vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
    vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));
    vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    // 强度
    float theta = dot(lightDir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);

    return ambient + (diffuse + specular) * intensity;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL 入门 10:光源 的相关文章

  • 着色器/矩阵问题 - 看不到对象

    我试图在屏幕上放置一个立方体并点亮它 我想要在立方体上添加 phong 阴影 当我运行代码时 我可以看到背景图像 但看不到立方体 我相当确定立方体本身是正确的 因为我已经设法用纯色着色器显示它 我已经设法编译着色器程序 但我根本看不到立方体
  • libgdx 中帧缓冲区的结果不明确

    我得到以下奇怪的结果帧缓冲区 http libgdx badlogicgames com nightlies docs api com badlogic gdx graphics glutils FrameBuffer htmllibgdx
  • OpenGL 将着色器附加到程序

    有没有办法访问附加到程序的着色器 也就是说 给定一个程序 我可以做类似的事情 vertexShader getVertexShaderFromProgram program 我想在验证我的程序的函数中记录着色器编译状态 但我只保留对程序的引
  • 退出 glutFullScreen()

    我不明白为什么当我按 f 时它进入全屏但不退出全屏 在这个方法的开头我已经设置了bool fullscreen false 这是我的切换代码 case f toggle screenmode if fullscreen glutFullSc
  • glutPostRedisplay 不在循环内工作

    我试图让一个人在 y 轴上跳跃 所以我使用 2 秒的循环 第一秒它应该向下移动并弯曲膝盖 第二秒它应该向上移动 然后在起始位置完成 现在我刚刚开始让这个人在第一秒内跪下并弯曲膝盖 我还没有编写动画的其余部分 问题是 glutPostRedi
  • OpenGL 和加载/读取 AoSoA(混合 SoA)格式的数据

    假设我有以下 AoSoA 格式的简化结构来表示顶点或点 struct VertexData float px 4 position x float py 4 position y 也就是说 每个实例VertexData存储4个顶点 我见过的
  • 使用 Qt 在 xoverlay 之上绘制

    我希望在使用 Xoverlay 渲染的视频流之上绘制一些 UI 我正在使用 gstreamer 播放视频并使用 xoverlay 在 xvimagesink 上渲染它 我的小部件继承自 QGLWidget 我希望使用 QPainter 绘制
  • 即使在顶点着色器中使用,glGetUniformLocation()也会返回-1

    我正在尝试用法线渲染一个简单的立方体 我使用以下代码来初始化着色器 void initShader const char vertexShaderPath const char fragmentShaderPath cout lt lt I
  • glEnableVertexAttribArray 中“index”参数的含义以及(可能)OS X OpenGL 实现中的错误

    1 我是否正确理解 要使用顶点数组或VBO进行绘制 我需要所有属性在着色器程序链接之前调用glBindAttribLocation 或者在着色器程序成功链接后调用glGetAttribLocation 然后使用glVertexAttribP
  • lwjgl 3 , glUniformMatrix4 导致 jre 崩溃

    我正在使用 lwjgl 3 并学习现代 opengl 3 我想将统一矩阵发送到顶点着色器 以便我可以应用转换 我尝试过 但程序因此错误而崩溃 A fatal error has been detected by the Java Runti
  • lnk1104:无法打开“LIBC.lib”链接

    使用 GLee 将着色器写入我的 OpenGL 项目并编译后 我收到了错误LNK1104 cannot open file LIBC lib 我尝试按照其他人的建议添加它并忽略它 但没有解决问题 有没有其他方法可以解决我错过的这个问题 以下
  • 阻止 OpenGL.framework 在 Cocoa 应用程序中加载

    我的应用程序链接到这些框架 Cocoa Framework AppKit Framework CoreData Framework Foundation Framework 请注意 OpenGL Framework 是NOT已链接 但是 设
  • 为什么 OpenGL 给对象提供句柄而不是指针?

    OpenGL 的传统是让用户使用 unsigned int 句柄来操作 OpenGL 对象 为什么不直接给出一个指针呢 与指针相比 唯一 ID 有何优点 TL DR OpenGL ID 不会双射映射到内存位置 单个 OpenGL ID 可能
  • OpenGL 与 Eclipse CDT + MinGW + GLEW + GLFW:未定义的参考

    Edit 与此同时 我已经弄清楚了这一点 并在下面写了详细的答案 我刚刚尝试在 Win7 上从 Express 版本的 MSVC 10 切换到 Eclipse CDT 在配置时遇到了以下简单 OpenGL 代码的问题 在 Visual St
  • 为什么OpenGL使用float而不是double? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 为什么拥有单独的投影矩阵但结合模型和视图矩阵会有好处?

    当您学习 3D 编程时 您会被告知用 3 个变换矩阵来思考是最简单的 模型矩阵 该矩阵对于每个模型都是独立的 它根据需要旋转和缩放对象 最后将其移动到 3D 世界中的最终位置 模型矩阵将模型坐标转换为世界坐标 视图矩阵 对于大量对象 如果不
  • 将带有 glut 的点击坐标添加到向量链接列表中

    我想创建一个向量链接列表 并在 GLUT 库的帮助下获取点击的位置并将它们附加到链接列表中 这些是我写的结构 typedef struct vector int x int y Vector typedef struct VectorLis
  • 在 Linux 上运行我自己的程序的权限被拒绝? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有Ubuntu 9 4 我已经构建了程序 一些基本的 OpenGL 该程序只是制作一个旋转的正方形 然后运行它并 sh blabla p
  • OpenGL缓冲区更新[重复]

    这个问题在这里已经有答案了 目前我正在编写一个模拟水的程序 以下是我所做的步骤 创建水面 平面 创建VAO 创建顶点缓冲区对象 在其中存储法线和顶点 将指针绑定到此 VBO 创建索引缓冲区对象 然后我使用 glDrawElements 渲染
  • 使用 JOGL 和 Android OpenGL 编写可移植 Java 应用程序

    我计划编写一款可以在 PC 和 Android 上运行的 Java 3D 游戏 不幸的是 这两个平台似乎没有通用的 OpenGL API API 是否有显着差异 有没有办法在两个版本中使用相同的 3D 代码 这是不是一个好主意 Androi

随机推荐

  • 【Linux学习】vim编辑器的使用

    Linux环境中vim编辑器的使用 前言 一 vim是什么 二 vim的使用 1 vim的三种模式 1 1 命令模式 Command mode 1 2 输入模式 Insert mode 1 3 底线命令模式 Last line mode 2
  • xshell5中文破解版

    http www xue51 com soft 1442 html
  • 护网面试题

    1 有无安全设备的使用经验 2 了解过TOP10没有 1 SQL注入 2 失效的身份认证和会话管理 3 跨站脚本攻击XSS 4 直接引用不安全的对象 5 安全配置错误 6 敏感信息泄露 7 缺少功能级的访问控制 8 跨站请求伪造CSRF 9
  • 解决找不到mfc140.dll的问题

    mfc140 dll控件常规安装方法 仅供参考 如果在运行某软件或编译程序时提示缺少 找不到mfc140 dll等类似提示 您可将从脚本之家下载来的mfc140 dll拷贝到指定目录即可 一般是system系统目录或放到软件同级目录里面 或
  • Neo4j宣布下一代图数据平台Neo4j 5上线

    增强的可扩展性 敏捷性 高效率和性能优势使企业能够在任何环境中更快 更轻松地创建和部署智能应用程序 中国北京 2022 年 11 月 10日 图技术的领导者Neo4j 今天宣布了下一代可用于云端的图数据平台Neo4j 5上线 在传统数据库的
  • 不均匀硬币产生等概率/均匀硬币产生非等概率

    不均匀硬币产生等概率 已知随机数生成函数random 返回0的概率是60 返回1的概率是40 根据random 实现一个随机数函数f 使返回0和1的概率是50 连续投掷两次 第一次为0 第二次为1 返回0 第一次为1 第二次为0 返回1 这
  • Unicode汉字编码表

    1 Unicode编码表 Unicode只有一个字符集 中 日 韩的三种文字占用了Unicode中0x3000到0x9FFF的部分 Unicode目前普遍采用的是UCS 2 它用两个字节来编码一个字符 比如汉字 经 的编码是0x7ECF 注
  • 使用layui 写一段动态向form表单添加select下拉框,带删除功能

    可以参考如下示例代码 实现动态向表单中添加和删除 select 下拉框 div class layui container div
  • [debug] “ImportError DLL load failed 找不到指定的程序”的解析和解决办法。

    ImportError DLL load failed 找不到指定的程序 的解析和解决办法 文章目录 ImportError DLL load failed 找不到指定的程序 的解析和解决办法 问题描述 问题解析 解决方法 查看依赖库信息
  • python request要求接口参数必须是json数据

    Reqeusts支持以form表单形式发送post请求 只需要将请求的参数构造成一个字典 然后传给requests post 的data参数即可 data参数的格式如下 content type在header中设置 1 data为dict时
  • 线代:1.3矩阵的逆

    文章目录 任务详解 矩阵的逆 一定是方阵 先导知识 逆的定义 定理1 定理2 逆矩阵的性质 本课程来自深度之眼 部分截图来自课程视频 第一章 线性代数 1 3矩阵的逆 在线LaTeX公式编辑器 任务详解 1 掌握矩阵逆的来源 可逆的充要条件
  • Nginx 安装与部署配置以及Nginx和uWSGI开机自启

    下载 官方网站 https nginx org en download html Windows下安装 安装 下载后解压 切记不能含有中文路径 文件结构如图 我解压的路径就有中文 记得拷贝放置于英文目录下即可 启动 两种方法 1 直接双击该
  • 多线程实现字典系统(server+client)

    多线程字典系统实现 首先说明下该系统可以实现的功能 小白都可以 该博客只提供学习和实现的思路 如果需要详细的代码 请留言 1 具体要求 简单来说 就是实现服务器端和客户端 可以做到多个客户端并发对字典中的数据进行操作 但是不考虑跨局域网的情
  • java.lang.NoClassDefFoundError: org/mybatis/logging/LoggerFactory

    java lang NoClassDefFoundError org mybatis logging LoggerFactory 目录 文章目录 后记 内容 问题如题目所示 这种情况通常由依赖冲突导致 解决方案如下 说明 本人使用IDE为i
  • java版 SpringCloud 之目前得前端框架都有哪些?

    1 AngularJS Angular JS 是一个有Google维护的开源前端web应用程序框架 它最初由Brat Tech LLC的Misko Hevery于2009年开发出来 Angular JS是一个模型 视图 控制器 MVC 模式
  • 通达信资金净流入公式_通达信资金净入净出指标公式

    额 AMOUNT 10000000 NODRAW VAR1 AMOUNT HIGH LOW 2 ABS CLOSE OPEN 流入亿 IF CLOSE gt OPEN VAR1 HIGH LOW IF CLOSE 流出亿 IF CLOSE
  • 【数据库】期末复习总结

    第一章 概念 数据库定义 是一种依照特定数据模型组织 存储和管理数据的文件集合 数据库和普通文件区别 支持不同应用对数据共享访问 数据管理复杂 可独立于应用 管理由DBMS实现 数据模型定义 描述事物对象的数据结构组成 数据语义联系 数据约
  • 实战22:文本摘要实战:基于句子相似度矩阵构建图结构实现文本摘要 代码+数据

    任务描述 自动文本摘要 Text Summarization 是指给出一段文本 我们从中提取出要点 然后再形成一个短的概括性的文本 自动的文本摘要是非常具有挑战性的 当我们作为人类总结一篇文章时 我们通常会完整地阅读它以发展我们的理解 然后
  • Pycharm和Python关系

    Pycharm和Python关系 简单来说 Pycharm是一个代码编辑器 是目前最流行的代码编辑器之一 用于编写python代码 Python是一个代码解释器 用于将Python代码翻译成计算机可以理解的指令 Pycharm下载地址 Py
  • OpenGL 入门 10:光源

    点光源 点光源的强度需要随着距离增加而减少 至于减少的系数公式大致如下 在这里d代表了片段距光源的距离 接下来为了计算衰减值 我们定义3个 可配置的 项 常数项Kc 一次项Kl和二次项Kq 常数项通常保持为1 0 它的主要作用是保证分母永远