简单镜面反射

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

通过反射光向量实现流程:如下所示:
核心要点:如下所示:
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模型是通过半角向量(光照向量和视角向量相加获取)实现镜面高光处理。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我猜测"c" 是指计算机编程语言中的一种。C是一种通用的、高级的编程语言,它广泛应用于软件开发领域。C语言由美国计算机科学家丹尼斯·里奇在20世纪70年代早期开发。他设计C语言的目的是为了开发UNIX操作系统。 C语言是一种在编写底层代码和操作系统时广泛使用的编程语言。它有简单的语法和强大的功能,允许程序员直接访问计算机硬件和内存。C语言也是其他高级语言,如C++和Java的基础。 在C语言中,程序员可以使用C语言的语法编写各种类型的程序,包括控制台应用程序、图形界面、嵌入式系统和网络应用程序。C语言的语法简单明了,理解和学习起来相对较容易。它支持面向过程编程和结构化编程风格。 C语言的特点之一是它的可移植性。C语言编写的程序可以在不同的操作系统和硬件平台上运行,只需稍微修改代码即可适应不同的环境。 此外,C语言还具有高效性和灵活性。由于C语言拥有底层访问硬件的能力,程序员可以编写高性能的程序。C语言的灵活性使得程序员可以通过使用指针和内存管理来进行复杂的数据结构和算法操作。 总体而言,C语言是一种重要且广泛应用的编程语言。它是软件开发领域的基础,并且在计算机科学教育中仍然被广泛教授和使用。无论是从事编程工作的专业人士还是初学者,都有必要学习和了解C语言。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值