我们的目标:UnityStandard

我一直作Unity开发,所以环境也就选择了Unity。目标也就是尽可能接近UnityStandard的效果,不过主要为了学习PBR,所以就只实现基本的PBR和法线。也就是使用Albedo,Matellic,Normal三个贴图。遮蔽,自发光,反射和ImageBasedLighting这种就先不管了

PBR原理

PBR的原理务必要看Trace的这篇【PBR】基于物理渲染的基础理论1.0版

简单的解释
1. 分开处理反射面的绝缘体特性和金属特性,最后光照应该是Diffuse+Specular
2. 纯金属没有Diffuse,非金属主要是Diffuse,有一点反射
3. 反光部分的主要有三个东西影响:微表面的法线分布(NDF),微表面的入射和反射遮挡(Geometry Function),反射率和入射角的关系(Fresnel反射)

猜测Unity的方案

我没去找Standard的源码,照着WORKING WITH PHYSICALLY-BASED SHADING: A PRACTICAL APPROACH猜测一下他的方案。

Unity Metallic方式

1. Albedo:基础色,和以前的Diffuse图不一样,不能画出光的效
2. Metallic:Metallic图的r通道,描述金属性质。应该是描述反射率的参数,用这个参数将Diffuse部分和Specular部分分开
3. Smoothness:光滑度,Metallic图的a通道。用来描述光滑程度(废话),或者说,描述微表面理论中微表面法线和整体法线的一致程度(NDF),以及微表面遮挡的程度(Geometry Function)
4. Normal:法线,没啥说的

所以看起来差不多应该是这样的意思:
Diffuse*(1-Metallic)+f(l,v)*Metallic

f(l,v)就是PBR的核心内容,BRDF公式:

//       D(h) F(v,h) G(l,v,h)
//f(l,v) = ---------------------------
//       4(n·l)(n·v)

BRDF公式的选择与说明

公式这东西谁看谁晕,所以先强推这个文章
如何看懂这些"该死的"图形学公式
我能继续搞下去全靠这个了。

到这里问题就很简单了,选择合适的公式就对了。翻找了一下SIGGRAPH的文章,全是干货真是好地方!照着人家的干货做了如下选择:

1. D(h):GGX

//	  alpha^2
//D(m) = -----------------------------------
//   pi*((n·m)^2 *(alpha^2-1)+1)^2

Q:alpha是什么
A:alpha = roughness * roughness,roughness是粗糙度,roughness= 1-smoothness

Q:m是什么?h是什么?
A:m是微表面的法线, h是入射光和反射光的半角向量,根据微表面原理,只有法线和半角向量一致的微表面参与反射,所以m和h是相等的

2. G(l,v,h):Smith-Schlick,在Smith近似下G(l,v,h) = g(l)*g(v),讲道理入射和反射的遮蔽应该是相同的。

//	  n·v
//g(v) = -----------------
//     (n·v) *(1-k) +k

  

Q:k是什么?
A:k是Schlick公式的参数,具体的要去看Schlick的论文

Q:k应该是多少?
A:我看了几个地方,k的选择都不太一样,这里我们本着找NB的抄的态度,用了UE4的 k =(a^2 +1) * (a^2 +1)/8;

3. F(v,h):UE4对Schlick的一个近似。

//Schlick
//F(v,h) = F0 +(1-F0)*(1-(v·h))^5
//
//UE4 approximation
//
//F(v,h) = F0+(1-F0)2^((-5.55473(v·h)-6.98316)*v·h)

  

Q:F0是什么?
A:F0,入射角0时的反射率。这个数值我用了Metallic的值,应该还有跟roughness相关的计算,谁清除求告知

然后合成一个!

//       alpha^2 * (F0+(1-F0)*pow(2,(-5.55473(v·h)-6.98316)*v·h))
//f(l,v) = ---------------------------------------------------------------------------
//       4*pi*((n·h)^2 *(alpha^2-1)+1)^2*((n·v) *(1-k) +k)*((n·l) *(1-k) +k)

  

