用vulkan写个引擎 (四)PBR着色器

PBR全称(Physicallly-Based Rendering),基于物理的渲染。本文将提供一份GLSL实用型着色器。对于理论部分网络上已经有太多文章了。

仓库:https://bitbucket.org/mm_longcheng/mm-vulkan

扣扣交流群:554329899

一、着色器入参问题

对于着色器入参的不同会导致着色器实现的不同
1. 顶点着色器的顶点属性,是否有骨骼蒙皮。
2.片段着色器对于PBR需要的采样器的提供程度不同,而顶点着色器传过来的数据也有不同。

我的方案是将入参形式做一些哈希,得到一些序号组合,然后分别实现它们。

以下是顶点着色器,片段着色器,管线的取名规则
```
    // Primitive Attribute flag
    // 
    // a0 | 0 NotNORMAL     NotTANGENT    | 1 HasNORMAL     NotTANGENT    | 2 HasNORMAL     HasTANGENT    |
    // a1 | 0 NotTEXCOORD0  NotTEXCOORD1  | 1 HasTEXCOORD0  NotTEXCOORD1  | 2 HasTEXCOORD0  HasTEXCOORD1  |
    // a2 | 0 NotCOLOR0VEC3 NotCOLOR0VEC4 | 1 HasCOLOR0VEC3 NotCOLOR0VEC4 | 2 HasCOLOR0VEC3 HasCOLOR0VEC4 |
    // a3 | 0 NotJOINTS0    NotJOINTS1    | 1 HasJOINTS0    NotJOINTS1    | 2 HasJOINTS0    HasJOINTS1    |
    //
    // Material Texture flag
    // 
    // m0 has TextureBaseColor
    // m1 has TextureMetallicRoughness
    // m2 has TextureNormal
    // m3 has TextureOcclusion
    // m4 has TextureEmissive
    //
    // Attribute Type flag
    // 
    // t0 type AttributeTEXCOORD0
    // t1 type AttributeTEXCOORD1
    // t2 type AttributeCOLOR0
    // t3 type AttributeJOINTS0
    // t4 type AttributeJOINTS1
    // t5 type AttributeWEIGHTS0
    // t6 type AttributeWEIGHTS1
    //
    // u0 = Primitive mode
    // d0 = Material double sided
    // 
    // vertex   shader hash: a3a2a1a0
    // fragment shader hash: a2a1a0-m4m3m2m1m0
    // pipeline        hash: a3a2a1a0-m4m3m2m1m0-t6t5t4t3t2t1t0-u0d0


    以下是顶点着色器入参格式的哈希规则

    // Primitive Component Type
    //     NONE            0
    //     UNSIGNED_BYTE   1
    //     UNSIGNED_SHORT  2
    //     FLOAT           3
    //
    // POSITION   "VEC3" 5126 (FLOAT)
    //
    // NORMAL     "VEC3" 5126 (FLOAT)
    //
    // TANGENT    "VEC4" 5126 (FLOAT)
    //
    // TEXCOORD_0 "VEC2" 5126 (FLOAT)                    
    //                   5121 (UNSIGNED_BYTE)normalized  
    //                   5123 (UNSIGNED_SHORT)normalized 
    //
    // COLOR_0    "VEC3" 5126 (FLOAT)                    
    //            "VEC4" 5121 (UNSIGNED_BYTE)normalized  
    //                   5123 (UNSIGNED_SHORT)normalized 
    //
    // JOINTS_0   "VEC4" 5121 (UNSIGNED_BYTE)            
    //                   5123 (UNSIGNED_SHORT)           
    //
    // WEIGHTS_0  "VEC4" 5126 (FLOAT)                    
    //                   5121 (UNSIGNED_BYTE)normalized  
    //                   5123 (UNSIGNED_SHORT)normalized 


	顶点着色器 常用81种                         取名位
	无法线无切线 有法线无切线 有法线有切线      3  000F 0000 0001 0002
	无纹理       有纹理0      有纹理1           3  00F0 0000 0010 0020
	无颜色       有颜色0vec3  有颜色0vec4       3  0F00 0000 0100 0200
	无骨骼       有骨骼0      有骨骼1           3  F000 0000 1000 2000
	
	片段着色器 常用27种                         取名位
	无法线无切线 有法线无切线 有法线有切线      3  000F 0000 0001 0002
	无纹理       有纹理0      有纹理1           3  00F0 0000 0010 0020
	无颜色       有颜色0vec3  有颜色0vec4       3  0F00 0000 0100 0200
```

