简单镜面反射

2023-11-20

前言:本篇博客只是一个简单的实现镜面反射功能的例子,主要是当做笔记使用。

通过反射光向量实现流程:如下所示:
核心要点:如下所示:
1.顶点镜面反射颜色值等于反射光颜色乘以反射强度来获取。而入射光向量,法向量,视角向量,反射光向量的关系图如下所示:
在这里插入图片描述
2.入射光向量实际上就是光源指向顶点的向量。要获取该向量,可以使用顶点指向光源向量的反操作获取(也就是-WorldSpaceLightDir处理)。

3.反射光向量可以通过将法向量和入射光向量作为参数来调用reflect函数进行获取。当然也可以通过法向量和入射光向量来推导出反射光向量,具体推导流程如下图所示:
在这里插入图片描述
1>.这里的L就是入射光向量的反向向量(也就是光照向量),N就是法向量,且L和N都是单位向量(模长为1)。
2>.将L平移至L’,将R平移至R’。此时由于L和R都是模长为1的单位向量。所以由此组成的平行四边形的对角线互相平分且互相垂直。
3>.L+R = R+L’=S,所以R = S - L。因此只需要计算出S向量,就可以推导出反射光R向量。
4>.S向量模长就是等于顶点到S点对角线的长度。由第2条得知当前平行四边形对角线互相平分且垂直,此时假设顶点到对角线交叉点的距离为d,那么S向量的模长就是2d。而cos(a) = d / L模长,又因为dot(N,L) / (L模长 * N模长) = cos(a),所以dot(N,L) / (L模长 * N模长) = d / L模长。由于L和N都是单位向量,对应模长都是1,所以dot(N,L) = d。因此S向量的长度为2 * dot(N,L) 。
5>.由于S向量和N向量在相同方向上,只是模长不同,所以 S模长 / N模长 = S向量 / N向量。由于N向量是单位向量,模长为1,所以就可以推导出S向量 = S模长 * N向量,也就是S = 2 * dot(N,L) * N。
6>.结合第3点和第5点可以得出:反射光向量R = 2 * dot(N,L) * N。

4.视角向量实际上就是顶点指向摄像机的向量。要获取该向量,可以使用摄像机向量减去顶点向量来获取(也就是调用WorldSpaceViewDir处理)。

5.获取反射光向量和视角向量的点积值。当点积值为负数时表示反射光在平面背面,可以不做反射光照处理。因此可以将点积值限制在0~1之间,并且点积值越大,反射强度越大,夹角越小。

6.反射强度并不是像第5点中那样描述的线性变化,而是存在平滑系数来控制反射强度衰减大小。平滑系数越大,反射强度乘以平滑系数平方值趋于0的顶点就会相对变多,从而高光范围就越小,反射光照越集中。平滑系数越小,反射强度乘以平滑系数平方值趋于0的顶点就会相对变少,从而高光范围就越大,反射光照越分散。

核心代码:如下所示:

Shader "Custom/demo12" {
	Properties {
		// 镜面反射光颜色
		_SpecularColor("Specular Color", COLOR) = (1, 0, 0, 1)
		// 镜面光平滑度
		_SpecularShiness("Specular Shiness", Range(1, 64)) = 8
	}

	SubShader {
		pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"
			#include "lighting.cginc"

			struct v2f {
				float4 pos:POSITION;
				fixed4 col:COLOR;
			};

			fixed4 _SpecularColor;
			float _SpecularShiness;

			v2f vert(appdata_base i)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
				
				// 获取世界空间中不受缩放影响的归一化法向量:使用模型到世界矩阵的逆矩阵的转置矩阵进行变换模型空间中的法向量
				float3 N = normalize(mul(float4(normalize(i.normal), 0), unity_WorldToObject).xyz);
				// 获取世界空间中的归一化光照向量
				float3 L = normalize(_WorldSpaceLightPos0.xyz);
				// 获取世界空间中法向量和光照向量的点积值:值越大,漫反射强度越大,角度越小。
				// 将点积值为负数时表示光照在平面背面,此时可以不做光照处理。因此将点积值要限制在0~1之间
				float ndotl = saturate(dot(N, L));
				// 使用光照强度乘以世界空间中的光照颜色,获取顶点的漫反射颜色
				fixed4 myDiffuse = _LightColor0 * ndotl;

				// 获取世界空间中入射光向量:光源指向顶点的向量,可以使用顶点指向光源向量的反操作获取。
				float3 I = -WorldSpaceLightDir(i.vertex);
				// 获取世界空间中归一化反射光向量
				float3 R = normalize(reflect(I, N)); //等价于normalize(2 * dot(N, L) * N - L);
				// 获取世界空间中归一化视角向量:顶点指向摄像机的向量
				float3 V = normalize(WorldSpaceViewDir(i.vertex));
				// 获取世界空间中反射光向量和视角向量的点积值:值越大,反射强度越大,夹角越小。
				// 将点积值为负数时表示反射光在平面背面,此时可以不做光照处理。因此将点积值要限制在0~1之间
				float3 rdotv = saturate(dot(R, V));
				// 使用平滑系数来控制反射强度衰减大小:
				// 平滑系数越大,反射强度乘以平滑系数平方值趋于0的顶点就会相对变多,从而高光范围就越小,反射光照越集中。
				// 平滑系数越小,反射强度乘以平滑系数平方值趋于0的顶点就会相对变少,从而高光范围就越大,反射光照越分散。
				float specularScale = pow(rdotv, _SpecularShiness);
				// 使用反射强度乘以反射光颜色,获取顶点最终反射颜色
				fixed4 mySpecular =  _SpecularColor * specularScale;

				// 获取漫反射 + 高光反射 + 环境光混合颜色
				o.col = myDiffuse + mySpecular + UNITY_LIGHTMODEL_AMBIENT;

				return o;
			}

			fixed4 frag(v2f i):COLOR
			{
				return i.col;
			}

			ENDCG
		}
	}
}

