我们做光栅化模式的渲染都了解有两种比较常用的渲染方式,一个是blinnphong的渲染,一个是pbr的渲染。

blinnphong:

blinnphong的渲染模式更多的是一种经验值模拟光照对物体的效果。所以他不是一个正确的能量守恒的渲染方式。

blinnphong的渲染公式

其中是漫反射的颜色值,也就是我们的贴图乘颜色。是高光图乘高光颜色

是顶点法线与光线方向的点积,主要要得到光线与法线的角度,决定漫反射的颜色。

是顶点法线与半角向量的点积,主要得到光线照射到视角范围的角度。

原理:

为什么需要H呢?

按照现实的光照显示,应该是光线射到地面上每个分子然后进行反射或散射后吸收热量然后最终反射出来的光线。但是如果按照这个方式去判断究竟显示的是什么内容的话,那计算量会非常大。

而我们其实只关心的是射入我们眼球范围内的光照,那么我们就需要寻找跟我们眼球反射有关的物体反射信息,所以用到了半角向量,半角向量是通过入射光线到点、射线方向和点相加后得到的向量,实际上他反映了是与视线和灯光相关的法线信息。

那么我们要判断指定部位的光照强度就可以用半角向量与顶点的法线向量做点乘后得出。点乘值越大说明法线与半角向量越重合,那么高光肯定越高。

那么这里可能会考虑另一个问题,正常来说法线向量和半角向量重叠才能说明光线L射入了视线V中。为什么用来表示呢?因为点乘可能会再-1到1之间。

是因为我们真实世界是每个分子都能做该运算,然后照到瞳孔接收光线范围内就让我们看到了物体的颜色。但再光栅化下,每个像素可能对应的是多个分子,我们要做的只能是得出该像素下分子反射的平均值,如果我们要L和V的法线完全与H重叠的话,会导致一些像素上完全没有高光,看起来是不自然的。所以我们再光栅化下接受了点乘再-1到1之间的浮点数值来表示他再该像素上高光的比值。

说完参数原理,我们可以开始带入具体的参数来实现我们的效果了。

这里跟有关系的原因是,我们知道圆面积是,已经单位圆的半径r=1,所以单位圆面积就是

我们做的颜色控制都是再对单元圆做的,所以需要用颜色除单位圆面积得到具体面积内的颜色。

然后需要说下m,m是一个高光聚焦强度的值,m越大越聚焦,反之越小。同样需要跟单位圆做运算。可以试着把值带进去就知道效果了。

之后的Cspec就是高光的颜色信息,没什么可说的了。

代码:

Shader "Custom/BlinnPhong"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_SpecTex("SpecTex", 2D) = "white" {}_SpecColor("SpecColor", Color) = (1,1,1,1)_SpecM("Spec M", Range(0.0, 10.0)) = 0.5}SubShader{Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}Pass{Tags{ "LightMode" = "ForwardBase"}//设置光照类型Blend SrcAlpha OneMinusSrcAlpha   //开启颜色混合模式CGPROGRAM#pragma vertex vert                  //vextex着色器阶段#pragma fragment frag                //fragment着色器阶段#include "UnityCG.cginc" sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;sampler2D _SpecTex;float4 _SpecTex_ST;fixed4 _SpecColor;fixed _SpecM;//定义输入顶点着色器阶段的数据结构  struct Input{float4 vertex : POSITION;       //顶点位置float4 texcoord : TEXCOORD0;    //纹理坐标float4 normal : NORMAL;    float2 uv : TEXCOORD1;};//定义顶点着色器阶段输出的数据结构struct v2f{float4 pos:SV_POSITION;float2 uv:TEXCOORD0;float3 normalWorld : TEXCOORD1;float4 posWorld : TEXCOORD2;};//输出v2f到下一渲染阶段v2f vert(Input v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);float4 posWorld = mul(unity_ObjectToWorld, v.vertex);float3 normalWorld = UnityObjectToWorldNormal(v.normal);o.uv = v.uv;o.normalWorld = normalWorld;o.posWorld = posWorld;return o;}fixed4 frag(v2f i) :SV_TARGET{float3 lightDirWorld = normalize(_WorldSpaceLightPos0.xyz - i.posWorld.xyz);half3 viewDirWorld = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);half NdotL = saturate(dot(i.normalWorld, lightDirWorld));half3 halfDir = normalize(lightDirWorld + viewDirWorld);half NdotH = saturate(dot(i.normalWorld, halfDir));float4 mainTex = tex2D(_MainTex, i.uv) * _Color;float4 specTex = tex2D(_SpecTex, i.uv) * _SpecColor;half pi = 3.1415926;float3 color = (mainTex.rgb / pi + (_SpecM + 8) / (8 * pi) * specTex.rgb * (NdotH * NdotH * NdotH * NdotH * NdotH)) * NdotL;return fixed4(color.rgb, 1);}ENDCG}}
}