以下是材质参数的哈希规则
```
void
mmVKTFMaterial_MakeLayoutHashId(
    const struct mmVKTFMaterial*                   p,
    char                                           l[2])
{
    int n = 0;

    n += (-1 != p->vIndex[mmVKTFTextureBaseColor]);
    n += (-1 != p->vIndex[mmVKTFTextureMetallicRoughness]);
    n += (-1 != p->vIndex[mmVKTFTextureNormal]);
    n += (-1 != p->vIndex[mmVKTFTextureOcclusion]);
    n += (-1 != p->vIndex[mmVKTFTextureEmissive]);

    // layout          hash: n
    sprintf(l, "%d", n);
}
```

例子:

我们以加载Cesium_Man.glb为例,以下是gltf中提供的数据

以下是模型的材质和图元信息
```
	"materials": [{
		"pbrMetallicRoughness": {
			"baseColorTexture": {
				"index": 0,
				"texCoord": 0
			},
			"metallicFactor": 0,
			"baseColorFactor": [1, 1, 1, 1],
			"roughnessFactor": 1
		},
		"emissiveFactor": [0, 0, 0],
		"alphaMode": "OPAQUE",
		"doubleSided": false,
		"name": "Cesium_Man-effect"
	}],

	"meshes": [{
		"primitives": [{
			"attributes": {
				"JOINTS_0": 1,
				"NORMAL": 2,
				"POSITION": 3,
				"TEXCOORD_0": 4,
				"WEIGHTS_0": 5
			},
			"indices": 0,
			"mode": 4,
			"material": 0
		}],
		"name": "Cesium_Man"
	}],
```

以下是从模型加载出来的顶点属性的具体格式
```
VkVertexInputBindingDescription vibd = 
{
	"POSITION"
	binding	0	unsigned int
	stride	12	unsigned int
	inputRate	VK_VERTEX_INPUT_RATE_VERTEX (0)	VkVertexInputRate

	"NORMAL"
	binding	1	unsigned int
	stride	12	unsigned int
	inputRate	VK_VERTEX_INPUT_RATE_VERTEX (0)	VkVertexInputRate

	"TEXCOORD_0"
	binding	2	unsigned int
	stride	8	unsigned int
	inputRate	VK_VERTEX_INPUT_RATE_VERTEX (0)	VkVertexInputRate

	"JOINTS_0"
	binding	3	unsigned int
	stride	8	unsigned int
	inputRate	VK_VERTEX_INPUT_RATE_VERTEX (0)	VkVertexInputRate

	"WEIGHTS_0"
	binding	4	unsigned int
	stride	16	unsigned int
	inputRate	VK_VERTEX_INPUT_RATE_VERTEX (0)	VkVertexInputRate
}

VkVertexInputAttributeDescription viad = 
{
	"POSITION"
	location	0	unsigned int
	binding	0	unsigned int
	format	VK_FORMAT_R32G32B32_SFLOAT (106)	VkFormat
	offset	0	unsigned int

	"NORMAL"
	location	1	unsigned int
	binding	1	unsigned int
	format	VK_FORMAT_R32G32B32_SFLOAT (106)	VkFormat
	offset	0	unsigned int

	"TEXCOORD_0"
	location	2	unsigned int
	binding	2	unsigned int
	format	VK_FORMAT_R32G32_SFLOAT (103)	VkFormat
	offset	0	unsigned int

	"JOINTS_0"
	location	3	unsigned int
	binding	3	unsigned int
	format	VK_FORMAT_R16G16B16A16_UINT (95)	VkFormat
	offset	0	unsigned int

	"WEIGHTS_0"
	location	4	unsigned int
	binding	4	unsigned int
	format	VK_FORMAT_R32G32B32A32_SFLOAT (109)	VkFormat
	offset	0	unsigned int
}
```

我们得到了入参的哈希信息

它有骨骼动画 1
没有颜色入参 0
有纹理0      1
有法线无切线 1

它有baseColor基础漫反射贴图                       1
没有metallicRoughnessTexture物理属性贴图          0
没有normalTexture法线贴图                        0
没有occlusionTexture遮蔽贴图                     0
没有emissiveTexture高光贴图                      0

VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST            3
没有使用doubleSided双面渲染                      0

Pipeline lName: mmVKTFMaterial-1
Pipeline vName: spiv/gltf/3d-1011.vert.spv
Pipeline fName: spiv/gltf/3d-011-00001.frag.spv
Pipeline xName: 3d-1011-00001-0302003-30

二、PBR着色器函数

网上关于这个的其实很多,但是并没有把它们拼合成一个可用的版本,都在罗列理论,函数比较零散。

下面是顶点着色器和片段着色器的部分,注意我们是通过一些开关来处理不同入参形式。

