如何在unity3d pbr材质里面实现pbr

查看: 467回复: 10
Surforge 1.02 - 在Unity直接制作PBR的专用插件
元素币活跃度贡献值
元素皇, 积分 71610, 距离下一级还需 28390 积分
元素皇, 积分 71610, 距离下一级还需 28390 积分
土豪金3021
在线时间629 小时
马上注册成为元素者,获取海量元素资源,结识各路大神级人物
才可以下载或查看,没有帐号?
本帖最后由 成林 于
09:57 编辑
价格:500元素币 销售总额:1500元素币 购买人数:3尊敬的游客 ,本内容需要支付 500元素币 来下载,您可以 或者 。也可使用获取 同等点击次数 ,来轻松下载。
09:48 上传
元素币 +20
活跃度 +10
阅贴无数,楼主最强!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
享有帖子相关版权3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和的同意4、所有内容仅供个人学习、研究之用,请勿用于商业用途5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意7、管理员和版主有权不事先通知发贴者而删除本文', this.href);">论坛版权
元素币活跃度贡献值
元素王, 积分 12635, 距离下一级还需 17365 积分
元素王, 积分 12635, 距离下一级还需 17365 积分
在线时间135 小时
想要成大触,天天上元素!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素皇, 积分 70467, 距离下一级还需 29533 积分
元素皇, 积分 70467, 距离下一级还需 29533 积分
在线时间510 小时
来来,抢沙发
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
unity3D区版主
土豪金2028
在线时间1433 小时
每天一早上元素,挖矿撩妹两不误!
伟大的元素神,送我一个水灵灵的妹纸吧!
元素币活跃度贡献值
元素皇, 积分 42630, 距离下一级还需 57370 积分
元素皇, 积分 42630, 距离下一级还需 57370 积分
在线时间954 小时
这世上没有什么是元素币搞不定的,如果有,那就用更多!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素皇, 积分 87775, 距离下一级还需 12225 积分
元素皇, 积分 87775, 距离下一级还需 12225 积分
在线时间1702 小时
首发必发微元素,荣耀加身装备酷!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素王, 积分 27304, 距离下一级还需 2696 积分
元素王, 积分 27304, 距离下一级还需 2696 积分
在线时间507 小时
每天一早上元素,挖矿撩妹两不误!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素王, 积分 19787, 距离下一级还需 10213 积分
元素王, 积分 19787, 距离下一级还需 10213 积分
在线时间259 小时
想要成大触,天天上元素!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
在线时间5687 小时
带你赚币带你飞,元素里面有正妹!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素魔, 积分 2892, 距离下一级还需 2108 积分
元素魔, 积分 2892, 距离下一级还需 2108 积分
在线时间33 小时
元素帖子强,满满正能量!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
元素币活跃度贡献值
元素皇, 积分 42630, 距离下一级还需 57370 积分
元素皇, 积分 42630, 距离下一级还需 57370 积分
在线时间954 小时
首发必发微元素,荣耀加身装备酷!
还没有设置签名!您可以在此展示你的链接,或者个人主页!
【MP50】这次的冒险才刚刚开始,和所有故事一样,你得先有一个属于自己的法杖!即使他的法力会在30天后消失
【50小时】的学习,已经证明了你自己,这件法袍是你的了,由于使用了简陋的材料,能穿一个月已经不错了!
达成成就【元素贤者】在元素知道
超过20次解决提问者的问题
【拥有1000元素币】你就可以领取
达成成就【元素皇者】升级成为元素皇
【拥有3000元素币】其实也是很容易的事情
守望者【EX】
守望者EX的强大武器
加入【元素1年】自古美女爱英雄,这是千年不变的定律!英雄盾会为你们见证
种下一颗种子,许下一个愿望,小苹果可以帮你实现,直接获取任何一个售价低于2000元素币的资源
加入【元素6月】在元素冒险的时候,你一定会用到它,尤其是在遇到像小强这种可怕生物时
【1W元素币】的持有者,对于很多元素者来说,是一件值得骄傲的事情!
【200小时】的时间,足以打造出红龙之甲,接下来的半年,它将为你抵挡来自外界的各种伤害!
达成成就【皇室后裔】最先成为元素皇的30个人,或者土豪逆袭获取
【剑LV4,主题=100】-上好钢材所铸之剑,持久耐用,用60天应该是没什么问题的
【剑LV3,主题=50】一把像样的武器,但是你依然有更好的追求,45天后你肯定会看上更棒的武器
【剑LV1,主题=5】新人的随身武器,一般在使用20天后会损坏
达成成就【元素智者】在元素知道
超过5次解决提问者的问题
【元素晶石】
只有【元素大神】才能拥有,自由出入元素秘境之钥
【真爱之花】
情人节限量版,据说此花会带来桃花运!
守望者【猎空】
据说可以随时呼唤猎空,需要消耗100点贡献
【600小时】在线,足以驾驭魔神的力量,他们会守护你!
达成成就【中级追猎者】在元素悬赏中30次被评为最佳答案 ,双十二特别礼品
平时一次性兑换土豪金超过3000,圣诞节期间一次性兑换土豪金超过500即可获取!
【5W元素币】的持有者是真的土豪!赶快用它做点什么吧!
达成成就【有钱人】单次充值100RMB或以上
达成成就【闪电链】发布55张包含3条以上内部相关链接的帖子
元素推荐-友情提示 /1
每周一次的扩散大招带来更多元素币收益!
快速登录:
收藏帖子(7)  本文主要介绍Untiy5以后的GI,PBS,以及光源探头,反射探头的用法以及在着色器代码中如何发挥作用,GI是如何影响渲染的,主要分成三个部分,最开始说明PBS需要的材质与相应概念,二是Unity&里相应GI的操作,三是对应着色器代码的理解。如果没有特殊声明,所有操作与代码都是针对Unity5.3.
PBS材质与概念
  简单来说,PBS的优点不同的照明下获得一致的外观,更容易实现,更直观的参数。
