【一步步学Metal图形引擎6】-《漫反射》

教程 6

漫反射

教程源码下载地址: https://github.com/jiangxh1992/MetalTutorialDemos

CSDN完整版专栏: https://blog.csdn.net/cordova/category_9734156.html

一、原理

1.1 基础光照

之前的教程我们实现了将一个模型加载到场景中,并实现了模型的矩阵变换和相机的移动变换。但我们之前并没有开始真正的渲染,只是通过纹理采样将贴图上的颜色显示到了屏幕上。这篇教程开始我们将讨论场景中的一些基础光照模型的渲染,将光线的计算加入到我们的场景中。

基础的光照计算主要包括漫反射、镜面反射和环境光,这些光照模型属于本地照明模型,是在模型表面上对光照建模计算光照后的颜色。

模型表面建模的几个关键变量有:法线n,入射光L和观察者视线v。计算过程中只关心进入眼睛的光线。

【一步步学Metal图形引擎6】-《漫反射》_第1张图片

1.2 平行光

平行光是一种只有固定方向,没有光源的光线,实际上指的就是太阳光。由于太阳离地球太远,可以认为太阳光线是平行光,光线全都平行,以一个固定方向射到物体表面。定义一个平行光只需要一个表示方向的向量,另外可以定义光源的颜色。太阳光的颜色为白光。

【一步步学Metal图形引擎6】-《漫反射》_第2张图片

1.3 漫反射模型

漫反射是一种理想光照模型,指的是物体表面等同的向各个方向散射的现象。漫反射可以通过兰伯特定律(Lambert’s Law)来描述,计算公式为:

在这里插入图片描述

I是我们计算的漫反射光强结果,IL是光源的强度,kd(0~1)是物体表面的反射系数,theta是光线方向和法线的夹角。

Lambert漫反射描述的实际上是光源以一定角度入射到物体表面,在法线方向上的投影光强度。可见直射到表面时漫反射最强,随着夹角增大漫反射的强度逐渐衰弱。漫反射的光强和入射光的方向与表面法线夹角的余弦成正比即为Lambert定律。

【一步步学Metal图形引擎6】-《漫反射》_第3张图片

二、源码分析

2.1 Render.m

- (void)updateGameState
{
    Uniforms * uniforms = (Uniforms*)_uniformBuffer.contents;
    // P
    uniforms->projectionMatrix = _projectionMatrix;
    // V
    uniforms->viewMatrix = matrix_multiply(matrix4x4_translation(0, -100, 1100),
                                           matrix4x4_rotation(-0.5, (vector_float3){1,0,0}));
    // M
    uniforms->modelMatrix = matrix_multiply(matrix4x4_rotation(_rotation, (vector_float3){0,1,0}),
                                            matrix4x4_translation(0, 0, 0));
    // MV
    uniforms->modelViewMatrix = matrix_multiply(uniforms->viewMatrix, uniforms->modelMatrix);
        
    // 平行光
    uniforms->directionalLightDirection = (vector_float3){-1.0,-1.0,-1.0};
    uniforms->directionalLightColor = (vector_float3){0.8,0.8,0.8};
    
    uniforms->IL = 10.0f;
    uniforms->Kd = 0.1f;

    _rotation += 0.002f;
}

上一片教程我们通过矩阵变换实现让相机围着模型旋转,这里我们为了更好的展示光照模型效果,更新Uniform Buffer中的矩阵变量,固定相机视角,调整相机距离,并让模型自转。

此外这里我们要添加第一个光源,最基础的平行光,也就是太阳光。太阳光的光源认为无穷远,是一种只有方向没有光源的光照,我们只需要在Uniform Buffer中定义一个方向和光源颜色来表达这个光源即可。光源的方向我们设置为从右上方2点钟方向照向左下方,光源颜色设置为一定灰度的白光。

最后我们要在Uniform Buffer中添加并设置光源强度IL,物体表面漫反射系数Kd,这个系数的取值范围为0~1之间,这里设置为0.1。

2.2 ShaderTypes.h

typedef struct
{
    matrix_float4x4 projectionMatrix;
    matrix_float4x4 modelViewMatrix;
    matrix_float4x4 modelMatrix;
    matrix_float4x4 viewMatrix;
    
    float IL; // 光源强度
    float Kd; // 漫反射系数
        
    vector_float3 directionalLightDirection;
    vector_float3 directionalLightColor;
} Uniforms;

Uniform Buffer结构体中我们增加了平行光相关的数据,以及光源强度IL和漫反射系数Kd。

2.3 Shaders.metal

// ...
out.normal = normalize(uniforms.modelMatrix * float4((float3)in.normal,0));
// ...

我们模型顶点buffer中的坐标都是定义在模型空间坐标系的,光源是定义在世界空间,这里顶点着色器中我们需要将法线从模型空间变换到世界空间,统一在世界空间进行漫反射光照计算。

// ...
float3 N = float3(in.normal.xyz);
float3 L = normalize(-uniforms.directionalLightDirection);
    
// Lambert diffuse
float diffuse = uniforms.IL * uniforms.Kd * max(dot(N,L),0.0);
    
float3 out = float3(uniforms.directionalLightColor) * float3(color_sample.xyz) * diffuse;
// ...

光照的计算我们在片段着色器逐像素进行。参与计算的法向量为N,入射光方向取反和法线同向,向量都单位化。法线和入射光向量点积乘以反射系数Kd即为当前片段的漫反射光强度系数,乘以光强IL得到当前片段最终的漫反射光强度。

最后片段纹理颜色乘以光源颜色进行颜色叠加,然后乘以漫反射强度得到片段最终的漫反射颜色值,直接返回。

三、运行效果

根据漫反射的明暗层次可以看出光源在右上方,模型背向光源的会是漆黑一片,面向光源的则比较明亮。
【一步步学Metal图形引擎6】-《漫反射》_第4张图片

你可能感兴趣的:(一步步学Metal图形引擎)