顶点着色器 3d-1011.vert
```
#version 450

#extension GL_GOOGLE_include_directive : enable

#include "vs-UBOScene.glsl"
#include "vs-PushConstants.glsl"
#include "vs-UBOSkin.glsl"

layout (location = 0) in vec3  a_position;
layout (location = 1) in vec3  a_normal;
layout (location = 2) in vec2  a_texcoord[1];
layout (location = 3) in uvec4 a_joints_0;
layout (location = 4) in vec4  a_weights_0;

layout (location = 0) out vec3 v_position;
layout (location = 1) out vec3 v_normal;
layout (location = 2) out vec2 v_texcoord[1];

out gl_PerVertex
{
    vec4 gl_Position;
};

void main()
{
    // world space.
    mat4 skinMat = mat4( 0.0 );
    skinMat += a_weights_0.x * uboSkin.joints[a_joints_0.x];
    skinMat += a_weights_0.y * uboSkin.joints[a_joints_0.y];
    skinMat += a_weights_0.z * uboSkin.joints[a_joints_0.z];
    skinMat += a_weights_0.w * uboSkin.joints[a_joints_0.w];

    mat4 modelMat = constants.model * skinMat;
    mat3 normalMat = transpose(inverse(mat3(modelMat)));
    v_position = vec3(modelMat * vec4(a_position, 1.0));
    v_normal = normalize(normalMat * a_normal);
    v_texcoord[0] = a_texcoord[0];
    gl_Position = uboScene.projection * uboScene.view * vec4(v_position, 1.0);
}
```

片段着色器 3d-011-00001.frag
```
#version 450

#extension GL_GOOGLE_include_directive : enable

// Interface protocol macro definition.
#define HAS_VaryingNormal
// #define HAS_VaryingTBN
// #define HAS_VaryingColor
#define HAS_VaryingTexcoord

// Material texture macro definition.
#define HAS_TextureBaseColor
// #define HAS_TextureMetallicRoughness
// #define HAS_TextureNormal
// #define HAS_TextureOcclusion
// #define HAS_TextureEmissive

// Vertex input Protocol.
layout (location = 0) in vec3 v_position;
layout (location = 1) in vec3 v_normal;
layout (location = 2) in vec2 v_texcoord[1];

// Material sampler Protocol.
layout (set = 1, binding = 1) uniform sampler2D baseColorTexture;

#include "fs-Main.glsl"
```

下面是include的实际部分

vs-UBOScene.glsl
```
layout (set = 0, binding = 0, std140) uniform UBOScene
{
    mat4 projection;
    mat4 view;
} uboScene;
```

vs-PushConstants.glsl
```
layout (push_constant, std140) uniform PushConstants
{
    mat4 normal;
    mat4 model;
} constants;
```

vs-UBOSkin.glsl
```
layout (set = 2, binding = 0, std430) readonly buffer UBOSkin 
{
    mat4 joints[];
} uboSkin;
```

fs-Main.glsl
```
#include "fs-Constant.glsl"
#include "fs-PerturbNormal.glsl"
#include "fs-SRGB.glsl"
#include "fs-Lights.glsl"
#include "fs-UBOScene.glsl"
#include "fs-UBOMaterial.glsl"
#include "fs-BRDF.glsl"
#include "fs-ToneMap.glsl"
#include "fs-AlphaMode.glsl"

layout (location = 0) out vec4 outColor;

void main()
{
    // view vector
    // world space.
    vec3 V = normalize(uboScene.camera - v_position);
    
    // normal vector
    vec3 N = GetNormal();

    vec4 baseColor = GetBaseColor();
    
    baseColor = AlphaModeColor(baseColor);
    
#ifndef HAS_TextureMetallicRoughness
    float metallic = uboMaterial.metallicFactor;
    float roughness = uboMaterial.roughnessFactor;
#else
    vec2 value = GetMetallicRoughness();
    float metallic = value.x;
    float roughness = value.y;
#endif//HAS_TextureMetallicRoughness    
    
    vec3 color = vec3(0.0);
    vec3 ambient = uboScene.ambient;
    vec3 ambientColor = baseColor.rgb * ambient;
    
    const vec3 black = vec3(0.0);
    vec3 diffuseColor = mix(baseColor.rgb, black, metallic);
    const float f90 = 1.0;
    vec3 f0 = mix(vec3(0.04), baseColor.rgb, metallic);
    
    int i;
    for (i = 0;i < uboScene.lightCount;++i)
    {
        UBOLight light = uboLights.lights[i];
        vec3 L = GetPointToLight(light, v_position);
        vec3 intensity = GetLighIntensity(light, L);
        vec3 material  = BRDF_Material(L, V, N, diffuseColor, f0, f90, roughness);
        color += material * intensity;
    }
    
    color += ambientColor;

    /*
    vec3 lightColor   = uboScene.lightColor;
    float distance    = length(L);
    float attenuation = 1.0 / (distance * distance);
    vec3 radiance     = lightColor * attenuation;
    vec3 material     = BRDF_Material(L, V, N, diffuseColor, f0, f90, roughness);
    
    color = material * radiance + ambientColor;
    */

#ifdef HAS_TextureOcclusion
    color = GetOcclusionColor(color);
#endif//HAS_TextureOcclusion

#ifdef HAS_TextureEmissive
    color += GetEmissiveColor();
#endif//HAS_TextureEmissive

    // Tone mapping
    outColor = vec4(toneMap(color), baseColor.a);
}
```

