【C++游戏引擎开发】第13篇:光照模型与Phong基础实现

一、Phong模型数学原理

1.1 光照叠加公式

L = k a I a + k d I d max ⁡ ( 0 , n ⋅ l ) + k s I s max ⁡ ( 0 , r ⋅ v ) α L = k_a I_a + k_d I_d \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s I_s \max(0, \mathbf{r} \cdot \mathbf{v})^\alpha L=kaIa+kdIdmax(0,nl)+ksIsmax(0,rv)α

  • 符号说明
    • k a , k d , k s k_a, k_d, k_s ka,kd,ks:材质的环境/漫反射/高光系数(标量)
    • I a , I d , I s I_a, I_d, I_s Ia,Id,Is:光源的环境/漫反射/高光强度(三通道向量或标量)
    • n \mathbf{n} n:表面单位法线向量
    • l \mathbf{l} l:单位光照方向向量(从表面指向光源)
    • v \mathbf{v} v:单位视线方向向量(从表面指向观察点)
    • r \mathbf{r} r:反射方向单位向量
    • α \alpha α:高光锐度系数(值越大高光越集中)

1.2 核心分量数学推导

1.2.1 环境光(Ambient)

L a = k a ⋅ I a L_a = k_a \cdot I_a La=kaIa

  • 特性:均匀照亮场景,与几何关系和光源方向无关
  • 缺陷:易导致画面“过平”,需结合其他分量使用
1.2.2 漫反射(Lambert’s Cosine Law)

L d = k d ⋅ I d ⋅ max ⁡ ( 0 , n ⋅ l ) L_d = k_d \cdot I_d \cdot \max(0, \mathbf{n} \cdot \mathbf{l}) Ld=kdIdmax(0,nl)

  • 几何解释
    • n ⋅ l = cos ⁡ θ \mathbf{n} \cdot \mathbf{l} = \cos\theta nl=cosθ,其中 θ \theta θ 为入射角
    • max ⁡ ( 0 , ⋅ ) \max(0, \cdot) max(0,) 确保背面无光照贡献
  • 能量守恒:入射角越大,单位面积接收的光能越少
1.2.3 高光反射(Phong Specular)

L s = k s ⋅ I s ⋅ max ⁡ ( 0 , v ⋅ r ) α L_s = k_s \cdot I_s \cdot \max(0, \mathbf{v} \cdot \mathbf{r})^\alpha Ls=ksIsmax(0,vr)α

  • 反射向量计算
    r = 2 ( n ⋅ l ) n − l \mathbf{r} = 2(\mathbf{n} \cdot \mathbf{l})\mathbf{n} - \mathbf{l} r=2(nl)nl
  • Blinn-Phong优化
    • 半角向量 h = l + v ∥ l + v ∥ \mathbf{h} = \frac{\mathbf{l} + \mathbf{v}}{\|\mathbf{l} + \mathbf{v}\|} h=l+vl+v
    • 高光项改写为 ( n ⋅ h ) α (\mathbf{n} \cdot \mathbf{h})^\alpha (nh)α,计算效率更高

1.3 坐标系变换与关键推导

1.3.1 反射向量 r \mathbf{r} r 的严格推导

设入射方向 l \mathbf{l} l 为指向表面的单位向量,法线 n \mathbf{n} n 为单位向量:

  1. l \mathbf{l} l 分解为法线分量和切平面分量:
    l = ( n ⋅ l ) n + l ⊥ \mathbf{l} = (\mathbf{n} \cdot \mathbf{l})\mathbf{n} + \mathbf{l}_{\perp} l=(nl)n+l
  2. 反射方向 r \mathbf{r} r 的法线分量反向,切平面分量不变:
    r = − ( n ⋅ l ) n + l ⊥ = l − 2 ( n ⋅ l ) n \mathbf{r} = -(\mathbf{n} \cdot \mathbf{l})\mathbf{n} + \mathbf{l}_{\perp} = \mathbf{l} - 2(\mathbf{n} \cdot \mathbf{l})\mathbf{n} r=(nl)n+l=l2(nl)n
1.3.2 法线矩阵 N = ( M − 1 ) T \mathbf{N} = (\mathbf{M}^{-1})^T N=(M1)T 的证明