总结:

blinnphong是一个前人做出来的经验值算法,这个算法因为简单,运算低,所以不需要真实渲染或要求比较低的话可以用这个公式代替渲染方程。而且因为不需要真实渲染,这里也可以只用用GAMMA空间就足够了,不需要用到线性空间。整体运算量还是比较小的。

PBR:

因为BlinnPhong是一些渲染的经验模型,所以他的适用范围就比较受限,那么考虑到一个更通用,更合理的运算公式。一个基于物理平衡的渲染公式自然就被研究出来了。

原理:

PBR运算的是一个像素内光照反射和折射后的信息比值。

既然是基于物理平衡的真实渲染,那么pbr自然就有自己的一套理念(借用一下别人的总结):

  • 微平面理论(Microfacet Theory)。微平面理论是将物体表面建模成做无数微观尺度上有随机朝向的理想镜面反射的小平面(microfacet)的理论。在实际的PBR 工作流中,这种物体表面的不规则性用粗糙度贴图或者高光度贴图来表示。
  • 能量守恒(Energy Conservation)。出射光线的能量永远不能超过入射光线的能量。随着粗糙度的上升镜面反射区域的面积会增加,作为平衡,镜面反射区域的平均亮度则会下降。
  • 菲涅尔反射(Fresnel Reflectance)。光线以不同角度入射会有不同的反射率。相同的入射角度,不同的物质也会有不同的反射率。万物皆有菲涅尔反射。F0是即 0 度角入射的菲涅尔反射值。大多数非金属的F0范围是0.02~0.04,大多数金属的F0范围是0.7~1.0。
  • 线性空间(Linear Space)。光照计算必须在线性空间完成,shader 中输入的gamma空间的贴图比如漫反射贴图需要被转成线性空间,在具体操作时需要根据不同引擎和渲染器的不同做不同的操作。而描述物体表面属性的贴图如粗糙度,高光贴图,金属贴图等必须保证是线性空间。
  • 色调映射(Tone Mapping)。也称色调复制(tone reproduction),是将宽范围的照明级别拟合到屏幕有限色域内的过程。因为基于HDR渲染出来的亮度值会超过显示器能够显示最大亮度,所以需要使用色调映射,将光照结果从HDR转换为显示器能够正常显示的LDR。
  • 物质的光学特性(Substance Optical Properties)。现实世界中有不同类型的物质可分为三大类:绝缘体(Insulators),半导体(semi-conductors)和导体(conductors)。在渲染和游戏领域,我们一般只对其中的两个感兴趣:导体(金属)和绝缘体(电解质,非金属)。其中非金属具有单色/灰色镜面反射颜色。而金属具有彩色的镜面反射颜色。即非金属的F0是一个float。而金属的F0是一个float3,如下图。

渲染公式

Le是自发光,一些物体可能会有自发光的信息或者有自发光的图片,通过这个可以得到本身发光物体的颜色信息。

fr是入射与出射之间的反射比例,一般有BRDF,BTDF,BSDF(就是BRDF+BTDF),BSSRDF等。

Li是入射光亮度

是法线与反射的点乘,得出的值可以表示入射光的衰减

是入射方向的半球积分,可以立即理解为累计的数据值。

近似解

通过蒙特卡洛积分的方式,通过N项相加可以求出积分的近似解。

=*

环境光照

其中是环境光照,我们可以用cubemap 来代替=Cubemap.sample(r.mip)

BRDF

然后我们可以根据高光的BRDF来算自身颜色

其中需要说明的是VDF

D是法线分布函数,相当于是确定再一个像素内所有分子的法线分布相关信息的均值。体现得更好得法线分布函数会有更好得高光长尾。

F是Fresnel的简写,是一个反射与折射的关系式,我们现实环境中看物体,比如水面,垂直看下来会比较容易看到水底下的物体,是因为折射大于反射。越接近平面的看水面会看到反射的内容越多,这是因为反射大于折射。

V是一个结合函数(其实应该是G(Gemetry Function))这里描述的是被自身遮挡的信息。

三种情况会导致遮挡,一是光照照射不到的地方,二是视图方向看不到的物体信息,三是反射后进入视线的信息。

公式分别是:

其中F0是反射的值,分为导体和电解质,导体一般会有rgb三个不同的颜色,而电解质一般rgb三个颜色是一样的值。默认的电解质为(0.4,0.4,0.4),但也有可能一个像素内出现金属和非金属,所以我们需要用一个公式来近似这个解(金属比值加上非金属比值):(Metallic是当前像素的金属度)

,这里是对光照和对视线方向都做了GGX运算结合起来得到视线与光照方向的遮蔽信息。下面除于是对运算的值做光照方向和视线方向的纠正的值