fs-Constant.glsl
```
#define MM_E                  2.71828182845904523536028747135266250   /* e           */
#define MM_LOG2E              1.44269504088896340735992468100189214   /* log2(e)     */
#define MM_LOG10E             0.434294481903251827651128918916605082  /* log10(e)    */
#define MM_LN2                0.693147180559945309417232121458176568  /* loge(2)     */
#define MM_LN10               2.30258509299404568401799145468436421   /* loge(10)    */
#define MM_PI                 3.14159265358979323846264338327950288   /* pi          */
#define MM_PI_DIV_2           1.57079632679489661923132169163975144   /* pi/2        */
#define MM_PI_DIV_4           0.785398163397448309615660845819875721  /* pi/4        */
#define MM_1_DIV_PI           0.318309886183790671537767526745028724  /* 1/pi        */
#define MM_2_DIV_PI           0.636619772367581343075535053490057448  /* 2/pi        */
#define MM_2_DIV_SQRTPI       1.12837916709551257389615890312154517   /* 2/sqrt(pi)  */
#define MM_SQRT2              1.41421356237309504880168872420969808   /* sqrt(2)     */
#define MM_1_DIV_SQRT2        0.707106781186547524400844362104849039  /* 1/sqrt(2)   */

#define MM_EPSILON   1e-6

#define saturate( a ) clamp( a, 0.0, 1.0 )

#define pow2( a ) (a * a)

const float MM_GAMMA = 2.2;
const float MM_1_DIV_GAMMA = 1.0 / MM_GAMMA;

// enum mmGLTFAlphaMode_t
const int mmGLTFAlphaModeOpaque  = 0;// "OPAQUE"
const int mmGLTFAlphaModeMask    = 1;// "MASK"
const int mmGLTFAlphaModeBlend   = 2;// "BLEND"
```

fs-PerturbNormal.glsl
```
// Bump Mapping.
// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
// http://api.unrealengine.com/attachments/Engine/Rendering/LightingAndShadows/BumpMappingWithoutTangentSpace/mm_sfgrad_bump.pdf
vec3 PerturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection) 
{
    // Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
    vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );
    vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );
    vec3 vN = surf_norm;        // normalized

    vec3 R1 = cross( vSigmaY, vN );
    vec3 R2 = cross( vN, vSigmaX );

    float fDet = dot( vSigmaX, R1 ) * faceDirection;

    vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
    return normalize( abs( fDet ) * surf_norm - vGrad );
}

// Normal Mapping Without Precomputed Tangents
// http://www.thetenthplanet.de/archives/1180
vec3 PerturbNormal2Arb(vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection, vec2 vUv) 
{
    // Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
    vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
    vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
    vec2 st0 = dFdx( vUv.st );
    vec2 st1 = dFdy( vUv.st );

    vec3 N = surf_norm; // normalized

    vec3 q1perp = cross( q1, N );
    vec3 q0perp = cross( N, q0 );

    vec3 T = q1perp * st0.x + q0perp * st1.x;
    vec3 B = q1perp * st0.y + q0perp * st1.y;

    float det = max( dot( T, T ), dot( B, B ) );
    float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );

    return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );
}
```

fs-SRGB.glsl
```
// linear to sRGB approximation
// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
vec3 linearTosRGB(vec3 color)
{
    return pow(color, vec3(MM_1_DIV_GAMMA));
}

// sRGB to linear approximation
// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
vec3 sRGBToLinear(vec3 srgbIn)
{
    return vec3(pow(srgbIn.xyz, vec3(MM_GAMMA)));
}

vec4 sRGBToLinear(vec4 srgbIn)
{
    return vec4(sRGBToLinear(srgbIn.xyz), srgbIn.w);
}
```