设模型变换矩阵为 M \mathbf{M} M,切线向量 t \mathbf{t} t 变换后为 M t \mathbf{M}\mathbf{t} Mt,法线 n \mathbf{n} n 变换后为 N n \mathbf{N}\mathbf{n} Nn。需满足:
( N n ) ⋅ ( M t ) = 0 (\mathbf{N}\mathbf{n}) \cdot (\mathbf{M}\mathbf{t}) = 0 (Nn)(Mt)=0
展开得:
n T N T M t = 0 \mathbf{n}^T \mathbf{N}^T \mathbf{M} \mathbf{t} = 0 nTNTMt=0
因原始法线与切线正交( n T t = 0 \mathbf{n}^T \mathbf{t} = 0 nTt=0),需有:
N T M = I    ⟹    N = ( M − 1 ) T \mathbf{N}^T \mathbf{M} = \mathbf{I} \implies \mathbf{N} = (\mathbf{M}^{-1})^T NTM=IN=(M1)T

1.3.3 视线方向 v \mathbf{v} v 的计算

在视图坐标系中,设表面点坐标为 p \mathbf{p} p,则:
v = − p ∥ p ∥ \mathbf{v} = -\frac{\mathbf{p}}{\|\mathbf{p}\|} v=pp

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/78841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言中数组与指针:差异、应用及深度剖析

在C语言编程领域中,数组和指针是极为重要的概念,它们各自扮演着独特的角色,既有着紧密的联系,又存在显著的区别。深入理解它们的作用与差异,是掌握C语言编程的关键。 数组:数据的有序集合 数组是一组具有相…

【AI大模型】大模型RAG技术Langchain4j 核心组件深入详解

目录 一、前言 二、Langchain4j概述 2.1 Langchain4j 是什么 2.2 Langchain4j 主要特点 2.3 Langchain4j 核心组件 2.4 Langchain4j 核心优势 三、Langchanin4j组件应用实战 3.1 前置准备 3.1.1 导入如下依赖 3.1.2 获取apikey 3.1.3 获取官方文档 3.2 聊天组件 3.…

Web渗透之文件包含漏洞

文件包含漏洞原理 1、源代码 <?php$filename $_GET[filename]; include $filename; //或include_once,require,require_onceecho "欢迎来到PHP的世界.";?> 2、利用条件 php.ini中alllow_url_fopenOn(默认开启)和allow_url_includeOff(默认关闭)要开启…

MySQL 中查询 VARCHAR 类型 JSON 数据的

在数据库设计中&#xff0c;有时我们会将 JSON 数据存储在 VARCHAR 或 TEXT 类型字段中。这种方式虽然灵活&#xff0c;但在查询时需要特别注意。本文将详细介绍如何在 MySQL 中有效查询存储为 VARCHAR 类型的 JSON 数据。 一、问题背景 当 JSON 数据存储在 VARCHAR 列中时&a…

路由器开启QOS和UPNP的作用

QOS 的作用 保障关键业务带宽&#xff1a;可根据网络应用的重要性分配带宽。比如在家庭网络中&#xff0c;当多人同时使用网络时&#xff0c;将视频会议等实时性要求高的关键业务设置为高优先级&#xff0c;确保其能获得足够带宽&#xff0c;避免卡顿&#xff0c;而文件下载等…

5G网络下客户端数据业务掉线频繁

MCPTT&#xff08;Mission Critical Push-to-Talk&#xff09;客户端的日志&#xff0c;和界面在待机状态下&#xff08;即没有做通话等业务操作&#xff09;&#xff0c;会频繁提示“离线”。 主要先看有没有丢网&#xff0c;UL BLER有没有问题。确认没有问题。看到业务信道释…

使用Python和Matplotlib可视化字体轮廓:从路径数据到矢量图形

引言 字体设计和矢量图形处理是编程中一个有趣且实用的领域。通过Python的matplotlib库&#xff0c;我们可以轻松将字体轮廓的路径数据转换为直观的矢量图形。本文将带你一步步实现这一过程&#xff0c;并解析代码细节&#xff0c;帮助你理解如何将复杂的路径指令转化为可视化…

4.13日总结

javafx中实现发送qq邮箱验证码: 手动导入jar包方法&#xff1a; 第一步&#xff1a;开启QQ邮箱的 POP3/IMAP 或者 SMTP/IMAP 服务 打开qq邮箱&#xff08;电脑端&#xff09;&#xff0c;找到设置里的账号与安全的安全设置&#xff0c;往下滑就可以找到 POP3/IMAP 或者 SMTP…

智慧乡村数字化农业全产业链服务平台建设方案PPT(99页)

