一步步学Metal图形引擎7-《镜面反射》

Posted Mr_厚厚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步步学Metal图形引擎7-《镜面反射》相关的知识,希望对你有一定的参考价值。

教程 7

镜面反射

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

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

一、原理

1.1 镜面反射光

上一篇教程中基于Lambert模型计算的漫反射较好的表现了粗糙物体表面的光泽,像石灰墙、泥塑等。但对于表面光滑的镜子、玻璃、金属、水面、瓷碗等则表现不出那种耀眼的光泽。这一章在漫反射的基础上,我们继续介绍镜面反射光,用来表现光滑表面的光泽。

镜面反射光,又称高光,指的是当相机视线和光源在光滑物体表面的反射光线基本重合时发出耀眼光芒的现象。可以看出镜面反射光和观察者眼睛的位置直接相关,所以镜面反射的计算中需要加入相机视角变量V。镜面反射实现的情况下,对于表面光滑的物体,当眼睛接近反射光方向时物体表面亮度很高以至于成为白色的光看不出物体表面的颜色,当眼睛偏离反射光方向时亮度又急速衰减以至于镜面反射光消失。

1.2 Phong模型

基本的镜面反射光我们使用Phong模型来建模表达。冯氏模型是一个经验模型,在漫反射的基础上加上了镜面反射分量,和真实的物理模型有差距但是简单高效。冯氏模型中镜面反射光强度的结果跟反射光强和反射光与视线的夹角相关,模型的数学表达式如下:


Phong模型公式分为两部分,前半部分是漫反射分量,后半部分则是镜面反射分量。IL是光照强度,ks是物体表面材质的镜面反射系数(范围为0~1),r是反射光向量,v是视线方向向量,角度phi是r和v的夹角,通过点积计算得到。

最后n是高光指数,反应物体表面的光泽程度。n越大高光对视线的移动越敏感,高光范围越集中,高光越强;n越小表示表面越粗糙,高光越分散,强度越小。


另外关于反射光向量r可以通过入射光方向L和法线n求出:


所以反射光向量为:

二、源码分析

2.1ShaderTypes.h

typedef struct

    matrix_float4x4 projectionMatrix;
    matrix_float4x4 modelViewMatrix;
    matrix_float4x4 modelMatrix;
    matrix_float4x4 viewMatrix;
        
    vector_float3 directionalLightDirection;
    vector_float3 directionalLightColor;
    
    float IL; // 光源强度
    float Kd; // 漫反射系数
    float Ks; // 镜面反射系数
    float shininess; // 镜面反射高光指数
    
    vector_float3 cameraPos; // 相机世界坐标
 Uniforms;

首先Uniform Buffer中,我们增加了镜面反射的两个参数,另外还添加了相机的位置,因为在计算镜面反射的时候我们需要通过相机位置和物体表面片段位置做差来计算视线的方向V。

2.2 Render.m

// ...
uniforms->Ks = 0.9f;
uniforms->shininess = 15.0f;
    
uniforms->cameraPos = (vector_float3)0,100,-1100;
// ...

updateGameState函数中我们设置了镜面反射相关的值。镜面反射系数Ks我们设置的比较大,表示模型表面比较光滑反光能力强,shininess高光指数也设置的比较大,使得高光集中,反光明亮效果突出。cameraPos设置相机的世界坐标。

2.3 Shaders.metal

// ...
out.worldPos = uniforms.modelMatrix * position;
// ...

顶点着色器中我们另外计算了顶点的世界坐标,因为我们要在世界空间计算视线V的方向。

// ...
// 视线方向
float3 V = normalize(uniforms.cameraPos - in.worldPos.xyz);
// 反射光方向
float3 R = normalize(2 * fmax(dot(N, L), 0) * N - L);
// ...

光照计算还是在片段着色阶段。根据世界空间相机的位置和当前片段的坐标计算的到视线方向V。另外根据我们分析的公式用法线向量N和入射光方向L求出反射光方向R。

// ...
float diffuse = uniforms.IL * uniforms.Kd * max(dot(float3(in.normal.xyz),L),0.0);
float specular = uniforms.IL * uniforms.Ks * pow(fmax(dot(V, R), 0), uniforms.shininess);
// ...

使用前面准备好的变量,我们实现Pong模型中漫反射光强度分量和镜面反射光强度分量的计算。

// ...
float3 out = float3(uniforms.directionalLightColor) * float3(color_sample.xyz) * (diffuse + specular);
// ...

最后叠加漫反射和镜面反射光强度,得到Phong模型最终的光照效果。

三、运行效果

可以看到加上镜面反射光,模型表面的光影层次更加清晰,明暗更加分明,光斑效果突出,光线随着物体移动更有交互性和动态性。

以上是关于一步步学Metal图形引擎7-《镜面反射》的主要内容,如果未能解决你的问题,请参考以下文章

一步步学Metal图形引擎8-《环境光》

一步步学Metal图形引擎8-《环境光》

一步步学Metal图形引擎9-《Blinn-Phong光照模型》

一步步学Metal图形引擎9-《Blinn-Phong光照模型》

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

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