fs-Lights.glsl
```
// KHR_lights_punctual extension.
// see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
struct UBOLight
{
    vec3 direction;
    vec3 position;
    vec3 color;
    float range;
    float intensity;
    float innerConeCos;
    float outerConeCos;
    int type;
};

const int mmGLTFLightTypeInvalid      = 0; //  Invalid
const int mmGLTFLightTypeDirectional  = 1; // "directional"
const int mmGLTFLightTypePoint        = 2; // "point"
const int mmGLTFLightTypeSpot         = 3; // "spot"

// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#range-property
float GetRangeAttenuation(
    const in float range, 
    const in float distance)
{
    if (range <= 0.0)
    {
        // negative range means unlimited
        return 1.0 / pow(distance, 2.0);
    }
    else
    {
        return max(min(1.0 - pow(distance / range, 4.0), 1.0), 0.0) / pow(distance, 2.0);
    }
}

// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#inner-and-outer-cone-angles
float GetSpotAttenuation(
    const in vec3  pointToLight, 
    const in vec3  spotDirection, 
    const in float outerConeCos, 
    const in float innerConeCos)
{
    float actualCos = dot(normalize(spotDirection), normalize(-pointToLight));
    if (actualCos > outerConeCos)
    {
        if (actualCos < innerConeCos)
        {
            return smoothstep(outerConeCos, innerConeCos, actualCos);
        }
        else
        {
            return 1.0;
        }
    }
    else
    {
        return 0.0;
    }
}

vec3 GetLighIntensity(
    const in UBOLight light, 
    const in vec3 pointToLight)
{
    float rangeAttenuation = 1.0;
    float spotAttenuation = 1.0;

    if (light.type != mmGLTFLightTypeDirectional)
    {
        rangeAttenuation = GetRangeAttenuation(
            light.range, 
            length(pointToLight));
    }
    
    if (light.type == mmGLTFLightTypeSpot)
    {
        spotAttenuation = GetSpotAttenuation(
            pointToLight, 
            light.direction, 
            light.outerConeCos, 
            light.innerConeCos);
    }

    return rangeAttenuation * spotAttenuation * light.intensity * light.color;
}

vec3 GetPointToLight(
    const in UBOLight light, 
    const in vec3 position)
{
    if (light.type != mmGLTFLightTypeDirectional)
    {
        return light.position - position;
    }
    else
    {
        return -light.direction;
    }
}
```

fs-UBOScene.glsl
```
layout (set = 0, binding = 1, std140) uniform UBOScene
{
    vec3 camera;
    vec3 ambient;
    float exposure;
    int lightCount;
} uboScene;

layout (set = 0, binding = 2, std430) readonly buffer UBOLights 
{
    UBOLight lights[];
} uboLights;
```