PBS材质概念:
  1.albedo&反照率&
  反照率贴图定义漫反射的基本颜色,与原来的漫反射贴图相比,不包含定向光与AO,在这我们应该由环境自己的定向光与AO来影响,Unity里,我们用GI得到相应烘培或是实时的方向光与AO。
  2.Smoothness/Microsurface&表面细节&材料细节&光滑度
  光滑度:描述物体微表面的一个参数,可以用来定义法线分布函数,这样,粗糙的表面呈现宽,淡的镜面反射,光滑呈现集中和明亮的镜面反射。
  3.Metal/reflectivity&金属性/反射&
  金属与绝缘体应该使用不同的反射设置,导体的反射率&60-90%,绝缘体0-20%,高反射光分子不容易到达内部和散射,这样金属的高表现出来比较淡。
  如下全局设置:
  对于固定的材料,反射率趋于稳定,这样一般来说,一个模型的metal相对变化较少,如泥,水,木头这些他们的反射率相关并不大,只有金属与绝缘体会有相对比较大的反差,而模型表面的粗糙度应该用上面的图光滑性来表示,这个相对变化会比较大些,如下,水和泥土有相似的金属性(反射率),但是光滑度相差大,所以相差比较明显。
  上图来自pbr-theory:&
  Unity中事项,Metallic&贴图,金属性质占用R通道,光滑性占用A通道,GB被忽略。  
  物体的本来颜色用albedo表示,光滑性用Smoothness表示,材质特性用Metal表示,其中Unity&二种PBS标准着色,Standard其中金属性越高,本身镜面颜色占比越高,灯光的颜色占比越低,而高光可以设置自己的镜面反射。
  PS: pbr-practice& 
BRDF&光照模型&概念
  1&能量守恒:一个对象不能反射比他接收的光多。
  粗糙的材料有更多比较淡的亮点,而光滑的材料有更集中,更明亮的亮点,也可以这么理解,漫反射越多,镜面反射相对越少,镜面反射越多,漫反射越少。
  2&菲涅尔效应:边缘的反射更亮。
  更具体的可以看:
  3&微表面模型模型:
  普通的着色模型假定着色的区域是一个平滑的表面,表面有一个法线,而微表面则认为,着色区域是一个无数比入射光线覆盖范围更小的微小表面组成的粗糙区域,这个微小表面是光滑的镜面反射。表面细微细节对扩散的影响,表面越粗糙,反射光越发散或模糊。