写Shader

这就没啥说的了,就是把公式写上去。
效果图:右边是Standard,左边是Custom,有一点点不同,反射更强烈一点

Shader "Stein/CustomPBR"
{
Properties
{
_Matel("Matel",2D) = "white"{}
_Albedo("Albedo", 2D) = "white" {}
_Normal ("Normal", 2D) = "bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD Pass
{
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
} CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#pragma multi_compile_fwdbase_fullshadows #include "UnityCG.cginc"
#include "AutoLight.cginc"
#define PI 3.14159265359 struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent : TANGENT;
}; struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float3 tangentDir : TEXCOORD3;
float3 bitangentDir : TEXCOORD4;
LIGHTING_COORDS(,)
UNITY_FOG_COORDS()
}; uniform float4 _LightColor0; sampler2D _Albedo;
float4 _Albedo_ST;
sampler2D _Matel;
float4 _Matel_ST;
uniform sampler2D _Normal;
uniform float4 _Normal_ST; VertexOutput vert (appdata v)
{
VertexOutput o = (VertexOutput); o.pos = mul(UNITY_MATRIX_MVP, v.vertex );
o.uv0 = v.uv;
o.posWorld = mul(_Object2World, v.vertex); //世界坐标下的几个向量值,参考ShaderForge
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.tangentDir = normalize( mul( _Object2World, float4( v.tangent.xyz, 0.0 ) ).xyz );
o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); UNITY_TRANSFER_FOG(o,o.pos);
TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
} fixed4 frag (VertexOutput i) : SV_Target
{
i.normalDir = normalize(i.normalDir); float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz); //法线左边转换
float3x3 tangentTransform = float3x3( i.tangentDir, i.bitangentDir, i.normalDir);//法线的TBN旋转矩阵
float4 _Normal_var = tex2D(_Normal,TRANSFORM_TEX(i.uv0, _Normal));
float3 normalLocal =_Normal_var.rgb*-;//之前的问题是没有Unpack,整个坐标是偏了的,参考UnityCG.cginc
float3 normalDirection = normalize(mul( normalLocal, tangentTransform )); // 最终的法线 //从matellic图上取数据
fixed4 matelTex = tex2D(_Matel,TRANSFORM_TEX(i.uv0,_Matel));
float matellic = matelTex.r;//unity matellic 值,是一个grayscale value ,存在 r 通道
float roughness = -matelTex.a;//unity 用的是smoothness,在matellic map的alpha 通道,这里转换一下
float f0 = matelTex.r;//HACK 这个就是先这样用…… //预先计算一些常量
float3 h =normalize( lightDirection+viewDirection);//h,l和v的半角向量
float a = roughness*roughness;//alpha
float a2 = a*a;//alpha^2 float NoL =saturate( dot(normalDirection,lightDirection));
float NoV =saturate(dot(normalDirection,viewDirection));
float NoH =saturate(dot(normalDirection,h));
float VoH =saturate(dot(viewDirection,h)); //light & light color
float3 attenColor = LIGHT_ATTENUATION(i) * _LightColor0.xyz; // sample the _Albedo texture
fixed4 albedo = tex2D(_Albedo, i.uv0); //diffuse part
float3 directDiffuse =dot( normalDirection, lightDirection ) * attenColor;
float3 indirectDiffuse = float3(,,);
indirectDiffuse += UNITY_LIGHTMODEL_AMBIENT.rgb; // Ambient Light
float3 diffuse = (directDiffuse + indirectDiffuse) * albedo*(-matellic); //specular part
//微表面BRDF公式
// D(h) F(v,h) G(l,v,h)
//f(l,v) = ---------------------------
// 4(n·l)(n·v) //这个是GGX
// alpha^2
//D(m) = -----------------------------------
// pi*((n·m)^2 *(alpha^2-1)+1)^2 //简化 D(h)*PI/4
float sqrtD = rcp(NoH*NoH*(a2-)+);
// float D = a2*sqrtD*sqrtD/rcp(PI*4);
float D = a2*sqrtD*sqrtD/;//在 direct specular时,BRDF好像要乘PI,这里就直接约去。Naty Hoffman的那个文没太看懂 //in smith model G(l,v,h) = g(l)*g(v),这个公式是Schlick的趋近公式,参数各有不同
// n·v
//G(v) = -----------------
// (n·v) *(1-k) +k // float k = a2*sqrt(2/PI); //Schlick-Beckmann
// float k = a2/2; //Schlick-GGX
float k =(a2+)*(a2+)/; //UE4,咱们就挑NB的抄 //简化G(l,v,h)/(n·l)(n·v)
float GV=(NoV *(-k) +k);
float GL =(NoL *(-k) +k); //F(v,h)
float f = f0 +(-f0)*pow(,(-5.55473*VoH-6.98316)*VoH);//参数是从UE4那里抄来的,应该是Schlick公式的趋近 fixed3 specularTerm = D*f *rcp(GV*GL); fixed3 specular = albedo*attenColor*(/PI+ specularTerm)*NoL*matellic;//albedo/PI是BRDF公式的diffuse部分,没有就会偏黑
fixed4 finalcolor = (fixed4);
finalcolor.rgb =diffuse +specular;
finalcolor.a = albedo.a; // apply fog
UNITY_APPLY_FOG(i.fogCoord, finalcolor);
return finalcolor;
}
ENDCG
} Pass
{
Name "FORWARD_DELTA"
Tags {
"LightMode"="ForwardAdd"
}
Blend One One CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#pragma multi_compile_fwdbase_fullshadows #include "UnityCG.cginc"
#include "AutoLight.cginc"
#define PI 3.14159265359 struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent : TANGENT;
}; struct VertexOutput
{
float4 pos : SV_POSITION;
float2 uv0 : TEXCOORD0;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float3 tangentDir : TEXCOORD3;
float3 bitangentDir : TEXCOORD4;
LIGHTING_COORDS(,)
UNITY_FOG_COORDS()
}; uniform float4 _LightColor0; sampler2D _Albedo;
float4 _Albedo_ST;
sampler2D _Matel;
float4 _Matel_ST;
uniform sampler2D _Normal;
uniform float4 _Normal_ST; VertexOutput vert (appdata v)
{
VertexOutput o = (VertexOutput); o.pos = mul(UNITY_MATRIX_MVP, v.vertex );
o.uv0 = v.uv;
o.posWorld = mul(_Object2World, v.vertex); //世界坐标下的几个向量值,参考ShaderForge
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.tangentDir = normalize( mul( _Object2World, float4( v.tangent.xyz, 0.0 ) ).xyz );
o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); UNITY_TRANSFER_FOG(o,o.pos);
TRANSFER_VERTEX_TO_FRAGMENT(o)
return o;
} fixed4 frag (VertexOutput i) : SV_Target
{
i.normalDir = normalize(i.normalDir); //light dir & light color
float3 lightDirection =(float3);
float3 attenColor = (float3); if(_WorldSpaceLightPos0.w==)
{
lightDirection = normalize(_WorldSpaceLightPos0.xyz);
attenColor = LIGHT_ATTENUATION(i) * _LightColor0.xyz;
}
else
{
lightDirection =_WorldSpaceLightPos0.xyz- i.posWorld;
attenColor =_LightColor0.xyz /(+length(lightDirection));
lightDirection = normalize(lightDirection);
} // float3 attenColor = LIGHT_ATTENUATION(i) * _LightColor0.xyz; float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz); //法线左边转换
float3x3 tangentTransform = float3x3( i.tangentDir, i.bitangentDir, i.normalDir);//法线的TBN旋转矩阵
float4 _Normal_var = tex2D(_Normal,TRANSFORM_TEX(i.uv0, _Normal));
float3 normalLocal =_Normal_var.rgb*-;//之前的问题是没有Unpack,整个坐标是偏了的,参考UnityCG.cginc
float3 normalDirection = normalize(mul( normalLocal, tangentTransform )); // 最终的法线 //从matellic图上取数据
fixed4 matelTex = tex2D(_Matel,TRANSFORM_TEX(i.uv0,_Matel));
float matellic = matelTex.r;//unity matellic 值,是一个grayscale value ,存在 r 通道
float roughness = -matelTex.a;//unity 用的是smoothness,在matellic map的alpha 通道,这里转换一下
float f0 = matelTex.r;//HACK 这个就是先这样用…… //预先计算一些常量
float3 h =normalize( lightDirection+viewDirection);//h,l和v的半角向量
float a = roughness*roughness;//alpha
float a2 = a*a;//alpha^2 float NoL =saturate( dot(normalDirection,lightDirection));
float NoV =saturate(dot(normalDirection,viewDirection));
float NoH =saturate(dot(normalDirection,h));
float VoH =saturate(dot(viewDirection,h)); // sample the _Albedo texture
fixed4 albedo = tex2D(_Albedo, i.uv0); //diffuse part
float3 directDiffuse =dot( normalDirection, lightDirection ) * attenColor;
float3 indirectDiffuse = float3(,,);
indirectDiffuse += UNITY_LIGHTMODEL_AMBIENT.rgb; // Ambient Light
float3 diffuse = (directDiffuse + indirectDiffuse) * albedo*(-matellic); //specular part
//微表面BRDF公式
// D(h) F(v,h) G(l,v,h)
//f(l,v) = ---------------------------
// 4(n·l)(n·v) //这个是GGX
// alpha^2
//D(m) = -----------------------------------
// pi*((n·m)^2 *(alpha^2-1)+1)^2 //简化 D(h)*PI/4
float sqrtD = rcp(NoH*NoH*(a2-)+);
// float D = a2*sqrtD*sqrtD/rcp(PI*4);
float D = a2*sqrtD*sqrtD/;//在 direct specular时,BRDF好像要乘PI,这里就直接约去。Naty Hoffman的那个文没太看懂 //in smith model G(l,v,h) = g(l)*g(v),这个公式是Schlick的趋近公式,参数各有不同
// n·v
//G(v) = -----------------
// (n·v) *(1-k) +k // float k = a2*sqrt(2/PI); //Schlick-Beckmann
// float k = a2/2; //Schlick-GGX
float k =(a2+)*(a2+)/; //UE4,咱们就挑NB的抄 //简化G(l,v,h)/(n·l)(n·v)
float GV=(NoV *(-k) +k);
float GL =(NoL *(-k) +k); //F(v,h)
float f = f0 +(-f0)*pow(,(-5.55473*VoH-6.98316)*VoH);//参数是从UE4那里抄来的,应该是Schlick公式的趋近 fixed3 specularTerm = D*f *rcp(GV*GL); fixed3 specular = albedo* attenColor*(/PI+ specularTerm)*NoL*matellic;//albedo/PI是BRDF公式的diffuse部分,没有就会偏黑
fixed4 finalcolor = (fixed4);
finalcolor.rgb =diffuse +specular;
finalcolor.a = ; // apply fog
UNITY_APPLY_FOG(i.fogCoord, finalcolor);
return finalcolor;
}
ENDCG
}
}
}