fs-UBOMaterial.glsl
```
struct UBOTextureInfo
{
    int index;
    int texCoord;
};

layout (set = 1, binding = 0, std140) uniform UBOMaterial 
{
    UBOTextureInfo baseColorTexture;
    UBOTextureInfo metallicRoughnessTexture;
    UBOTextureInfo normalTexture;
    UBOTextureInfo occlusionTexture;
    UBOTextureInfo emissiveTexture;
    vec4 baseColorFactor;
    vec3 emissiveFactor;
    float metallicFactor;
    float roughnessFactor;
    float scale;
    float strength;
    float alphaCutoff;
    int alphaMode;
    int doubleSided;
} uboMaterial;

// BaseColor
#ifndef HAS_VaryingColor

#ifndef HAS_TextureBaseColor

vec4 GetBaseColor()
{
    return uboMaterial.baseColorFactor;
}

#else

#ifndef HAS_VaryingTexcoord
vec4 GetBaseColor()
{
    return uboMaterial.baseColorFactor;
}
#else
vec4 GetBaseColor()
{
    vec4 color;
    UBOTextureInfo t = uboMaterial.baseColorTexture;
    if (-1 == t.index)
    {
        color = uboMaterial.baseColorFactor;
    }
    else
    {
        color = texture(baseColorTexture, v_texcoord[t.texCoord]);
        color = uboMaterial.baseColorFactor * sRGBToLinear(color);
    }
    return color;
}
#endif//HAS_VaryingTexcoord

#endif//HAS_TextureBaseColor

#else

#ifndef HAS_TextureBaseColor

vec4 GetBaseColor()
{
    return uboMaterial.baseColorFactor * v_color_0;
}

#else

#ifndef HAS_VaryingTexcoord
vec4 GetBaseColor()
{
    return uboMaterial.baseColorFactor * v_color_0;
}
#else
vec4 GetBaseColor()
{
    vec4 color;
    UBOTextureInfo t = uboMaterial.baseColorTexture;
    if (-1 == t.index)
    {
        color = uboMaterial.baseColorFactor;
    }
    else
    {
        color = texture(baseColorTexture, v_texcoord[t.texCoord]);
        color = uboMaterial.baseColorFactor * sRGBToLinear(color);
    }
    return color * v_color_0;
}
#endif//HAS_VaryingTexcoord

#endif//HAS_TextureBaseColor

#endif//HAS_VaryingColor

// Normal
#ifndef HAS_TextureNormal

#ifndef HAS_VaryingNormal

#ifndef HAS_VaryingTBN
// 0 NotNORMAL     NotTANGENT  NotTextureNormal
vec3 GetNormal()
{
    // Use flat normal
    return normalize(cross(dFdx(v_position), dFdy(v_position)));
}
#else

// x NotNORMAL     HasTANGENT  NotTextureNormal
vec3 GetNormal()
{
    // Use TBN normal
    return normalize(v_tbn[2]);
}

#endif//HAS_VaryingTBN

#else

#ifndef HAS_VaryingTBN
// 1 HasNORMAL     NotTANGENT  NotTextureNormal
vec3 GetNormal()
{
    // Use varying normal
    return normalize(v_normal);
}
#else
// 2 HasNORMAL     HasTANGENT  NotTextureNormal
vec3 GetNormal()
{
    // Use TBN normal
    return normalize(v_tbn[2]);
}
#endif//HAS_VaryingTBN

#endif//HAS_VaryingNormal

#else

#ifndef HAS_VaryingNormal

#ifndef HAS_VaryingTBN
// 0 NotNORMAL     NotTANGENT  HasTextureNormal
vec3 GetNormal()
{
    vec3 normal;
    UBOTextureInfo t = uboMaterial.normalTexture;
    if (-1 == t.index)
    {
        // Use flat normal
        normal = normalize(cross(dFdx(v_position), dFdy(v_position)));
    }
    else
    {
        vec2 vUv = v_texcoord[t.texCoord];
        normal = texture(normalTexture, vUv).rgb;
        normal = normalize(normal * 2.0f - 1.0f);
    }
    return normal;
}
#else
// x NotNORMAL     HasTANGENT  HasTextureNormal
vec3 GetNormal()
{
    vec3 normal;
    UBOTextureInfo t = uboMaterial.normalTexture;
    if (-1 == t.index)
    {
        // Use TBN normal
        normal = normalize(v_tbn[2]);
    }
    else
    {
        vec2 vUv = v_texcoord[t.texCoord];
        normal = texture(normalTexture, vUv).rgb;
        normal = normalize(normal * 2.0f - 1.0f);
        normal = normalize(v_tbn * normal);
    }
    return normal;
}

#endif//HAS_VaryingTBN

#else

#ifndef HAS_VaryingTBN
// 1 HasNORMAL     NotTANGENT  HasTextureNormal
vec3 GetNormal()
{
    vec3 normal;
    UBOTextureInfo t = uboMaterial.normalTexture;
    if (-1 == t.index)
    {
        // Use varying normal
        normal = normalize(v_normal);
    }
    else
    {
        // face direction.
        float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;

        vec2 vUv = v_texcoord[t.texCoord];
        normal = texture(normalTexture, vUv).rgb;
        normal = normalize(normal * 2.0f - 1.0f);
        normal = PerturbNormal2Arb(-uboScene.camera, v_normal, normal, faceDirection, vUv);
    }
    return normal;
}
#else
// 2 HasNORMAL     HasTANGENT  HasTextureNormal
vec3 GetNormal()
{
    vec3 normal;
    UBOTextureInfo t = uboMaterial.normalTexture;
    if (-1 == t.index)
    {
        // Use varying normal
        normal = normalize(v_normal);
    }
    else
    {
        normal = texture(normalTexture, v_texcoord[t.texCoord]).rgb;
        normal = normalize(normal * 2.0f - 1.0f);
        normal = normalize(v_tbn * normal);
    }
    return normal;
}
#endif//HAS_VaryingTBN

#endif//HAS_VaryingNormal

#endif//HAS_TextureNormal

// Metallic Roughness
#ifndef HAS_TextureMetallicRoughness
vec2 GetMetallicRoughness()
{
    vec2 value;
    value.x = uboMaterial.metallicFactor;
    value.y = uboMaterial.roughnessFactor;
    return value;
}
#else
vec2 GetMetallicRoughness()
{
    vec2 value;
    UBOTextureInfo t = uboMaterial.metallicRoughnessTexture;
    if (-1 == t.index)
    {
        value.x = uboMaterial.metallicFactor;
        value.y = uboMaterial.roughnessFactor;
    }
    else
    {
        value = texture(metallicRoughnessTexture, v_texcoord[t.texCoord]).bg;
    }
    return value;
}
#endif//HAS_TextureMetallicRoughness

// Occlusion
#ifndef HAS_TextureOcclusion
vec3 GetOcclusionColor(vec3 color)
{
    return color;
}
#else
vec3 GetOcclusionColor(vec3 color)
{
    float occlusion;
    UBOTextureInfo t = uboMaterial.occlusionTexture;
    if (-1 == t.index)
    {
        occlusion = 1.0;
    }
    else
    {
        occlusion = texture(occlusionTexture, v_texcoord[t.texCoord]).r;
        color = mix(color, color * occlusion, uboMaterial.strength);
    }
    return color;
}
#endif//HAS_TextureOcclusion

// Emissive
#ifndef HAS_TextureEmissive
vec3 GetEmissiveColor()
{
    return vec3(0.0);
}
#else
vec3 GetEmissiveColor()
{
    vec3 color;
    UBOTextureInfo t = uboMaterial.emissiveTexture;
    if (-1 == t.index)
    {
        color = vec3(0.0);
    }
    else
    {
        color = texture(emissiveTexture, v_texcoord[t.texCoord]).rgb;
        color = uboMaterial.emissiveFactor * sRGBToLinear(color);
    }
    return color;
}
#endif//HAS_TextureEmissive
```