GI结合BRDF渲染
  GI全局照明指的是全局照明会模拟光线在场景中的多次反射,所有的东西都是一个潜在的光源,任何可见的模型不是辐射光线,就是反射光线,其中GI(渲染间接光源,本身一般也用BRDF渲染)&配合BRDF生成带灯光直射,环境内模型互想影响的逼真场景。 &
  天空盒是GI的组成部分,反射天空盒可以改变场景所有模型接受反射量。
  GI结合PBS,如下,摄像机的镜头是一样的材质,在不同的环境下:
  更详细的可以点进这个链接:&&
  全局光照的特点在于能够捕捉间接光照,所以5以后,除开原来的direct&light的效果,增加indirect&light的效果,简单来说,就是除开光源之后,然后模型本身做为光源,幅射到别的模型上,层层递归后的效果。现不管是预计算实时GI还是烘培GI都只是针对静态模型。预计算Gi的实时光源与烘陪对应的烘培光源里的强度与反射强度都会影响幅射图与方向图的内容。需要注意,预计算Gi针对的是实时方向光,而烘培GI针对的是烘培方向光。
  Skybox&:天空盒,参考材质Skybox/Cubemap,如果全局光源与全局反射探头都选择Skybox模式,则会把Skybox当做一个Cubemap,场景所有模型,静态与非静态的一部分光源从这个Cubemap反射上得到。
  Sun:Skybox选择一个方向光,以这个方向光的方向做方向,这个方向光如颜色与强度不影响全局本身,如果没有设置方向光,选择强度最大的那个方向光源。&
  Ambient&Source:全局光源,如果设置天空盒,但是天空盒本身没有设置,自动选择下面的全局颜色设置,全局反射探头以Ambient&Source当做光源。
  Ambient&Intensity:全局光源强度,越高越亮,为0时,光源不起作用。
  Ambient&GI:当预计算GI与烘培GI二个都选择后,这个可以选择是用实时还是烘培。&
  Reflection&Source:Unity默认放入的全局反射探头,选择Skybox会以Ambient&Source里提供的光源颜色做反射,同时也可以自己提供cubemap当做反射源。
  Resolution:反射探头解析度,应该是对应RTT的cubemap六张纹理的分辨率。
  Compression:是否压缩。
  Reflection&Intensity:Reflection&Source针对所有模型反射强度,值越大,相应的模型面上显示越清晰的Reflection&Source。
  Reflection&Bounces:当设置多个Reflection&Probe时,互相反射对方信息的次数,如二面镜子。
  Precomputed&Realtime&GI
  预计算实时GI,针对实时静态物体之间的幅射光,故相应的幅射图与方向图都是低分辨率下的。动态物体可以使用光照探头来得到相应反射光源信息,注意动态模型与光探头的距离。
  Realtime&Resolution:预计算实时GI,把场景分成许多格,得到每个格的幅射信息。那么这个值越高,计算量将以平方增加,最终值还将和General&GI里的光源参数里的Resolution相乘。
  CPU&Usage:生成相应GI的数据时,在游戏运行时分配多少CPU计算能力。&
  Baked&GI
  烘培GI,因此能得到更精确的模型之间的反射光信息,但是不能运行时更改相应的光源信息,如颜色,方向,预计算实时GI没有这个问题。
  Baked&Resolution:一般来说,是Realtime&Resolution&10+,因为相应的幅射图与方向图精确度要高很多。
  Baked&Padding:网上说是光照贴图中分隔的距离,还需要验证。
  Compressed:是否压缩
  Ambient&Occlusion:值越高,遮挡地区得到的光比差越大。
  Final&Gather:用FG技术来产生烘培数据,这种技术时间会长一些。参考
  Ray&Count:Final&Gather所用的光线追踪光线数目。
  Atlas&Size:图集里贴图的大小,越低实际占用越精确也就是越小,但是贴图产生越多,应该选择一个合适的大小。&  
  General&GI
  预计算实时GI还是烘培如何生成.
  Direction&Mode:幅射图/带方向光/加镜面,具体看Shader分析,其中预计算实时GI与烘培GI在这生成的相应幅射图,方向图等有所不同,后面会提到。
  Indirect&Intensity:间接光的强度。
  Default&Parameters:生成相应贴图所需要的信息。
  其中全局预计算GI,烘培GI,全局反射探头相应的改动需要重新烘培,如果选择自动,相关改动会自动在后台烘培。
  上面这些说实话,写这么操作没啥用,自己对着每项实践一篇,什么都清楚了。&
Light&Probe:&
  对于GI来说,不管是预计算GI与烘培GI,都不会对非静态模型计算间接反射,光探头的加入,可以使非静态模型得到周围静态模型的幅射光,主要技术原理使用一种球谐光照的技术,注意light probe一般不会对静态模型有影响,你看到的影响,只是因为非静态模型的颜色变化大造成的反差。
  相关球谐光照的技术原理可以参见,本人也看不懂,只能说看了后有点印象是怎么回事:
  一种2D傅立叶级数的球形推广,可以把光照函数展开成SH基函数的叠加,类似傅立叶变换能把任何函数展开成正弦波的叠加,对光照图来说一般只用2&bands&=&4&RGB&textures,通过丢失高频细节来压缩存储。  