运行效果:如下所示:
在这里插入图片描述
通过半角向量实现流程:如下所示:
核心要点
1.顶点镜面反射颜色值等于反射光颜色乘以反射强度来获取。而光照向量,法向量,视角向量和半角向量的关系如图所示:
在这里插入图片描述
2.光照向量实际上就是顶点指向光源的向量。要获取该向量,可以使用光源向量减去顶点向量来获取(也就是调用WorldSpaceLightDir处理)。

3.视向量实际上就是顶点指向摄像机的向量。要获取该向量,可以使用摄像机向量减去顶点向量来获取(也就是调用WorldSpaceViewDir处理)。

4.半角向量可以使用光照向量与视角向量相加来获取。

5.获取半角向量和法向量的点积值。当点积值为负数时表示反射光在平面背面,可以不做反射光照处理。因此可以将点积值限制在0~1之间,并且点积值越大,反射强度越大,夹角越小。

6.反射强度并不是像第5点中那样描述的线性变化,而是存在平滑系数来控制反射强度衰减大小。平滑系数越大,反射强度乘以平滑系数平方值趋于0的顶点就会相对变多,从而高光范围就越小,反射光照越集中。平滑系数越小,反射强度乘以平滑系数平方值趋于0的顶点就会相对变少,从而高光范围就越大,反射光照越分散。

核心代码:如下所示:

Shader "Custom/demo13" {
	Properties {
		// 镜面反射光颜色
		_SpecularColor("Specular Color", COLOR) = (1, 0, 0, 1)
		// 镜面光平滑度
		_SpecularShiness("Specular Shiness", Range(1, 64)) = 8
	}

	SubShader {
		pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 
			#include "unitycg.cginc"
			#include "lighting.cginc"

			struct v2f {
				float4 pos:POSITION;
				fixed4 col:COLOR;
			};

			fixed4 _SpecularColor;
			float _SpecularShiness;

			v2f vert(appdata_base i)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
				
				// 获取世界空间中不受缩放影响的归一化法向量:使用模型到世界矩阵的逆矩阵的转置矩阵进行变换模型空间中的法向量
				float3 N = normalize(mul(float4(normalize(i.normal), 0), unity_WorldToObject).xyz);
				// 获取世界空间中的归一化光照向量
				float3 L = normalize(_WorldSpaceLightPos0.xyz);
				// 获取世界空间中法向量和光照向量的点积值:值越大,漫反射强度越大,角度越小。
				// 将点积值为负数时表示光照在平面背面,此时可以不做光照处理。因此将点积值要限制在0~1之间
				float ndotl = saturate(dot(N, L));
				// 使用光照强度乘以世界空间中的光照颜色,获取顶点的漫反射颜色
				fixed4 myDiffuse = _LightColor0 * ndotl;

				// 获取世界空间中归一化视角向量:顶点指向摄像机的向量
				float3 V = normalize(WorldSpaceViewDir(i.vertex));
				// 获取世界空间中归一化的半角向量
				float3 H = normalize(L + V);
				// 获取世界空间中半角向量和法向量的点积值:值越大,反射强度越大,夹角越小。
				// 将点积值为负数时表示反射光在平面背面,此时可以不做光照处理。因此将点积值要限制在0~1之间
				float3 hdotv = saturate(dot(H, N));
				// 使用平滑系数来控制反射强度衰减大小:
				// 平滑系数越大,反射强度乘以平滑系数平方值趋于0的顶点就会相对变多,从而高光范围就越小,反射光照越集中。
				// 平滑系数越小,反射强度乘以平滑系数平方值趋于0的顶点就会相对变少,从而高光范围就越大,反射光照越分散。
				float specularScale = pow(hdotv, _SpecularShiness);
				// 使用反射强度乘以反射光颜色,获取顶点最终反射颜色
				fixed4 mySpecular =  _SpecularColor * specularScale;

				// 获取漫反射 + 高光反射 + 环境光混合颜色
				o.col = myDiffuse + mySpecular + UNITY_LIGHTMODEL_AMBIENT;

				return o;
			}

			fixed4 frag(v2f i):COLOR
			{
				return i.col;
			}

			ENDCG
		}
	}
}