fs-BRDF.glsl
```
// Fresnel Schlick approximation translates
vec3 F_Schlick(
    const in vec3  f0, 
    const in float f90, 
    const in float dotVH) 
{
    // Rf(l,h) = F0 + (1 - F0) * (1 - (l dot h))^5
    // F0 = Ff(h,h) = Cspec
    float fresnel = pow(1.0 - dotVH, 5.0);
    return f0 + (f90 - f0) * fresnel;
}

// Fresnel Schlick approximation translates
vec3 F_SchlickEpic( 
    const in vec3  f0, 
    const in float f90, 
    const in float dotVH) 
{
    // Original approximation by Christophe Schlick '94
    // float fresnel = pow( 1.0 - dotVH, 5.0 );
    //
    // Optimized variant (presented by Epic at SIGGRAPH '13)
    // https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
    float fresnel = exp2((-5.55473 * dotVH - 6.98316) * dotVH);

    return f0 + (f90 - f0) * fresnel;
}

// Geometric Shadowing function
// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
float V_GGX_SmithCorrelated( 
    const in float alpha, 
    const in float dotNL, 
    const in float dotNV) 
{
    float a2 = pow2( alpha );

    float gv = dotNL * sqrt(a2 + (1.0 - a2) * pow2(dotNV));
    float gl = dotNV * sqrt(a2 + (1.0 - a2) * pow2(dotNL));

    return 0.5 / max(gv + gl, MM_EPSILON);
}
// Geometric Shadowing function
float G_GGX( 
    const in float alpha, 
    const in float dotNL, 
    const in float dotNV) 
{
    float a2 = pow2( alpha );
    float GGXV = 2 * dotNV / (dotNV + sqrt(a2 + (1.0 - a2) * (dotNV * dotNV)));
    float GGXL = 2 * dotNL / (dotNL + sqrt(a2 + (1.0 - a2) * (dotNL * dotNL)));
    return GGXV * GGXL;
}

// Microfacet Models for Refraction through Rough Surfaces - equation (33)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// alpha is "roughness squared" in Disney’s reparameterization
float D_GGX( 
    const in float alpha, 
    const in float dotNH ) 
{
    float a2 = pow2(alpha);

    // avoid alpha = 0 with dotNH = 1
    float denom = pow2(dotNH) * (a2 - 1.0) + 1.0;

    return MM_1_DIV_PI * a2 / pow2(denom);
}

vec3 BRDF_Diffuse(
    const in vec3  diffuseColor)
{
    return MM_1_DIV_PI * diffuseColor;
}

vec3 BRDF_Specular(
    const in float dotNH, 
    const in float dotNL, 
    const in float dotNV, 
    const in float alpha)
{
    if (dotNL > 0.0 && dotNV > 0.0)
    {
        // Geometric Shadowing function
        float G = G_GGX(alpha, dotNL, dotNV);

        // Normal Distribution function
        float D = D_GGX(alpha, dotNH);

        // Visibility function
        float V = G / (4.0 * dotNL * dotNV);
        
        return vec3(V * D);
    }
    else
    {
        return vec3(0.0);
    }
}

vec3 BRDF_Material(
    const in vec3  L, 
    const in vec3  V, 
    const in vec3  N,
    const in vec3  diffuseColor,
    const in vec3  f0,
    const in float f90,
    const in float roughness)
{
    vec3 H = normalize (V + L);
    float dotNV = clamp(dot(N, V), 0.0, 1.0);
    float dotNL = clamp(dot(N, L), 0.0, 1.0);
    float dotNH = clamp(dot(N, H), 0.0, 1.0);
    float dotVH = clamp(dot(V, H), 0.0, 1.0);
    float alpha = roughness * roughness;
    vec3        F = F_Schlick(f0, f90, dotVH);
    vec3  diffuse = BRDF_Diffuse(diffuseColor);
    vec3 specular = BRDF_Specular(dotNH, dotNL, dotNV, alpha);
    vec3 material = (1 - F) * diffuse + F * specular;
    return dotNL * material;
}
```