Reflection&Probe:
  定义一个Cubemap用来影响周围模型的镜面反射,给镜面高光模型使用。
  一般来说,我们想得到一个实时场景的Cubemap,只需要在一个点,用摄像机对着前后,左右,上下,各拍摄一次,形成6个面,组合成Cubemap.
  Type:烘培,用户,自动。其中,烘培就是用户来控制生成一个当前的场景cubemap,用户就是用户自己提供一个cubemap,实时就是不断更新这个cubemap以映射最新的场景。
  当选择实时,Refresh&mode:On&&awake启动时,每桢,用户脚本控制,当每桢时,如下选择。
  Time&slicing:一桢先生成6个面,后面8桢每桢生成一个mipmap,一共9桢。
  Individual&face:6面6桢,加后面8桢每个mipmap,一共14桢。
  No&time&slicing:一桢内把mipmap与cubemap全部生成。
  Importance:当多个反射探头影响一个模型时,这个参数影响这个反射探头的比重。
  Intensity:间接光强度,强度影响镜面反射,镜面反射越亮。
  Box&Projection:从着色代码来看,应该根据当前反射探头的源点与AABB影响是模型原来法线。
  Size:大小,范围内的模型使用这个。
  Probe&Origin:原点。
  Cubemap&capture&sttings:
  Resolution:cubempa材质的大小。
  HDR:高光。
  Shadow&Distance:阴影距离,数值越少,阴影越近。
  下面就是RTT对应摄像机的属性。
GI间接光源算法:辐射度算法
  辐射度算法就是:把场景细分到很细很细的面片(如1个像素那么大的三角形),分别计算它们接受和发出的光能,然后逐次递归,直到每个面片的光能数据不再变化(或者到一定的阀值)为止.因此,计算量很大(要计算很多次),而且难以并行(因为递归),参考
  Unity中GI选择non-direction模式生成的辐射度,使用一张图,储存每个位置收到的间接光照,其中假定都只是扩散,没有镜面反射。&
GI&Directional&LightMap算法:
  把半球面的入射辐射度用某种方法进行采样,保存起来在运行时根据法线图中的法线方向来进行一次合成,由于带方向信息,也可以支持高光计算了,可以参考:
  Unity中GI选择direction模型,会在上面辐射图添加一张图,用来存储接收到的光的方向选择specular,烘培GI与预计算GI使用不同的方式,烘培GI在上面二张图各扩大一倍,原来的保存直接光的影响,新增加的位置用来保存间接光的影响,其中预计算GI新增加一张贴图,三张图分别保存辐射光照,光源方向,法线,需要结合实时方向光源。
  探讨Unity5中全局光照(Enlighten)&
  Unity&5.0新功能教学:
  Unity&5&中的全局光照技术详解&
  Unity5&官网文档GI三种模式具体区别&
Unity&GI&BRDF&Shader
  主要参考对照如下Shader文件:
  UnityStandardCore.cginc:前向渲染base,顶点着色入口vertForwardBase,片断着色入口fragForwardBase,这个是着色器的主要Pass,全局方向光,GI信息合并都在这个pass中。
  UnityStandardBRDF.cginc:BRDF的具体实现.
  UnityGloballllumination.cginc:提取GI信息,包含烘陪GI与预计算GI。
  预计算Gi针对的是实时方向光,而烘培GI针对的是烘培方向光,所以当说预计算GI的light时,指的是实时方向光,而烘培GI的light说的是烘培方向光。
  相应主要代码我都已经加上注释,相信还是比较容易看懂的。