运行结果:如下图所示:
在这里插入图片描述
知识小点
1.兰伯特模型就是包含环境光和漫反射光照处理的模型。
2.Phong模型和Bill-Phong模型就是在兰伯特模型的基础上增加了一个镜面高光处理的模型。其中Phong模型是通过反射光向量(法向量和入射光向量点积获取)实现镜面高光处理,而Bill-Phong模型是通过半角向量(光照向量和视角向量相加获取)实现镜面高光处理。

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

简单镜面反射 的相关文章

  • 利用TBDBitmapData对象查找两张图片上的不同

    利用TBDBitmapData对象查找两张图片上的不同 从右上角开始利用双层循环遍历两图上的所有象素点 并相互比较 不完整代码如下 procedure TForm1 Button5Click Sender TObject var Bmp1
  • [人工智能-深度学习-53]:循环神经网络 - LSTM长短记忆时序模型的简化:门控循环网络GRU模型

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121599096 目录 第1章 前序知
  • HTML开始历程Day01

    1 HTML 其实就是网页基本结构 CSS 功能 美化网页 不让网页太单调 JS 功能 能让网页动起来 产生很多的交互行为 HTML是什么 Hyper Text Markup Language 超文本标记语言 服务器端口号 路径位置 HTM
  • Linux下用命令行编译运行Java总结

    最近使用腾讯云的Cloud Studio写Java 只能使用命令行进行编译运行 趁此机会 学习一下Linux的一些常用命令 平时windows下IDE用习惯了 现在用命令行进行编译运行 发现其实问题还是挺多的 所以写下这篇文章 1 java
  • WordPress主题开发 — 模版循环(条件判断、多个循环、新建查询和文章循环)

    循环是 WordPress 通过主题模板文件输出文章的默认机制 在循环中 WordPress 遍历当前页面获取到的所有文章 然后使用主题中的模版标签将其格式化并输出 我们可以用 WordPress 循环来做很多事情 例如 在网站首页显示多个
  • java IO、NIO、AIO详解

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 一 IO流 同步 阻塞 二 NIO 同步 非阻塞 三 NIO2 异步 非阻塞 正文 回到顶部 概述 在我们学习Java的IO流之前 我们都要了解几个关键词 同步与异步 sy
  • hyperledger fabric介绍

    一 Hyperledger Fabric介绍 2015年 Linux基金会启动了Hyperledger项目 目标是发展跨行业的区块链技术 Hyperledger Fabric是Hyperledger中的一个区块链项目 包含一个账本 使用智能
  • 使用php生成6位密码大全,php生成随机产生六位数密码的代码

    php生成随机产生六位数密码的代码 供大家学习参考 复制代码代码示例 随机产生六位数密码Begin function randStr len 6 format ALL switch format case ALL chars ABCDEFG
  • 《CTF特训营》学习总结——Reverse:逆向分析概述

    一 逆向分析的主要方法 逆向分析主要是将二进制机器码进行反汇编得到汇编代码 在汇编代码的基础上 进行功能分析 经过反编译生成的汇编代码中缺失了源代码中的符号 数据结构等信息 因此需要尽可能地通过逆向分析还原以上信息 以便分析程序原有逻辑和功
  • qt样式表设置边框_QT 样式风格 & 样式表 (QStyleSheet)

    QT Style的机制和GTK的Style机制很类似 基本上就是 定义了一个基础的Style类 在Style类里面定义一系列的绘图相关函数接口 具体风格的Style类实现了这些函数接口 在控件的实现中 控件的绘图函数调用Style类的绘图函
  • 导航电子地图的制作过程

    背景知识 1 导航原理 现代导航通过实时测定运动客体的当前位置及速度 方向等运动参数 以此为基础通过分析和计算 确定若干条符合某些条件要求如 距离 速度 时间 方向 的路线和行驶方案 然后利用系统进行引导和控制客体沿确定路线行驶 并提供必要
  • 软件测试基础——WEB测试模块

    软件测试工程师体系 web测试模块 web测试模块脑图 本文内容以脑图形式展示
  • 什么是接口测试,如何做接口测试?

    比起点点点的功能测试 接口测试 显得专业又高大上 也因此让有些初级测试人员 望而生畏 别担心 其实接口测试也是功能测试的一种 它是针对接口进行的功能测试 写在前面 本文参考了茹炳晟老师的 测试工程师 全栈技术进阶与实践 并结合自己的理解进行
  • Kafka 监控系统Eagle 使用教程 V1.4.0

    1 下载安装zookeeper 2 下载安装kafka 3 下载安装kafka eagle http download kafka eagle org tar zvxf kafka eagle bin 1 4 0 tar gz 4 配置JA
  • 命令注入漏洞(1)

    命令注入漏洞原理 其实命令注入漏洞也叫命令行注入漏洞 此漏洞是指web应用程序中调用了系统可执行命令的函数 而且输入的参数是可控的 如果黑客拼接了注入命令 就可以进行非法操作了 靶机搭建 链接 https pan baidu com s 1
  • 用栈实现括号匹配问题

    通过观察 我们可以发现 括号匹配的字符串 左括号与右括号数目一定相等 且遇到右括号时 必定有与之相匹配的括号在之前最近出现过 这样 可以整理解决问题的思路如下 假设有一串带括号的字符串 依次访问每一个字符 遇到左括号入栈 遇到右括号时 取栈
  • 有关bool(布尔)类型在C语言中的应用

    文章目录 前言 一 bool类型是什么 二 使用举例 总结 前言 由于学习过程中接触到了bool类型 产生了浓厚的兴趣 便写下这一篇文章来阐述bool类型的大概情况 一 bool类型是什么 bool 布尔 是在C99标准中引入的类型 是以英
  • GPT-4来了,但大模型的诸多未解之谜仍然未解

    导语 在3月14日 OpenAI 的 GPT 4 正式发布 它拥有多模态能力 可以接受图像输入并理解图像内容 可接受的文字输入长度增加到 3 2 万个 token 在多种专业和学术基准测试中取得好成绩 然而 功能强大的 GPT 4 与早期的
  • 关于串口调试助手上面的DTR和RTS

    开发调试过程中 突然XCOM串口调试助手无法接发数据 而用了sscom却可以实现正常功能 emo了很久 对比了两个软件对串口的设置 包括波特率 停止位 校验位等设置 也没发现异端 以为是sscom这个软件禁用了XCOM 后来仔细比对发现 X