1. 农业全产业链概念 农业全产业链是依托数字化、电子商务、云计算等技术&#xff0c;整合规划咨询、应用软件设计与开发等服务&#xff0c;推动农业产业升级和价值重塑&#xff0c;构建IT产业融合新生态。 2. 产业链技术支撑 利用云计算、大数据、区块链等技术&#xff0c;为…

k8s的配置文件总结

在 Kubernetes 中&#xff0c;配置文件 是定义集群资源的核心&#xff0c;通常以 YAML 或 JSON 格式编写。以下是 Kubernetes 中关键的配置文件类型及其作用&#xff1a; 1. 核心工作负载配置 (1) Deployment • 用途&#xff1a;定义无状态应用的 Pod 副本管理策略&#xff…

STM32(基于标准库)

参考博客&#xff1a;江科大STM32笔记 Stm32外设 一、GPIO 基础 GPIO位结构 I/O引脚的保护二极管是对输入电压进行限幅的上面的二极管接VDD, 3.3V,下面接VSS, 0V&#xff0c;当输入电压 >3.3V 那上方这个二极管就会导通&#xff0c;输入电压产生的电流就会大部分充入VD…

为什么我们需要if __name__ == __main__:

[目录] 0.前言 1.什么是 __name__&#xff1f; 2.if __name__ __main__: 的作用 3.为何Windows更需if __name__ &#xff1f;前言 if __name__ __main__: 是 Python 中一个非常重要的惯用法&#xff0c;尤其在使用 multiprocessing 模块或编写可导入的模块时。它的作用是区分…

速盾:高防CDN的原理和高防IP一样吗?

随着互联网的发展&#xff0c;网络安全威胁日益严重&#xff0c;尤其是DDoS攻击、CC攻击等恶意行为&#xff0c;给企业带来了巨大的风险。为了应对这些挑战&#xff0c;许多企业开始采用高防CDN&#xff08;内容分发网络&#xff09;和高防IP作为防御措施。尽管两者都能提供一定…

《算法笔记》3.6小节——入门模拟->字符串处理

1009 说反话 #include <cstdio>int main() {char sen[80][80];int num0;while(scanf("%s",sen[num])!EOF){num;}for (int i num-1; i > 0; --i) {printf("%s ",sen[i]);}printf("%s\n",sen[0]);return 0; }字符串连接 #include <io…

供应链业务-供应链全局观(三)- 供应链三流的集成

概述 供应链的全局观的全两篇文章主要描述了供应链的基础概念和供应链的协作和集成问题。 供应链业务-供应链全局观&#xff08;一&#xff09;定义了什么是供应链和供应链管理。 所谓供应链就是把采购进来的东西&#xff0c;通过自身的生成加工&#xff0c;进行增值服务&am…

链表-算法小结

链表 单链表 双链表 循环链表 链表_stl-CSDN博客 虚拟头结点 反转链表 删除链表元素 方法一: 直接使用原来的链表来进行删除操作。 头节点是否为空头链表的值是否为要删除的值头结点删除后,新的头节点是否依旧要删除 ,删除后的,新头节点可能是空结点 方法二: 设置一个虚拟…

C语言中常用的调试宏和函数总结(__LINE__、__FUNCTION__)

表格&#xff1a;C语言调试工具 类别工具描述示例代码预定义宏__LINE__表示当前源代码的行号。printf("Error occurred at line %d\n", __LINE__);__FILE__表示当前源代码文件的名称。printf("Error occurred in file %s\n", __FILE__);__func__表示当前函…

DotnetCore开源库SampleAdmin源码编译

1.报错: System.Net.Sockets.SocketException HResult0x80004005 Message由于目标计算机积极拒绝&#xff0c;无法连接。 SourceSystem.Net.Sockets StackTrace: 在 System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, C…

如何使用切片操作来处理序列数据

1 问题 本文主要探究 Python 中切片操作的原理和应用。具体来说&#xff0c;我们将分析切片的基本语法、切片的步长和切片的边界&#xff0c;并通过示例代码展示如何使用切片操作来处理序列数据。 2 方法 为了更好地理解切片操作&#xff0c;我们采用如下的思路学习python中的切…

java(二):java的运算和流程控制

java中单引号和双引号区别和用法 区别1&#xff1a;java中的单引号表示字符&#xff0c;双引号表示字符串。 区别2&#xff1a;单引号引的数据一般是char类型的&#xff1b;双引号引的数据 是String类型的。 区别3&#xff1a;java中单引号里面只能放一个字母或数字或符号&…