PBR

简介

PBR全称Physically Based Rendering

即基于物理的渲染

它包含很多技术,折射、反射、GI、PBS等

其中的重点就是PBS

 

PBS

PBS全称Physically Based Shadering

即基于物理的着色

这是PBR的核心

而PBS的核心有

1.物质的光学特性

主要根据反射率去分为金属和非金属

金属

1.具有很高的反射率(>=0.5)

2.会立即吸收折射光,因此金属不会出现任何次表面散射或是透明效果

3.所有颜色都来自反射

非金属

1.具有很低的反射率(<=0.06)

2.会产生高光反射和漫反射

3.高光反射为单色/灰色

2.微平面理论

3.能量守恒

因此,镜面反射+漫反射<=入射光

4.菲涅尔反射

菲涅尔效应是一种表示光线的反射率与视角相关的现象

即视角观察方向于平面法线的对应关系

夹角越大,反射率就越大,亮度也就越亮

夹角越小,反射率就越小,亮度也就越低

5.线性空间光照

与之相对有个伽马空间

对于线性的颜色变化

人眼看到的

显示器输出的

可以看出他们是一样的

因此sRGB图就是在此基础上的矫正,但这会让着色器在计算时出现误差

因此需要使用Linear空间

双向反射分布函数BRDF

渲染方程

描述了光能在场景中的流动,根据光的物理学原理,渲染方程可以完美模拟出符合物理光学的结果

那个积分就是来自其他片段的漫反射

迪士尼原则的BRDF

1.使用直观的参数,而不是晦涩的物理参数

2.参数尽可能的少

3.参数在其合理范围内应该为0-1

4.允许参数在有意义的情况下超出正常范围

5.所有参数组应该尽可能的独立与合理

BaseColor固有色:表面的颜色,通常由纹理提供

SubSurface次表面:使用次表面近似的控制漫反射形状

Metallic金属度:0=非金属,1=金属,这是两种不同模型之间的线性混合

Specular镜面反射强度:字面意思

SpecularTint镜面反射颜色:对美术控制的让步,用于对BaseColor的入射镜面反射进行颜色控制

Roughness粗糙度:表面粗糙度,控制漫反射和镜面反射

Anisotropic各向异性强度:0=各项同性,1=各向异性,用于控制镜面反射高光的纵横比

Sheen光泽度:一种额外的掠射分量,主要用于布料

SheenTint光泽颜色:对Sheen的颜色控制

ClearCoat清漆强度:特殊用途的第二个镜面波瓣

ClearCoatGloss清漆光泽度:控制透明涂层光泽度

 

Shader&Cginc实现

1.准备材料

在Unity商店下载了个模型

带基础色、金属度、AO、法线、粗糙度贴图

2.创建标准表面shader,并解码,我们修改解码后的shader

1685行代码,没关系,我们进行优化

3.shader

1.属性部分

Properties
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Metallic ("Metallic", Range(0,1)) = 0.0
_MetallicTex ("MaterialTex", 2D) = "white" {}
_Smoothness ("Smoothness", Range(0,1)) = 0.5
_AO ("AO", Range(0,1)) = 1
_AOTex ("AOTex", 2D) = "white" {}
_NormalTex ("NormalTex", 2D) = "bump" {}

2.Pass部分

FORWARD
 Name "FORWARD"
Tags { "LightMode"="ForwardBase" }

CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_fog
#pragma multi_compile_fwdbase

#include "UnityCG.cginc"
#include "AutoLight.cginc"
    
#include "Lighting.cginc"//GI部分
#include "UnityPBSLighting.cginc"//PBS部分
//要么走下面这个,要么走上面两个
//#include "./MyPBS.cginc"//这是走自己的cginc

struct a2v
{
    float4 vertex:POSITION;
    float3 normal:NORMAL;
    float3 tangent:TANGENT;
    float2 uv:TEXCOORD0;
    float4 texcoord1:TEXCOORD1;
};

struct v2f
{
    float4 pos:SV_POSITION;
    float2 uv:TEXCOORD0;
    float3 worldNormal:TEXCOORD1;
    float3 worldPos:TEXCOORD2;
    #if UNITY_SHOULD_SAMPLE_SH
        half3 sh:TEXCOORD3;
    #endif
    UNITY_FOG_COORDS(4)
    UNITY_SHADOW_COORDS(5)
    float3 tSpace1:TEXCOORD6;
    float3 tSpace2:TEXCOORD7;
    float3 tSpace3:TEXCOORD8;
};

sampler2D _MainTex;fixed4 _MainTex_ST;
fixed4 _Color;
half _Smoothness;
half _Metallic;sampler2D _MetallicTex;
half _AO;sampler2D _AOTex;
sampler2D _NormalTex;

v2f vert(a2v v)
{
    v2f o;
    UNITY_INITIALIZE_OUTPUT(v2f,o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv,_MainTex);

    fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
    fixed3 worldTangent = UnityObjectToWorldDir(v.tangent);
    fixed3 worldBinormal = cross(worldNormal, worldTangent);
    o.tSpace1 = fixed3(worldTangent.x, worldBinormal.x, worldNormal.x);
    o.tSpace2 = fixed3(worldTangent.y, worldBinormal.y, worldNormal.y);
    o.tSpace3 = fixed3(worldTangent.z, worldBinormal.z, worldNormal.z);

    o.worldNormal = worldNormal;
    o.worldPos = mul(unity_ObjectToWorld,v.vertex);
    UNITY_TRANSFER_LIGHTING(o,v.texcoord1.xy);
    UNITY_TRANSFER_FOG(o,o.pos);
    return o;
}