1 ---------UnityStandardCore
2 //顶点着色器入口
3 VertexOutputForwardBase vertForwardBase (VertexInput v)
VertexOutputForwardB
UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
//世界坐标下位置
float4 posWorld = mul(_Object2World, v.vertex);
#if UNITY_SPECCUBE_BOX_PROJECTION
o.posWorld = posWorld.
//屏幕空间位置
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.tex = TexCoords(v);
o.eyeVec = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
#ifdef _TANGENT_TO_WORLD
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
o.tangentToWorldAndParallax[0].xyz = tangentToWorld[0];
o.tangentToWorldAndParallax[1].xyz = tangentToWorld[1];
o.tangentToWorldAndParallax[2].xyz = tangentToWorld[2];
o.tangentToWorldAndParallax[0].xyz = 0;
o.tangentToWorldAndParallax[1].xyz = 0;
o.tangentToWorldAndParallax[2].xyz = normalW
//We need this for shadow receving
TRANSFER_SHADOW(o);
// Static lightmaps
#ifndef LIGHTMAP_OFF
//开启烘培GI后
o.ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.
o.ambientOrLightmapUV.zw = 0;
// Sample light probe for Dynamic objects only (no static or dynamic lightmaps)
//光源探头对动态模型的影响,rgb(颜色)根据SH系数还原光源探头与不重要的点光源上的颜色信息
#elif UNITY_SHOULD_SAMPLE_SH
#if UNITY_SAMPLE_FULL_SH_PER_PIXEL
o.ambientOrLightmapUV.rgb = 0;
#elif (SHADER_TARGET & 30)
o.ambientOrLightmapUV.rgb = ShadeSH9(half4(normalWorld, 1.0));
// Optimization: L2 per-vertex, L0..L1 per-pixel
o.ambientOrLightmapUV.rgb = ShadeSH3Order(half4(normalWorld, 1.0));
// Add approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.ambientOrLightmapUV.rgb += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, posWorld, normalWorld);
//开启预计算GI后
#ifdef DYNAMICLIGHTMAP_ON
o.ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.
#ifdef _PARALLAXMAP
TANGENT_SPACE_ROTATION;
half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));
o.tangentToWorldAndParallax[0].w = viewDirForParallax.x;
o.tangentToWorldAndParallax[1].w = viewDirForParallax.y;
o.tangentToWorldAndParallax[2].w = viewDirForParallax.z;
UNITY_TRANSFER_FOG(o,o.pos);
72 //片断着色器入口
73 half4 fragForwardBase (VertexOutputForwardBase i) : SV_Target
FRAGMENT_SETUP(s)
UnityLight mainLight = MainLight (s.normalWorld);
half atten = SHADOW_ATTENUATION(i);
half occlusion = Occlusion(i.tex.xy);
//提取GI里的信息到UnityGI中。
UnityGI gi = FragmentGI (
s.posWorld, occlusion, i.ambientOrLightmapUV, atten, s.oneMinusRoughness, s.normalWorld, s.eyeVec, mainLight);
// 如果是预计算GI或动态模型,gi.light表示主光源,焙烘取
half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.oneMinusRoughness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
// GI生成类型是spceular,才会计算
c.rgb += UNITY_BRDF_GI (s.diffColor, s.specColor, s.oneMinusReflectivity, s.oneMinusRoughness, s.normalWorld, -s.eyeVec, occlusion, gi);
c.rgb += Emission(i.tex.xy);
UNITY_APPLY_FOG(i.fogCoord, c.rgb);
return OutputForward (c, s.alpha);
93 //填充UnityGIInput,用来得到UnityGI信息。
94 inline UnityGI FragmentGI (
float3 posWorld,
half occlusion, half4 i_ambientOrLightmapUV, half atten, half oneMinusRoughness, half3 normalWorld, half3 eyeVec,
UnityLight light)
d.worldPos = posW
d.worldViewDir = -eyeV
//如果有GI信息。
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
d.ambient = 0;
d.lightmapUV = i_ambientOrLightmapUV;
//一般来说,非静态模型,得到环境光
d.ambient = i_ambientOrLightmapUV.
d.lightmapUV = 0;
//全局反射探头的AABB
d.boxMax[0] = unity_SpecCube0_BoxM
d.boxMin[0] = unity_SpecCube0_BoxM
d.probePosition[0] = unity_SpecCube0_ProbeP
d.probeHDR[0] = unity_SpecCube0_HDR;
//用户定义的反射探头AABB
d.boxMax[1] = unity_SpecCube1_BoxM
d.boxMin[1] = unity_SpecCube1_BoxM
d.probePosition[1] = unity_SpecCube1_ProbeP
d.probeHDR[1] = unity_SpecCube1_HDR;
return UnityGlobalIllumination (
d, occlusion, oneMinusRoughness, normalWorld);
130 --------UnityGloballllumination.cginc
131 //FragmentGI 跳转到这
132 inline UnityGI UnityGlobalIllumination (UnityGIInput data, half occlusion, half oneMinusRoughness, half3 normalWorld, bool reflections)
UnityGI o_
UNITY_INITIALIZE_OUTPUT(UnityGI, o_gi);
// Explicitly reset all members of UnityGI
ResetUnityGI(o_gi);
//动态模型使用 SH得到的漫反射信息。
#if UNITY_SHOULD_SAMPLE_SH
#if UNITY_SAMPLE_FULL_SH_PER_PIXEL
half3 sh = ShadeSH9(half4(normalWorld, 1.0));
#elif (SHADER_TARGET &= 30)
half3 sh = data.ambient + ShadeSH12Order(half4(normalWorld, 1.0));
half3 sh = data.
o_gi.indirect.diffuse +=
//如果没有烘培GI,需要当前全局方向光源的信息
#if !defined(LIGHTMAP_ON)
o_gi.light = data.
//atten阴影信息,值越小阴影越重
o_gi.light.color *= data.
// Baked lightmaps
fixed4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
half3 bakedColor = DecodeLightmap(bakedColorTex);
//没有方向贴图
#ifdef DIRLIGHTMAP_OFF
//设置漫反射
o_gi.indirect.diffuse = bakedC
#ifdef SHADOWS_SCREEN
o_gi.indirect.diffuse = MixLightmapWithRealtimeAttenuation (o_gi.indirect.diffuse, data.atten, bakedColorTex);
#endif // SHADOWS_SCREEN
//方向与漫反射
#elif DIRLIGHTMAP_COMBINED
fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
//更精准的漫反射
调整过后的half Lambert
o_gi.indirect.diffuse = DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);
#ifdef SHADOWS_SCREEN
o_gi.indirect.diffuse = MixLightmapWithRealtimeAttenuation (o_gi.indirect.diffuse, data.atten, bakedColorTex);
#endif // SHADOWS_SCREEN
//漫反射,方向,高光
#elif DIRLIGHTMAP_SEPARATE
// Left halves of both intensity and direction lightmap right halves - indirect.
// Direct 调整o_gi.light
fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
o_gi.indirect.diffuse += DecodeDirectionalSpecularLightmap (bakedColor, bakedDirTex, normalWorld, false, 0, o_gi.light);
// Indirect 漫反射,镜面都是保存在unity_Lightmap中,竖直中间分开
//调整o_gi.light2
half2 uvIndirect = data.lightmapUV.xy + half2(0.5, 0);
bakedColor = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, uvIndirect));
bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, uvIndirect);
o_gi.indirect.diffuse += DecodeDirectionalSpecularLightmap (bakedColor, bakedDirTex, normalWorld, false, 0, o_gi.light2);
//预计算GI
#ifdef DYNAMICLIGHTMAP_ON
// Dynamic lightmaps unity_DynamicLightmap unity_DynamicDirectionality unity_DynamicNormal
fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
//间接漫反射
half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);
#ifdef DIRLIGHTMAP_OFF
o_gi.indirect.diffuse += realtimeC
#elif DIRLIGHTMAP_COMBINED
half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
//调整漫反射
o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);
#elif DIRLIGHTMAP_SEPARATE
half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
half4 realtimeNormalTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicNormal, unity_DynamicLightmap, data.lightmapUV.zw);
//调整o_gi.light3
o_gi.indirect.diffuse += DecodeDirectionalSpecularLightmap (realtimeColor, realtimeDirTex, normalWorld, true, realtimeNormalTex, o_gi.light3);
//gi里的信息全放入indirect的diffuse中
o_gi.indirect.diffuse *=
//有反射探头,设置镜面光源信息。
if (reflections)
half3 worldNormal = reflect(-data.worldViewDir, normalWorld);
#if UNITY_SPECCUBE_BOX_PROJECTION
half3 worldNormal0 = BoxProjectedCubemapDirection (worldNormal, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
half3 worldNormal0 = worldN
half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], worldNormal0, 1-oneMinusRoughness);
#if UNITY_SPECCUBE_BLENDING
const float kBlendFactor = 0.99999;
float blendLerp = data.boxMin[0].w;
UNITY_BRANCH
if (blendLerp & kBlendFactor)
#if UNITY_SPECCUBE_BOX_PROJECTION
half3 worldNormal1 = BoxProjectedCubemapDirection (worldNormal, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);
half3 worldNormal1 = worldN
half3 env1 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube1), data.probeHDR[1], worldNormal1, 1-oneMinusRoughness);
o_gi.indirect.specular = lerp(env1, env0, blendLerp);
o_gi.indirect.specular = env0;
o_gi.indirect.specular = env0;
//反射探头的信息存入到gi的indirect镜面中
o_gi.indirect.specular *=
262 --------UnityStandardBRDF.cginc
263 // Main Physically Based BRDF
264 // Derived from Disney work and based on Torrance-Sparrow micro-facet model
BRDF = kD / pi + kS * (D * V * F) / 4
I = BRDF * NdotL
269 // * NDF (depending on UNITY_BRDF_GGX):
a) Normalized BlinnPhong
272 // * Smith for Visiblity term
273 // * Schlick approximation for Fresnel
274 half4 BRDF1_Unity_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half oneMinusRoughness,
half3 normal, half3 viewDir,
UnityLight light, UnityIndirect gi)
half roughness = 1-oneMinusR
//nh,能射入眼睛的光线的角度 也叫半线
half3 halfDir = Unity_SafeNormalize (light.dir + viewDir);
half nl = light.
//nh,半线与法线夹角,夹角越少,射入眼睛的光越大
half nh = BlinnTerm (normal, halfDir);
//射线与法线夹角
half nv = DotClamped (normal, viewDir);
//光线与法线夹角
half lv = DotClamped (light.dir, viewDir);
//半线与光线夹角
half lh = DotClamped (light.dir, halfDir);
291 #if UNITY_BRDF_GGX
//遮挡函数
half V = SmithGGXVisibilityTerm (nl, nv, roughness);
//法线分布函数 1/Pi
half D = GGXTerm (nh, roughness);
//遮挡函数
half V = SmithBeckmannVisibilityTerm (nl, nv, roughness);
//法线分布函数 1/Pi
half D = NDFBlinnPhongNormalizedTerm (nh, RoughnessToSpecPower (roughness));
301 #endif
half nlPow5 = Pow5 (1-nl);
half nvPow5 = Pow5 (1-nv);
half Fd90 = 0.5 + 2 * lh * lh *
//disney Diffuse 菲涅尔 边角有更亮的光
half disneyDiffuse = (1 + (Fd90-1) * nlPow5) * (1 + (Fd90-1) * nvPow5);
// HACK: theoretically we should divide by Pi diffuseTerm and not multiply specularTerm!
// BUT 1) that will make shader look significantly darker than Legacy ones
// and 2) on engine side "Non-important" lights have to be divided by Pi to in cases when they are injected into ambient SH
// NOTE: multiplication by Pi is part of single constant together with 1/4 now
//镜面系数
half specularTerm = max(0, (V * D * nl) * unity_LightGammaCorrectionConsts_PIDiv4);// Torrance-Sparrow model, Fresnel is applied later (for optimization reasons)
half diffuseTerm = disneyDiffuse *
//Gi镜面,可以看到oneMinusReflectivity越高,grazingTerm越低,specColor越高(本身镜面颜色)
half grazingTerm = saturate(oneMinusRoughness + (1-oneMinusReflectivity));
//GI non-direction与direction的BRDF如下情况
//light.color
实时方向光直接信息
实时方向光直接信息
//gi.diffuse
直接与间接光源信息
间接光源信息
SH(光源探头)间接光源信息
//gi.specular
half3 color =
diffColor * (gi.diffuse + light.color * diffuseTerm)
+ specularTerm * light.color * FresnelTerm (specColor, lh)
+ gi.specular * FresnelLerp (specColor, grazingTerm, nv);
return half4(color, 1);
  整个代码并不多,但是对于前面所说所有东东,在这都有一个完整的解释,这份代码来告诉我们,那些金属性,光滑度,GI中幅射图,方向图,还有光源探头,反射探头所起的作用。