公式为:,其中k为

最后为了能量守恒,应该要把能量消耗的损失加上:

也就是前面公式下的Kdiff和Kspec。

Kspec描述的是有多少光的能灵被高光反射,也就是前面的F0。

Kdiff则是高光吸收后剩下的光线消耗:

代码:

Shader "Custom/BRDF"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_SpecTex("SpecTex (RGB)", 2D) = "white" {}_SpecColor("SpecColor", Color) = (1,1,1,1)_Roughness("Roughness", Range(0.0, 10.0)) = 1_Albedo("Albedo (RGB)", 2D) = "white" {}_Metallic("Metallic", Range(0.0, 10.0)) = 1_GGX_V_Transition("GGX V Transition", Range(0.0001, 10.0)) = 0.0001}SubShader{Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}Pass{Tags{ "LightMode" = "ForwardBase"}//设置光照类型Blend SrcAlpha OneMinusSrcAlpha   //开启颜色混合模式CGPROGRAM#pragma vertex vert                  //vextex着色器阶段#pragma fragment frag                //fragment着色器阶段#include "UnityCG.cginc" sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;sampler2D _SpecTex;sampler2D _Albedo;fixed _Metallic;fixed4 _SpecColor;fixed _Roughness;half _GGX_V_Transition;//定义输入顶点着色器阶段的数据结构  struct Input{float4 vertex : POSITION;       //顶点位置float4 texcoord : TEXCOORD0;    //纹理坐标float4 normal : NORMAL;    float2 uv : TEXCOORD1;};//定义顶点着色器阶段输出的数据结构struct v2f{float4 pos:SV_POSITION;float2 uv:TEXCOORD0;float3 normalWorld : NORMAL;float4 posWorld : TEXCOORD1;};//输出v2f到下一渲染阶段v2f vert(Input v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);float4 posWorld = mul(unity_ObjectToWorld, v.vertex);float3 normalWorld = normalize(UnityObjectToWorldNormal(v.normal));o.uv = v.uv;o.normalWorld = normalWorld;o.posWorld = posWorld;return o;}half doubleNumber(half num){return num * num;}half fiveNumber(half num){return num * num * num * num * num;}half GGX(half dotX){half k = doubleNumber(_Roughness + 1) / 8;return dotX / (k + (1 - k) * dotX);}fixed4 frag(v2f i) :SV_TARGET{half3 lightDirWorld = normalize(_WorldSpaceLightPos0.xyz - i.posWorld.xyz);half3 viewDirWorld = normalize(_WorldSpaceCameraPos - i.posWorld.xyz);half3 halfDir = normalize(lightDirWorld + viewDirWorld);half NdotL = saturate(dot(i.normalWorld, lightDirWorld));half NdotH = saturate(dot(i.normalWorld, halfDir));half NdotV = saturate(dot(i.normalWorld, viewDirWorld));float4 mainTex = tex2D(_MainTex, i.uv) * _Color;float4 specTex = tex2D(_SpecTex, i.uv) * _SpecColor;float3 metalliTex = tex2D(_Albedo, i.uv).rgb;half pi = 3.1415926;half D = doubleNumber(_Roughness) / (pi * doubleNumber(doubleNumber(NdotH) * (doubleNumber(_Roughness) - 1) + 1));half3 F0 = metalliTex * _Metallic + (1 - _Metallic) * half3(0.04, 0.04, 0.04);half F = F0 + (1 - F0) * (1 - fiveNumber(NdotH));half lambertNL = NdotL * 0.5 + 0.5;half V = (GGX(NdotL) * GGX(NdotV)) / (4 * lambertNL * NdotV);half kdiff = F0;half kSpec = (1 - F0) * (1 - _Metallic);float3 brdf = (kdiff * mainTex.rgb / pi + kSpec * V * F * D);return fixed4(brdf.rgb, 1);}ENDCG}}
}

参考资料

https://mp.weixin.qq.com/s?__biz=MzA4MDc5OTg5MA==&mid=2650610133&idx=1&sn=4ab4e5bd554f842e8b18e4aaf204cf74&chksm=87970828b0e0813e5ae7d3ff4fae72d848b9cde9d16fb5a992a3f4f39f35a2a6dff61f2784e4&mpshare=1&scene=1&srcid=0102fNRbQaGB8DIIVdfb3778&sharer_sharetime=1578021589935&sharer_shareid=8df5fbc7718f1db8a2e1763ea8b1a99f&rd2werd=1#wechat_redirect

https://zhuanlan.zhihu.com/p/53086060?utm_source=ZHShareTargetIDMore&utm_medium=social&utm_oi=827612570348310528

