Unity Shader:实现菲涅尔+色散效果以及相关原理解析
1,色散在光学中的原理
2,反射的原理以及环境映射的实现
3,折射的原理以及色散的实现
4,菲涅尔效果
5,将菲涅尔与色散效果增加到环境映射中
1,色散在光学中的原理
复色光
——现实生活中的许多光都是复色光,例如阳光。
光谱
——光学频谱,简称光谱,是复色光通过色散系统(如光栅、棱镜)进行分光后,依照光的波长(或频率)的大小顺次排列形成的图案。
https://zh.wikipedia.org/wiki/%E5%85%89%E5%AD%B8%E9%A0%BB%E8%AD%9C
色散
——复色光通过介质产生折射时,由于各个通过复色光中各个光的波长不同而导致的折射率不同所产生各个光线依次分开。
2,反射的原理以及环境映射的实现
反射可以用来解释你在镜子或水面中看到的景象。将你看向镜面的视角抽象为一组向量,当这组向量接触到镜面后会生成一组反射向量,而这组反射向量再次接触到的点的集合既是你在镜面中所见的景象。
二维中看起来比较简单,w为入射向量,r为反射向量,S为反射介质,P为反射点,n垂直于S。只要做到w与s的夹角与r与s的夹角相等,既做到了反射。
但在三维中,是通过一系列向量运算来寻找反射向量的。
首先计算w的projection,并移到下方。视觉上理解既是将w移到w’处,n向下延伸,从w’做一条垂直于n的虚线,n的延伸与虚线的交点既是向量wp的终点,wp=dot(w,n/|n|),然后再做一条wp。在wp*2的终点画一条’-w’,根据向量的加法法则,r’=wp*2-w, -r’=r既是w的反射向量。
用数学公式表达则是:
wp=dot(w,n/|n|) , wp is the projection of w on n.
r’=wp*2-w
r=-r’ ,w的反射向量
而在Shader中,以上这些都被简化成了一个方法:
float r=reflect(w,n);
- 1
环境映射既是利用反射对天空盒进行立方体采样,模拟现实中的镜像。
摄像机在镜面S上所见的景象为Skybox左边的点R。
以下代码为Unity官网的环境映射示例Shader。将插值与计算部分移到了片段着色器中,以优化显示效果。
Shader "Unlit/SkyReflection"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f {float3 normal:NORMAL;half3 worldRefl : TEXCOORD0;float4 pos : SV_POSITION;float4 objectPOS:float;};v2f vert (float4 vertex : POSITION, float3 normal : NORMAL){v2f o;o.pos = UnityObjectToClipPos(vertex);o.normal=normal;o.objectPOS=vertex;return o;}fixed4 frag (v2f i) : SV_Target{//i.normal既是上图中的n。i.normal = UnityObjectToWorldNormal(i.normal);//worldPos为上图中的P。float3 worldPos = mul(unity_ObjectToWorld, i.objectPOS).xyz;//worldViewDir为由P点射向camera的一条向量。float3 worldViewDir = UnityWorldSpaceViewDir(worldPos);//所以在reflect函数中要使用-worldViewDir,将方向颠倒过来,既成为了上图中的w。i.worldRefl既是//上图中的r。i.worldRefl = reflect(-worldViewDir, i.normal);//进行立方体采样,既是根据上图中的r寻找立方盒上的点R。half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);fixed4 c = 0;c.rgb = skyColor;return c;}ENDCG}}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
效果:
这里进一步解释一下,当利用反射进行环境映射时,有弧度的物体的效果要明显优于平面物体,因为有弧度的平面生成的反射向量角度更广,在进行立方体采样时模拟出的距离感更加真实。而由于天空盒与场景中的物体距离是有限的(而且其实并不是很远), 平面物体生成的反射向量大部分都反射到了天空盒的一侧的小部分区域,效果很失真,实际是由于这种反射”揭穿”了天空盒虚拟出的的无限远效果。
上图中立方体反射出星球的一小块区域。
3,折射的原理以及色散的实现
折射可以解释透过玻璃所看到的扭曲的景象。
假设g为玻璃,空气的折射率为n’,玻璃的折射率为n”
当入射向量接触到介质g后,根据折射率n’与n”的差别,它的前行方向将会发生变化。
这种变化可用斯涅耳定律来进行计算:
snell’s law: n′sinθi=n′′sinθT
在Shader中,折射的计算也被简化成了一个方法:
T=refract(I,n,etaRatio);
- 1
其中etaRatio=n’/n”。
各种介质的etaRatio一览:
————————————————
真空:1.0
水:1.3333
玻璃:1.5
钻石:2.417
水晶: 1.544-1.553
————————————————
色散的实现:
最上面解释过:复色光通过介质产生折射时,由于各个通过复色光中各个光的波长不同而导致的折射率不同所产生各个光线依次分开。
所以首先看下复色光中各个光的波长:
——————————
红:620-750nm
橙:590nm
黄:570nm
绿:495nm
青:450nm
蓝:420nm
紫:380nm
——————————
而由于折射率是取决于波长的,所以折射出的各个颜色的光的顺序是一致的。
代码,这里只用红绿蓝做示例:
先将折射率计算好,存在etaRatio的x,y,z中。
float3 Tred=refract(normalize(-worldViewDir),i.normal,etaRatio.x);float3 Tgreen=refract(normalize(-worldViewDir),i.normal,etaRatio.y);float3 Tblue=refract(normalize(-worldViewDir),i.normal,etaRatio.z);
- 1
- 2
- 3
算出折射后的向量,分别进行立方体采样,并只取相应的颜色。
float4 refractedColor;refractedColor.r=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tred),unity_SpecCube0_HDR).r;refractedColor.g=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tgreen),unity_SpecCube0_HDR).g;refractedColor.b=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tblue),unity_SpecCube0_HDR).b;
- 1
- 2
- 3
- 4
- 5
refractedColor既是在环境映射中模拟出的介质后方景象产生色散的效果。
实际是在环境映射的过程中偏移采样角度反向的模拟出色散。Tblue,Tgreen,Tred既是上面反射采样中的r,而入射角实际是在g的下方。
4,菲涅尔效果
意义:
当光从一种具有折射率为的介质向另一种具有折射率为的介质传播时,在两者的交界处(通常称作界面)可能会同时发生光的反射和折射。菲涅尔方程描述了不同光波分量被折射和反射的情况。
https://zh.wikipedia.org/wiki/%E8%8F%B2%E6%B6%85%E8%80%B3%E6%96%B9%E7%A8%8B
这个没能力具体的好好解释了。总之有一个公式在shader里可以使用,返回值将用于最终的反射颜色与折射颜色的lerp混合中。
公式中的fresnelBias,fresnelScale与fresnelPower的赋值将会影响最终效果,如何赋值的原理没找到。我在下面的例子中赋值的power=4,scale=0.1,bias=-0.2。试验中发现将返回值的可能范围尽量精确至0-1效果最佳。
float reflectionfactor=min(1.0f,max(0,fresnelBias+fresnelScale*pow(1.0f+dot(normalize(-worldViewDir),i.normal),fresnelPower)));
- 1
5,将菲涅尔与色散效果增加到环境映射中
综合以上写一个Shader:
Shader "Unlit/Dispersion"
{Properties {fresnelPower("fresnelPower",float)=0fresnelScale("fresnelScale",float)=0fresnelBias("fresnelBias",float)=0_r("r",float)=0_g("g",float)=0_b("b",float)=0}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f {float3 normal:NORMAL;float3 worldRefra : TEXCOORD0;float4 pos : SV_POSITION;float4 objectPOS:float;};//在外部可改为power=4,scale=0.1,bias=-0.2float fresnelPower=1.0f;float fresnelScale=1.0f;float fresnelBias=0.3f;//红,绿,蓝的折射率,从外部赋值,方便调整float3 etaRatio;// =float3(0.83f,0.67f,0.55f); //1/1.3 1/1.5 1/1.8float _r;float _g;float _b;v2f vert (float4 vertex : POSITION, float3 normal : NORMAL){v2f o;o.pos = UnityObjectToClipPos(vertex);o.normal=normal;o.objectPOS=vertex;return o;}//为了达到最佳效果,将所有计算都写在了片段着色器内。fixed4 frag (v2f i) : SV_Target{etaRatio=float3(_r,_g,_b);i.normal = UnityObjectToWorldNormal(i.normal);float3 worldPos = mul(unity_ObjectToWorld, i.objectPOS).xyz;float3 worldViewDir = UnityWorldSpaceViewDir(worldPos);//反射向量float R=reflect(-worldViewDir,i.normal);i.normal=normalize(i.normal);//计算用来模拟色散的折射向量float3 Tred=refract(normalize(-worldViewDir),i.normal,etaRatio.x);float3 Tgreen=refract(normalize(-worldViewDir),i.normal,etaRatio.y);float3 Tblue=refract(normalize(-worldViewDir),i.normal,etaRatio.z);//菲涅尔factor,用来混合反射与折射颜色。float reflectionfactor=min(1.0f,max(0,fresnelBias+fresnelScale*pow(1.0f+dot(normalize(-worldViewDir),i.normal),fresnelPower)));//如果利用color存此变量,可以省去min与max。// color reflectionfactor=fresnelBias+fresnelScale*pow(1.0f+dot(normalize(-worldViewDir),i.normal),fresnelPower);half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, R);//反射颜色float4 reflectedColor=half4(DecodeHDR (skyData, unity_SpecCube0_HDR),1.0f);float4 refractedColor;//折射颜色refractedColor.r=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tred),unity_SpecCube0_HDR).r;refractedColor.g=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tgreen),unity_SpecCube0_HDR).g;refractedColor.b=DecodeHDR (UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, Tblue),unity_SpecCube0_HDR).b;refractedColor.a=1.0f;fixed4 c=0;//混合c=lerp(refractedColor,reflectedColor,reflectionfactor.x);return c;}ENDCG}}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
效果:
上图中一个玻璃球使用普通折射Shader,一个使用菲涅尔色散Shader。
加在一个人形模型上。
利用此Shader表现的晶体材质更加真实。
——————————————————————————————————
参考资料:
Cg Tutorial–Nvidia
维基百科
https://en.wikipedia.org/wiki/Chromatic_aberration
百度百科
http://baike.baidu.com/link?url=mfGv-8-d_GLdSBfpzsxV38M9ugwIxgbTlEe6neAQziOMVLuJ27_ZM7y0TKNYMbiknrE9aN8qW2ZTGD0pMRJ1bkyYEP_z_u2cNu3kbulXMQu
Unity Shader:实现菲涅尔+色散效果以及相关原理解析相关推荐
- Unity Shader:实现菲涅尔+色散效果的环境映射以及相关原理解析
文章目录 1,色散在光学中的原理 2,反射的数学计算方法以及用它实现环境映射 3,折射的原理以及色散的实现 4,菲涅尔效果 5,拥有菲涅尔与色散效果的环境映射 1,色散在光学中的原理 复色光 --现实 ...
- matlab 菲涅尔环,菲涅耳区讨论 - 通信原理与基础 - 通信人家园 - Powered by C114
1.菲涅耳区 当需要计算传播主区的几何尺寸时,要应用惠更斯-菲涅尔原理.惠更斯-菲涅尔原理认为,波在传播过程中,波面上的每一点都是一个进行二次辐射球面波(子波)的波源,而下一个波面,就是前一个波面所辐 ...
- unity shader 菲涅尔效果
老样子,还是先上效果图: 菲涅尔效果的原理大家可以自己去百度看看,网上有很多,这里就不多说了,我们直接看如何实现这个效果 实现思路: 1.求得视角方向,法线方向(世界空间下) 2.根据菲涅尔效果的公式 ...
- Unity Shader 水多种元素的实现(反射、折射、菲涅尔、深浅、浪花/泡沫、水波、可交互)
综合效果 经过各元素叠加 和 程序的审美调参 后的综合效果 交互的水波与边缘浪花的合并需要优化一下 反射 两种方案: cubeMap 以水面对称设一个摄像机 cubeMap 实现:反射探针生成Cube ...
- Unity shader Note :高级纹理(CubeMap反射折射菲涅尔,Rendermap镜子玻璃,程序纹理)
1.Cubemap–反射折射 使用脚本来创建Cubemap ①通过Camera.RenderToCubemap 把任意位置观察到的场景图制作成一张Cubemap之中 ②脚本使用自定义编译窗体的命令 – ...
- Shader学习21——基于菲涅尔透明的扫描线
看到人家这样的一个效果,于是想自己也复制一个 Mar-26-2021 17-16-26.gif 想了一下思路,应该是菲涅尔的做的一个透明效果(单纯的菲涅尔透明可以看这篇),再去做了一个线的效果,线应该 ...
- 菲涅尔公式实现边缘光效果
Shader"review/EdgeEmission" {///标准光照模型 光照数据=自发光(E)+漫反射光(D)+环境光(A)+高光(S)Properties{_MainTex ...
- Chango的数学Shader世界(七)水波模拟-透明水面,菲涅尔(Fresnel)效应
目的: 解析,改进,批评一个国外免费透明水面Shader,进一步了解Shader背后的物理原理. 参考: 菲涅尔反射 分析: 我将原水面Shader一再简化,从中抽取最主要的部分,忽略细枝末节,并改掉 ...
- 【技术美术】菲涅尔效果的实现
菲涅尔效果可以直接使用,但是为了了解实现的原理,就直接自己手动实现一个
最新文章
- 转--Android如何在java代码中设置margin
- 利用802.11x协议实现动态vlan的划分
- Ubuntu下在线安装mysql
- 【查看Linux带宽】nload、iptraf 的安装与使用
- 青蛙跳台阶问题(思路与蜂窝问题一致)
- 2017ICPCECIC C.A math problem(高次剩余)
- Linux SendMail服务启动慢总结
- android 新闻功能列表,android根据提供的接口获取新闻列表
- nmcli 命令的基本使用
- zabbix详解(十五)——zabbix proxy配置实战
- border 0px和border none的区别
- VirtualBox网络NAT模式的端口映射设置
- 【血糖检测】基于matlab改进深度回归网络的无创血糖检测【含Matlab源码 1572期】
- 新概念模拟电路_第一册_晶体管_读书笔记
- smarty 执行php,smarty模板执行原理
- 嵌入式-stm32学习:使用固件库点亮LED
- 企业网络冗余技术(EthernetChannel、HSRP、VRRP)
- [Codeforces #379 E. Anton and Tree]缩点+树上最长路
- NLP学习-1--语料预处理与向量空间,语料格式
- Android 最全 BATJ 大厂面试题整理!