fixed4 frag(v2f i):SV_Target
{
    UNITY_EXTRACT_FOG(i);

    float3 worldPos = i.worldPos.xyz;

    float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));

    SurfaceOutputStandard o;
    UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard,o);

    o.Albedo = tex2D(_MainTex,i.uv.xy) * _Color;
    float3 norTex = UnpackNormal(tex2D(_NormalTex, i.uv));
    o.Normal = fixed3(dot(i.tSpace1,norTex),dot(i.tSpace2,norTex),dot(i.tSpace3,norTex));
    o.Emission = 0;
    o.Metallic = tex2D(_MetallicTex,i.uv).r * _Metallic;
    o.Smoothness = _Smoothness;
    o.Occlusion = tex2D(_AOTex,i.uv).r * _AO;
    o.Alpha = 1;

    UNITY_LIGHT_ATTENUATION(atten, i, worldPos);

    UnityGI gi;
    UNITY_INITIALIZE_OUTPUT(UnityGI,gi);
    gi.indirect.diffuse = 0;
    gi.indirect.specular = 0;
    gi.light.color = _LightColor0.rgb;
    gi.light.dir = _WorldSpaceLightPos0;

    UnityGIInput giInput;
    UNITY_INITIALIZE_OUTPUT(UnityGIInput,giInput);
    giInput.light = gi.light;
    giInput.worldPos = worldPos;
    giInput.worldViewDir = worldViewDir;
    giInput.atten = atten;

    #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXED
        giInput.ambient = i.sh;
    #else
        giInput.ambient = 0.0;
    #endif
    giInput.probeHDR[0] = unity_SpecCube0_HDR;
    giInput.probeHDR[1] = unity_SpecCube1_HDR;
    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
      giInput.boxMin[0] = unity_SpecCube0_BoxMin;
    #endif

    LightingStandard_GI(o,giInput,gi);

    fixed4 c = LightingStandard(o,worldViewDir,gi);

    UNITY_APPLY_FOG(_unity_fogCoord,c);

    return c;
}

ENDCG

CGInc-GI部分

主要是对LightingStandard_GI的解析

它就是去拿了全局烘焙贴图,当做漫反射

然后再去拿反射贴图(反射探针)烘焙出来的环境光贴图,当做高光反射

GI.cginc
 #ifndef UNITY_SPECCUBE_LOD_STEPS
#define UNITY_SPECCUBE_LOD_STEPS (6)
#endif

#include "UnityLightingCommon.cginc"

//源自UnityPBSLighting.cginc
struct SurfaceOutputStandard
{
    fixed3 Albedo;
    float3 Normal;
    half3 Emission;
    half Metallic;
    half Smoothness;
    half Occlusion;
    fixed Alpha;
};

//源自...不重要
struct Unity_GlossyEnvironmentData
{
    half roughness;
    half3 reflUVW;
};

//读取高光环境贴图
half3 Unity_GlossyEnvironment(UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{   
    //计算粗糙度,0-1区间是个上凸曲线
    half perceptualRoughness = glossIn.roughness;
    perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);

    //根据粗糙度去决定读取哪个mipmap,及LOD
    half mip = perceptualRoughness * UNITY_SPECCUBE_LOD_STEPS;
    //反射方向
    half3 R = glossIn.reflUVW;
    //读取高光环境贴图
    half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);

    return DecodeHDR(rgbm, hdr);
}

//获取高光反射部分
half3 UnityGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
{
    half3 specular = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
    return specular * occlusion;
}

//获取漫反射部分
void UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld, inout UnityGI gi)
{
    //如果有烘焙光照,就去读Baked贴图
    #if defined(LIGHTMAP_ON)
        half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
        half3 bakedColor = DecodeLightmap(bakedColorTex);

        #ifdef DIRLIGHTMAP_COMBINED
            fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
            gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);
        #endif
    #endif

    //将衰减应用于灯光颜色在
    gi.light.color *= data.atten;

    //环境光遮蔽
    gi.indirect.diffuse *= occlusion;
}

//获取全局光照
void UnityGlobalIllumination(SurfaceOutputStandard s, UnityGIInput data, inout UnityGI gi)
{
    //获取漫反射部分
    UnityGI_Base(data, s.Occlusion, s.Normal, gi);
    
    //获得一个粗糙度和反射方向
    Unity_GlossyEnvironmentData glossIn;
    glossIn.roughness = 1 - s.Smoothness;
    glossIn.reflUVW = reflect(-data.worldViewDir, s.Normal);
    //获取高光反射
    gi.indirect.specular = UnityGI_IndirectSpecular(data, s.Occlusion, glossIn);
}

//套一层,主要这是Unity内置方法的魔改
void LightingStandard_GI(SurfaceOutputStandard s, UnityGIInput data, inout UnityGI gi)
{
    //将数据传入
    UnityGlobalIllumination(s, data, gi);
}

 

posted @ 2024-02-19 14:26  被迫吃冰淇淋的小学生  阅读(113)  评论(0)    收藏  举报