【ShaderLab】Phong与Blinn-Phong两个光照模型的理解
正文
(顺手抄来一个图)
Phong与Blinn-Phong两个光照模型都是用来实现光照射在物体上,物体表现产生一个高光部分的效果的,两个模型也十分相似,毕竟后者是对前者进行了一个计算上的优化。虽然说两个光照模型都有了现成的代码函数封装,尤其是shaderforge插件跟新版本unity的shadergraph这两个东西,都能够直接拖拽几个图形化控件就能实现要说的这两个光照模型效果,简直是相当过份了。但理解原理之后,被别人问起来心都不会觉得虚。
Phong光照模型
理想情况下,光源射出的光线,通过镜面反射,正好在反射光方向观察,观察者可以接受到的反射光最多,那么观察者与反射方向之间的夹角就决定了能够观察到高光的多少。夹角越大,高光越小,夹角越小,高光越大。而另一个影响高光大小的因素是表面的光滑程度,表面越光滑,高光越强,表面月粗糙,高光越弱。L代表光源方向,N代表顶点法线方向,V代表观察者方向,R代表反射光方向。首先需要计算反射光的方向R,反射光方向R可以通过入射光方向和法向量求出,R + L = 2dot(N,L)N,进而推出R = 2dot(N,L)N - L。(至于如何推算出这个公式,CSDN有不少的博客都有笔记介绍,就是一个简单的向量与三角函数转换,不懂的可以百度下或者直接看这里)
(再顺手抄来一个图)
Blinn-Phong光照模型
Phong光照模型能够很好地表现高光效果,不过Blinn-Phong光照的缺点就是计算量较大,所以,在1977年,Jim Blinn对Phong光照进行了改进,称之为Blinn-Phong光照模型。
Blinn-Phong光照引入了一个概念,半角向量,用H表示。半角向量计算简单,通过将光源方向L和视线方向V相加后归一化即可得到半角向量。Phong光照是比较反射方向R和视线方向V之间的夹角,而Blinn-Phong改为比较半角向量H和法线方向N之间的夹角。半角向量的计算复杂程度要比计算反射光线简单得多,所以Blinn-Phong的性能要高得多,效果比Phong光照相差不多,所以OpenGL中固定管线的光照模型就是Blinn-Phong光照模型。
BlinnPhong光照模型的计算公式如下:
I(spcular) = I * k * pow(max(0,dot(N,H)), gloss) ,其中I为入射光颜色向量,k为镜面反射系数,gloss为光滑程度。
==================================================================
下面贴上两个光照模型的shaderlab代码
Shader "Custom/Phong" {Properties{_Specular("Specular", Color) = (1,1,1,1)_Diffuse("Diffuse",Color) = (1,1,1,1)_Gloss("Gloss",Range(1,255)) = 10}SubShader{Pass{Tags {"LightMode" = "ForwardBase" }LOD 200CGPROGRAM#include "Lighting.cginc"#pragma vertex vert#pragma fragment fragfixed4 _Diffuse;fixed4 _Specular;float _Gloss;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;};struct v2f{float4 pos:SV_POSITION;float3 worldNormal:NORMAL;float3 worldPos:TEXCOORD1;};v2f vert(a2v v){v2f o;//矩阵变换 获取投影坐标下的顶点o.pos = mul(UNITY_MATRIX_MVP, v.vertex);//获取世界坐标下的法线坐标o.worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));//o.worldNormal=normalize(mul((float3x3)_Object2World,v.normal));//与上面等价o.worldPos = mul(_Object2World, v.vertex).xyz;return o;}fixed4 frag(v2f i) :SV_Target{//获取环境光fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;//获取灯光方向 并归一化fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//归一化世界坐标下的法线fixed3 worldNormal = normalize(i.worldNormal);//获取漫反射fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));//获取反射光的方向 入射光方向为-worldLight,通过reflect函数获取反射光方向fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));//获取点在摄像机的观察位置,并归一化 (相机坐标-像素位置)fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);//phong的高光公式fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0, dot(reflectDir, viewDir)), _Gloss);fixed3 color = diffuse + ambient + specular;return fixed4(color, 1.0);}ENDCG}}FallBack "Specular"}
======================================================================
Shader "Custom/Blinn_Phong" {Properties{_Specular("Specular",color) = (1,1,1,1)_Diffuse("Diffuse",color) = (1,1,1,1)_Gloss("Gloss",Range(1,100)) = 20}SubShader{Pass{Tags { "RenderType" = "Opaque" }LOD 200CGPROGRAM#include "Lighting.cginc"#pragma vertex vert#pragma fragment fragfloat3 _Specular;float3 _Diffuse;float _Gloss;struct a2v {float4 vertex:POSITION;float3 normal:NORMAL;};struct v2f {float4 pos:SV_POSITION;float3 worldNormal:NORMAL;float3 worldPos:TEXCOORD0;};v2f vert(a2v a){v2f o;o.pos = mul(UNITY_MATRIX_MVP, a.vertex);o.worldNormal = mul(_Object2World, a.normal);o.worldPos = mul(_Object2World, a.vertex).xyz;return o;}float4 frag(v2f i):SV_Target{//获取环境光float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;//获取灯光 并且归一化float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//获取法线并归一化float3 worldNormal = normalize(i.worldNormal);//获取视线方向(摄像头位置-像素对应位置) 并归一化float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);//获取漫反射灯光float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));//获取半角向量(光线方向+视线方向) 并归一化float3 halfDir = normalize(worldLight + viewDir);//高光公式float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(halfDir, worldNormal)),_Gloss);float3 color = ambient + diffuse + specular;return float4(color,1.0);}ENDCG}}FallBack "Diffuse"}
=======================================================================================
代码里已经有对部分关键代码进行注释了,就不过多赘述。而Blinn-Pong是引入了一个半角向量来代替反射的向量,所以着色器的运算量少了挺多,下面就直接对Blinn-Phong的着色器代码进行总结。
Blinn-Pong光照着色器
引用光照模型"Lighting.cginc"
定义a2v结构体,要获取模型的顶点与法线
定义v2f结构体,存储顶点信息(SV_POSITION) 世界法线 和顶点在世界坐标的位置
顶点函数里需要获取
顶点的投影坐标
顶点的世界法线
顶点的世界坐标
片段函数里需要获取
环境光,需要将光照模型中的环境光(UNITY_LIGHTMODEL_AMBIENT.xyz)与Diffuse相乘
归一化世界法线
归一化光照位置(_WorldSpaceLightPos0.xyz)
归一化点的视角位置(_WorldSpaceCameraPos.xyz-worldPos)
漫反射(_LightColor0.rgb*Diffuse.rgb*saturate(dot(worldNormal,lightDir)))
归一化半角向量(viewDir+lightDir)
最后的高光公式LightColor0.rbg*_Specular.rbg*pow(dot(halfDir,worldNormal),_Gloss)
返回 高光+环境光+漫反射光
总结
这两个光照模型其实都有一个中文名,就叫冯光照模型与布林-冯光照模型,但换成了中文后感觉就拗口了不少啊。从理解上的话Phong模型可以更直观地推出结论,而后者的一个半角向量我也不知道是怎么推算出来的,但少了一个点乘运算感觉代码都简洁了不少,有种前人种树后人荫的爽快感。
偷懒了快一年最近才有空憋出了这一编个人理解,看来持之以恒还是一个不容易实现的词语啊。虽然平时也有做笔记的习惯,但感觉记了笔记就放在那里了,时间一久就基本忘光,回头看一看也得楞个很久才能悟过来,这样写写博客也算是对自己的记忆来一个回顾吧,还是希望能继续坚持下去吧。。。
【ShaderLab】Phong与Blinn-Phong两个光照模型的理解相关推荐
- 两个函数彻底理解Lua中的闭包
本文通过两个函数彻底搞懂Lua中的闭包,相信看完这两个函数,应该能理解什么是Lua闭包.废话不多说,上 code: 1 --[[************************************ ...
- 关于C语言异或实现两数交换的理解
首先麻烦了解这篇csdn文章: 使用异或运算实现两数交换_无关风月-CSDN博客_异或实现两数交换 异或^:相同为0:不同为1: 对于b=a^b^a;//我始终没有比较好的理解. 后来通过b/a*a, ...
- 两个月深入理解图像处理
学习目标: 两个月内掌握图像处理算法 学习内容: 小波在图像应用 分割算法 JPEG压缩编码算法 频域滤波 傅里叶变换 小波变换 阈值分割算法 图像变换与领域处理 图像复原 编码 形态学数学处理 遥感 ...
- XSS注入(1)-两个简单测试理解反射型xss注入和存储型xss注入
XSS注入(1)-两个例子理解反射型xss注入和存储型xss注入 XSS全称 Cross Site Script,为使与css语言重名,所以我们将其称为xss跨站脚本攻击.它指的是恶意攻击者往Web页 ...
- 【递归】两道程序题理解递归
递归的定义 在定义一个过程或者函数时,出现直接或间接调用自己的部分,称为递归.直接调用自己称为直接递归,间接调用自己称为间接递归. 第一题 编写一个递归函数计算最大公约数.使用该函数编程计算键盘输入两 ...
- 什么是epoll的水平触发与边缘触发?两段代码彻底理解
Edge trigger and level trigger of epoll 水平触发 对于读操作:只要缓冲内容不为空,LT模式返回读就绪. 对于写操作:只要缓冲区还不满,LT模式会返回写就绪. # ...
- 两道非常容易理解错的OSPF问题. 加深理解LSA的概念和ABR/ASBR的概念.
第一道题目,如下面拓扑所示,r3是ASBR,r3上rip重分发进ospf,为什么r1学习不到200.200.0.0的路由? 解答: 因为r3是ASBR,但是r2却不是ABR,所以区域2中没有4类LSA ...
- 通过两个实例来理解 devtool: 'source-map' 是什么意思
小编推荐:Fundebug专注于JavaScript.微信小程序.微信小游戏,Node.js和Java实时BUG监控.真的是一个很好用的bug监控费服务,众多大佬公司都在使用. 正如标题所说,有时候, ...
- JavaScript : 对LHS和RHS两个名词的理解
首先见名知意,"L"和"R"的含义,它们分别代表左侧和右侧 一.LHS查询 赋值操作左侧的查询,LHS查询试图找到变量的容器本身,,从而对其赋值 二.RHS查询 ...
最新文章
- tensorflow deep_speech2 神经网络结构代码分析
- webpack、rollup、parcel 它们的优劣?_尾货批发与正价批发优劣势,你有二者兼顾吗?...
- burpsuite配置指南
- JavaScript装逼指南
- RocketMQ集群搭建-4.2.0版本
- py-faster-rcnn + ZF 实现自己的数据训练与检测(二)
- 在Spring 框架中如何更有效的使用JDBC?
- 【GUI开发】图像处理类软件的浏览功能实现模型
- 看我72变:解决Entity Framework中枚举类型与tinyint的映射问题
- SQL SERVER 2008 索引、数据存储基本理论【原创】
- 金庸15部小说精校版
- 慕课版软件质量保证与测试(第一章.课后作业)
- js转换金额,元,万元
- 5G移动通信 笔记 - 系统架构与标准体系
- java邮件发送代码报错_javamail发送附件不通过也不报错
- Altium数据向Cadence数据转换向导(上)
- 学习淘淘商城第十六课(展示后台管理页面)
- 【微信小程序】小程序实现轮播图效果--swiper组件(一步步教你如何实现)
- 二项分布期望和方差的推导及推广
- .Header Manipulation漏洞
热门文章
- 酷派android手机怎么截屏,酷派手机怎么截屏啊第一页:安卓怎么截屏,截图在哪?安卓手机截...
- C语言:求最小公倍数
- c#期末考试知识点_C#期末复习资料
- 怎样才能吸引关键人才
- 经常使用的function Module
- 跨域解决方案CORS
- referer java_Java中伪造referer来获取数据
- 三菱服务器无法在线,三菱GX Works2和iQ Works常见问题
- 985211计算机大学名单排名,全国985211大学名单排名
- swif4基础学习(4)- 闭包、枚举