PBR实现的更多相关文章

  1. OpenBSD内核之引导PBR

    OpenBSD引导的第二部PBR,也是活动分区的一个扇区的代码,由第一步的MBR加载到0x7C00处,manpage里详细的讲解了过程和大致实现 biosboot(8) (http://man.ope ...

  2. PBR实现2.0

    之前的错误和欠缺 1. 过于简单的划分diffuse和specular,非常光滑的非金属材料也是很能反光的2. 费奈尔效应的处理,F0的选取也比较随意3. 没有GI,更不支持AO 正确划分diffus ...

  3. UnrealEngine4 PBR Shading Model 概述

      虽然是概述,但内容并还是有些多,写上一篇PBR概念概述后,也在考虑怎么继续下去,最后还是觉得先多写一些东西再慢慢总结,所以还是尽量把这些年PBR相关的Paper精粹沉淀下来吧.     因为UE4 ...

  4. H3C交换配置PBR最佳实践

    简要说明 PBR算是比较常用的功能,需要我们去掌握一下 配置步骤简要 配置BFD 配置track 配置acl 配置policy-based-route 在接口上面应用policy-based-rout ...

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

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

  6. 神州数码策略路由(PBR)配置

    实验要求:掌握PBR配置的方法 拓扑如下 R1 enable 进入特权模式 config 进入全局模式 hostname R1 修改名称 interface s0/1 进入端口 ip address ...

  7. Win7 VS2017编译PBR渲染引擎google filament

    按照官方说明 https://github.com/google/filament 前置工具包 Windows 10 SDKVisual Studio 2017Clang 6Python 3.7Git ...

  8. 【3D美术教程】手雷(传统与PBR流程)

    转自:https://www.sohu.com/a/156489635_718614 随着最新的次时代技术PBR流程的普及,越来越多的公司由传统流程转向了PBR流程,主要原因在于PBR材质不仅效果上更 ...

  9. Python 打包中 setpy.py settuptools pbr 的了解

    背景 nova服务构建失败,报错: 'tests_require' must be a string or list of strings containing valid project/versi ...

随机推荐

  1. Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path 解决办法

    返回数据解析错误 com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT ...

  2. winform 使用 ReportViewer做报表

    之前用过的水晶报表觉得有些麻烦,因此尝试了使用微软自带的报表. 第一种方法是 在winform界面上放置ReportViewer界面,相关的代码如下: public DataTable dt; pri ...

  3. 《剑指offer》面试题12:打印1到最大的n位数

    面试题12:打印1到最大的n位数 剑指offer题目12,题目如下 输入数字n,按顺序打印出1到最大的n位十进制数,比如输入3,则打印出1,2,3一直到最大的三位数999 方法一 和面试题11< ...

  4. JS中this的值到底为何?

    之前很久的时间,因为研究不深,对于this的值一直模模糊糊,不是很清楚,最近有空做了一些研究,终于彻底弄明白了this到底为何物. 首先, 先抛出一个定论:”在Javascript中,this关键字永 ...

  5. Mac安装GitLab CE记录

    0 REF REF1 原始的GitLab Documentation REF2 Installation-guide-for-GitLab-on-OS-X REF3 如何在Mac 终端升级ruby版本 ...

  6. Android使用AudioTrack发送红外信号

    最近要做一个项目,利用手机的耳机口输出红外信号,从而把手机变成红外遥控器,信号处理的知识基本都还给老师了,刚开始真的挺头疼.找了不少资料研究了一下,总算有点心得,在这里做个备忘. 一.音频信号输出原理 ...

  7. *** missing separator. Stop.

    在make命令后出现这种错误提示,是提示第2行没有分隔符. 例如: 1 target:prerequisites 2 command -- 改为: 1 target:prerequisites 2   ...

  8. 【Django】Django 定时任务实现(django-crontab+command)

    一.编写自定义django-admin命令 注:利用django-admin自定义命令我们可以ORM框架对model进行操作,如:定时更新数据库,检测数据库状态..... Django为项目中每一个应 ...

  9. 安装mysql因为/tmp权限不足而导致ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)的解决方案

    本机是centos 6.5  安装的mysql是5.1的版本. 在安装mysql之后,第一次启动mysql服务的时候,需要/tmp有777(rwxrwxrwx)的权限,然而楼主的/tmp是755(rw ...

  10. 关于vue.js中事件处理器的练习

    html: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8 ...