随机推荐

  • DDR布线要求及拓扑结构分析

    在DDR的PCB设计中 一般需要考虑等长和拓扑结构 等长比较好处理 给出一定的等长精度通常是PCB设计师是能够完成的 但对于不同的速率的DDR 选择合适的拓扑结构非常关键 在DDR布线中经常使用的T型拓扑结构和菊花链拓扑结构 下面主要介绍这
  • 动手学数据分析 Task3

    动手学数据分析 Task3 一 concat merge join 二 groupby 一 concat merge join concat方法可以在两个维度上拼接 默认纵向凭借 axis 0 拼接方式默认外连接 pd concat obj
  • window全局对象的全局变量与脚本的全局变量间的关系

    如果脚本中的变量声明出现在命名元素之前 那这个变量的存在就会组织元素获取他的window属性 而如果脚本中的变量声明出现在命名元素之后 那么变量的显示赋值会覆盖该属性的隐式值
  • 数据库系列MySQL:优化配置文件

    配置流程 1 MySQL文件目录中后缀名为 ini文件的就是MySQL的默认配置文件 2 程序启动会先加载配置文件中的的配置 之后才会真正启动程序 3 更改完配置文件设置后需要重新启动服务端才可以生效 优化方案一 服务器内存 4 8GB k
  • 删除C++ std::string字符串中的空格

    介绍一个使用标准库算法删除std string字符串中空格的方法 代码如下 std string str1 Hello world str1 erase std remove if str1 begin str1 end unsigned
  • unity: C#的Action Event Delegate的异同

    目录 一 Action 二 Event 三 Action和Event区别 四 Delegate 总结 Action Event Delegate的异同 前言 Action Event和Delegate都是C 语言中的重要概念 分别用于管理函
  • Human3.6M数据集下载

    Download H36M annotations mkdir data cd data wget http visiondata cis upenn edu volumetric h36m h36m annot tar tar xf h3
  • 利用Apache Tika分页解析pdf文件内容

    Apache Tika 实现pdf文档分页提取内容 Apache Tika是一个多功能的文档内容提取工具 可以提取多种类型的文档内容 常用的如pdf office等格式 网上的例子基本上都是提取整篇文档内容 实际上用Tika提取pdf等文档
  • QT 实现点击窗口以外任何位置即关闭窗口

    bool QTipLabel eventFilter QObject o QEvent e switch e gt type ifdef Q DEAD CODE FROM QT4 MAC case QEvent KeyPress case
  • LeetCode-----第八题-----字符串转换整数 (atoi)

    字符串转换整数 atoi 难度 中等 请你来实现一个 atoi 函数 使其能将字符串转换成整数 首先 该函数会根据需要丢弃无用的开头空格字符 直到寻找到第一个非空格的字符为止 接下来的转化规则如下 如果第一个非空字符为正或者负号时 则将该符
  • Java-spring相关八股

    1 Java中有哪几种方式来创建线程执行任务 继承Thread类 public class zhouyuThread extends Thread public static void main string args zhouyuThre
  • XL4015-ADJ 5A 大电流DC-DC原理图分享

    如有问题 请加扣扣群 460189483 最近咨询LM2596S ADJ 3A芯片竟然已经10元rmb了 于是找到5A负载能力的XL4015 ADJ 正品4元 以后就用这个代替啦 通过比较发现 XL4015的温度低一点 效率高一点 电流大一
  • Android 实时获取SurfaceView渲染的内容截图

    近期的需求 偶尔需要获取当前SurfaceView上渲染的内容视图 因为是通过网页端控制的 类似预览功能吧 百度了好久 没找到能用的 无意间发现了这个类PixelCopy java 网上没什么介绍 安卓系统封装的一个类PixelCopy j
  • 解决plt.title()中文显示问题

    我们直接用plt title 默认是显示英文 如图 应该在代码前加这几行代码就行了 解决中文显示问题 plt rcParams font sans serif SimHei plt rcParams axes unicode minus F
  • VMware虚拟机安装Ubuntu系统步骤详解

    VMware虚拟机安装Ubuntu系统步骤详解 Ubuntu系统介绍 VMware安装Ubuntu步骤 一 Ubuntu系统的下载 二 VMware workstation的下载安装 三 配置Ubuntu虚拟机系统 四 VMware安装Ub
  • vue插件(vue-print-nb)实现打印功能

    vue插件vue print nb实现打印功能 1 安装vue print nb 2 引入Vue项目 3 在组件中使用 4 vue print nb插件优化 1 安装vue print nb Vue2 0版本安装方法 npm install
  • 重命名文件或目录(renameTo)

    File or directory with old name File file new File oldname File or directory with new name File file2 new File newname R
  • list scala 当前位置 遍历_【Scala笔记——道】Scala List 遍历 foldLeft / foldRight详解...

    HOF foldLeft foldRight foldLeft 和 foldRight 都是对于 List 遍历的 高阶函数 是对列表遍历过程中进行函数操作的高阶函数抽象 List 遍历 假设有两个方法如下 求和 def sum ints
  • lyapunov直接法

    文章目录 定义6 6 Lyapunov第一定理 Lyapunov第二定理 用于刻画渐进稳定 内积分析 定义6 6 Lyapunov第一定理 假设 A C A subset C A C是闭的 如果存在A的邻域D和满足下面两条件的连续函数
  • 简单镜面反射

    前言 本篇博客只是一个简单的实现镜面反射功能的例子 主要是当做笔记使用 通过反射光向量实现流程 如下所示 核心要点 如下所示 1 顶点镜面反射颜色值等于反射光颜色乘以反射强度来获取 而入射光向量 法向量 视角向量 反射光向量的关系图如下所示