BRDF&光照模型:
  如下是现有光照模型没有考虑的问题:
  光照现象,漫反射并不是各个方面平均发散. & &----微表面模型(NDF).&&
  菲涅尔定理(Fresnel) & & & & & & & & & & & & & & &&&----光源在边角处有更明亮的反光。
  能量守恒,反射的光不能超过入射的光. & & & & &----遮挡因素,越光滑镜面越集中越亮&
  普通的着色模型假定着色的区域是一个平滑的表面,表面有一个法线,而微表面则认为,着色区域是一个无数比入射光线覆盖范围更小的微小表面组成的粗糙区域,这个微小表面是光滑的镜面反射,因为着色区域并不能一个法向量来表示表面的方向,转面代替用一概率分布函数(NDF)来表示。一般来说,分别用如下字母表示:
  D&用来表示法线分布。
  F&用来菲涅尔影响,光源在边角处有更明亮的反光。
  G/V&用来表示凹凸表面间的遮挡因素(Unity用V来表示)&
  如下是Unity相对应BRDF的处理
  先要说明每个引擎对BRDF处理各不同,在这只介绍Untiy的实现:
  D&采用GGX与BlinnPhong二种法线分布函数,BlinnPhong比较简单,效率高。
  F&采用简化的Disney&Fresnel方式求得菲涅尔影响。
  G/V&采用GGX与Beckmann二种技术,可以看到,光滑度是个关键参数。
  针对Unity5.3简化过的Disney&Fresnel,简单分析下.
  Nl:法线与灯光的夹角,夹角越大,这个值越小。
  Nv:法线与视线的夹角,夹角越大,这个值越小。
  Lh:灯光与视线的半线与法线的夹角,其中灯光与视线的半线就是灯光与视线的平均线,简单来说,这个线与法线重合,这条由灯光发出来的射线才能进入我们的眼镜。
  假定&fd90不变,nl与nl的角度越大,那么nlPow5与nvPow5的值越大,最终结果越大,这也是菲涅尔想表达的,光源在边角处有更明亮的反光。
  在Unity中,可以看到,D与V影响镜面反射,F影响漫反射,特别说明,只有Unity是这样处理。
  其中,可以看到反射率(也就是金属性)影响的是GI的镜面反射,也就是反射探头。
  其余的部分挑的说明下:
  顶点着色器中,填充VertexOutputForwardBase&信息,其中ambientOrLightmapUV(half4)&如果包含烘培GI,xy填充相应烘培GI的UV坐标,如果包含预计算GI,zw填充为预计算GI的UV坐标。如果是非静态模型,不包含GI信息,相应light&probe提供的光源信息放入rgb中。&
  片断着色器中,每个像素要得到对应像素上的Unity&GI信息,相应的,Unity&GI中的属性light并不是表示光源,而是当前像素受如主光源,幅射,镜面对当前像素的影响,每个像素对应的light都有差别,千万不要看到写的是个light,就把它当做光照,这样所有理解都不对了。
  其中如果只有烘培GI,当前像素的Unity&GI中参数light不提供信息,indirect里的漫反射包含烘培光源的光照。而预计算GI中当前像素的light本身就是全局光源,indirect只包含物体之间的漫反射信息,而非静态模型中当前像素只有实时光源等直接光照信息,其周围的静态模型的反射光只有通过光探头得到SH信息。
  FragmentGI里常见结构:
  1 UnityLight:
  包含当前像素中光源颜色,方向,法线与光源方向点积
  2 UnityIndirect:
  包含当前像素中diffuse漫反射,specular镜面信息
  3 UnityGI:特别注意,里面的light是UnityLight类型,并不表示光源,而是用来表示当前像素受光源影响的量。
  Gi.light&如果烘陪GI信息,则使用当前主光源填充UnityLight,如果有烘陪GI信息,则填充为空。
  Gi.light2&当烘培GI启用高光后,才会调用。
  Gi.light3&预计算GI启用高光后,才会调用。
  UnityGI在根据函数UnityGlobalIllumination被填充,我们可以分析得到动态模型,使用SH得到漫反射信息。如果没有烘培GI,我们需要实时全局光源,故gi.light是像素所受全局方向光。烘培GI,GI光照信息保存在gl.light2中,gi漫反射直接取光照图里的diffuse.预计算GI,GI光照信息保存在gl.light3中,gi漫反射添加光照图里的diffuse.而反射探头用来添加反射的信息到GI里的镜面信息中了,其中Occlusion&控制gi的diffuse与specular系数。(n*=occlusion)&   
  其中UNITY_SHOULD_SAMPLE_SH&当前渲染的动态模型,使用SH得到间接的漫反射信息,其中UNITY_SHOULD_SAMPLE_SH如下定义。#define&UNITY_SHOULD_SAMPLE_SH&(&defined&(LIGHTMAP_OFF)&&&&defined(DYNAMICLIGHTMAP_OFF)&)&
  预计算GI与烘培GI
  烘培GI如上代码中分析得到,选择non-direction,只有一张图,保存直接与间接光照所有信息,选择direction后,会保存方向,选择specular后,上面二张图,长度扩大一倍,保存镜面有关信息。
  其中预计算GI,选择non-direction,只有一张图,不保存直接光照,只保存间接光照,选择direction,保存方向,选择specular后,不同于烘培GI,会新增一张纹理保存镜面相关信息(从代码来上看,可能是组织过的法线)&
  反射探头:
  unity_SpecCube0_ 全局反射探头&unity_SpecCube1_当前模型受影响的反射探头。
  FragmnetGI:得到全局反射探头与用户定义的反射探头位置,AABB,IsHDR.
  UnityGlobalIllumination:片断像素中得到反射探头影响镜面值,可以看到,SpecCube0与SpecCube1通过深度影响gi间接光源上的镜面颜色。
  BoxProjectedCubemapDirection:影响worldNormal(反视线进过法线后的反射)
  Unity_GlossyEnvironment:可以看到&,光滑值影响LOD值,越光滑越清晰。 UNITY_SAMPLE_TEXCUBE&从cubemap取值。&
  光源探头:
  vertForwardBase:VertexOutputForwardBase的ambientOrLightmapUV设置颜色,从这可以看到,光源探头实际不影响静态的物体,你如果看到有影响,只是因为周围的非静态模型颜色反差造成给你的影响。
  UnityGlobalIllumination:灯光探头的值赋到gi间接光源上的漫反射上。
  :今天看UE4中的环境反射文档,我去,Unity里的做法完全是参照UE4的,通过上面的着色器代码,我们可以完全理解下面这个链接里所说的。
阅读(...) 评论()}

我要回帖

更多关于 unity3d pbr 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信