PBR与Blinnphong解读相关推荐

  1. LOD优化策略-通篇

    LOD优化策略-场景 ◆Shader LOD • shader.globalMaximumLOD来指定不同画质的LOD值 • Shader内部定义多个SubShader,逐个降低计算和纹理采样 使用S ...

  2. 【教程】关于各种USB启动模式的解读 MBR、PBR

      1楼MBR介绍,2楼是分区PBR介绍. (转载请注明出处:http://bbs.wuyou.com/viewthread.php?tid=166641&highlight=) 系统开机或者 ...

  3. Physically Based Render初学者PBR整理

    Physically Based Render PBR的概念 PBR一开始是离线渲染的技术,主要用来制作电影.通过基于物理规则的光与物质交互的规律,来计算光照.这样可以得到和照片一样写实的渲染效果. ...

  4. 【技术美术图形部分】PBR直接光部分:Disney原则的BRDF和次表面散射模型

    写在前面 补充去年遗漏下的知识.很多叙述都是参考了众多大佬的文章!因为是作为个人学习总结的博客,所以直接卑微的借鉴过来了,后面会给出所有参考的文章. 另外,放上一个忘了在哪一篇知乎评论里的截图: 说的 ...

  5. [3D游戏开发实践] Cocos Cyberpunk 源码解读-一文搞定延迟渲染管线原理与实践

    Cocos Cyberpunk 是 Cocos 引擎官方团队以展示引擎重度 3D 游戏制作能力,提升社区学习动力而推出的完整开源 TPS 3D游戏,支持 Web, IOS, Android 多端发布. ...

  6. More DETAILS! PBR的下一个发展在哪里?

    最近几年图形学社区对PBR的关注非常高,也许是由于Disney以及一些游戏引擎大厂的助推,也许是因为它可以被轻松集成进实时渲染的游戏引擎当中,也许是因为许多人发现现在只需要调几个参数就能实现具有非常精 ...

  7. unity 2020 怎么写shader使其接受光照?_如何在Unity中造一个PBR Shader轮子

    之前有业界大佬建议我去了解下Unity的PBR.说来惭愧,我查找了下资料才发现自己在这方面的知识居然是一片空白.经过几周的学习与尝试我对这一块算是有了初步的了解,于是写了这篇文章,一方面对自己学到的东 ...

  8. PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现

    PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现 BRDF简介 再介绍BRDF之前我们要引入渲染方程这个东西: 其中L表示辐射率,其公式为: 它表示了一个拥有辐射强度Φ的 ...

  9. pbr发光 unity_PBR的基础理论

    参考文献: 腾讯课堂taecg 冯乐乐shader入门精要 浅墨CSDN博客文章 传统的贴图渲染技术与PBR渲染的区别: 传统的贴图渲染技术: 传统的贴图渲染技术一般是使用3张贴图来表现模型的效果:漫 ...

最新文章

  1. UI设计培训技术分享:搞定萌萌哒可爱图标
  2. VC++ 单文档和多文档应用程序
  3. java -jar 启动优化_Android 8.1 启动时间优化--耗时分析
  4. Swoole的TCP/IP HTTP WebSocket关系
  5. java nlpir_4-NLPIR汉语分词系统-JAVA
  6. 基于JS实现回到页面顶部的五种写法(从实现到增强)
  7. c语言微秒级延时,linux下写个C语言程序,要求有0.5微秒以下的延时,要怎样写...
  8. 简单的java信息显示_JSP实现简单的用户登录并显示出用户信息的方法
  9. Intellij IDEA 导入Maven项目
  10. caffee学习中文指南(1)(1)
  11. Unity3D 发布APK安卓环境配置步骤、安装、教程(含Java/Android)(超全流程)
  12. 基于ros单线激光雷达的坐标读取
  13. 改进型MPPT算法C源代码仿真
  14. Dos攻击与DDos攻击
  15. chia币新协议矿池搭建教程
  16. java模拟超市商品库存管理平台
  17. Python数据分析入门教程(更新中)
  18. Mac浏览器有好多种,那么哪款好用呢?
  19. 浅谈“全栈工程师需要掌握哪些技能”
  20. 算法基础——蓝桥杯(python实现,实际上大多数用c++更明白易懂)(第一部分,共12个小题)

热门文章

  1. CSS 垂直居中,使用absolute和transform
  2. 机房夜话——单点登陆实现
  3. sketchbook手机版_sketchbook pro手机版
  4. @Configuration详解
  5. Oracle oder by 的汉字排序
  6. 怎么获取到已经上架宝贝的淘宝新开店铺链接
  7. html阴影效果怎么做,如何利用CSS3阴影效果制作出立体感效果
  8. 腮腺炎,淋巴肿大,翳风穴疼,脸麻
  9. 小学计算机课题研究方案,小学信息技术课堂作业有效性的实践研究方案
  10. satisfactory 幸福工厂