fs-ToneMap.glsl
```
// ACES tone map (faster approximation)
// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
vec3 toneMapACES_Narkowicz(vec3 color)
{
    const float A = 2.51;
    const float B = 0.03;
    const float C = 2.43;
    const float D = 0.59;
    const float E = 0.14;
    return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
}

vec3 toneMap(vec3 color)
{
    color *= uboScene.exposure;

    color = toneMapACES_Narkowicz(color);

    return linearTosRGB(color);
}
```

fs-AlphaMode.glsl
```
vec4 AlphaModeColor(vec4 baseColor)
{
    switch(uboMaterial.alphaMode)
    {
    case mmGLTFAlphaModeOpaque:
    {
        baseColor.a = 1.0;
    }
    break;
    case mmGLTFAlphaModeMask:
    {
        // Late discard to avoid samplig artifacts. 
        // See https://github.com/KhronosGroup/glTF-Sample-Viewer/issues/267
        if (baseColor.a < uboMaterial.alphaCutoff)
        {
            discard;
        }
        baseColor.a = 1.0;
    }
    break;
    case mmGLTFAlphaModeBlend:
    default:
    break;
    }

    return baseColor;
}
```

三、PBR原理

这里主要说一下BRDF的部分

根据gltf官方文档,实现pbr的混色流程

 注意,这里得到的material并不是最终颜色,我们需要将结果乘以dotNL。

下面是pbr的实现理论 glTF-2.0-and-PBR-GTC_May17.pdf

仅截图BRDF的部分

 

下面是对部分函数实现不同版本的探讨

Fresnel Schlick 有两个版本,我使用的原始版本,这两个函数从渲染结果来看并没有太大不同

// Fresnel Schlick approximation translates
vec3 F_Schlick(
    const in vec3  f0, 
    const in float f90, 
    const in float dotVH) 
{
    // Rf(l,h) = F0 + (1 - F0) * (1 - (l dot h))^5
    // F0 = Ff(h,h) = Cspec
    float fresnel = pow(1.0 - dotVH, 5.0);
    return f0 + (f90 - f0) * fresnel;
}

// Fresnel Schlick approximation translates
vec3 F_SchlickEpic( 
    const in vec3  f0, 
    const in float f90, 
    const in float dotVH) 
{
    // Original approximation by Christophe Schlick '94
    // float fresnel = pow( 1.0 - dotVH, 5.0 );
    //
    // Optimized variant (presented by Epic at SIGGRAPH '13)
    // https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
    float fresnel = exp2((-5.55473 * dotVH - 6.98316) * dotVH);

    return f0 + (f90 - f0) * fresnel;
}

 Geometric Shadowing function 也有两个版本,第一个版本是从three.js获取的,第二个是根据官方给的pdf实现的。从渲染结果来看,我使用了原始版本。第一个版本在模型边缘处会产生一些高亮瑕疵,可能是我使用方式不太对。

// Geometric Shadowing function
// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
float V_GGX_SmithCorrelated( 
    const in float alpha, 
    const in float dotNL, 
    const in float dotNV) 
{
    float a2 = pow2( alpha );

    float gv = dotNL * sqrt(a2 + (1.0 - a2) * pow2(dotNV));
    float gl = dotNV * sqrt(a2 + (1.0 - a2) * pow2(dotNL));

    return 0.5 / max(gv + gl, MM_EPSILON);
}
// Geometric Shadowing function
float G_GGX( 
    const in float alpha, 
    const in float dotNL, 
    const in float dotNV) 
{
    float a2 = pow2( alpha );
    float GGXV = 2 * dotNV / (dotNV + sqrt(a2 + (1.0 - a2) * (dotNV * dotNV)));
    float GGXL = 2 * dotNL / (dotNL + sqrt(a2 + (1.0 - a2) * (dotNL * dotNL)));
    return GGXV * GGXL;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值