Direct3D 11 Tutorial 6:Lighting_Direct3D 11 教程6:灯光

时间:2024-09-26 23:06:26

概述

在之前的教程中,世界看起来很无聊,因为所有对象都以相同的方式点亮。 本教程将介绍简单照明的概念及其应用方法。 使用的技术将是朗伯照明。

本教程的结果将修改前面的示例以包含光源。 该光源将附在轨道上的立方体上。 可以在中心立方体上看到光的影响。

资源目录

(SDK root)\Samples\C++\Direct3D11\Tutorials\Tutorial06

Github

灯光

在本教程中,将介绍最基本的照明类型:朗伯照明。 无论距离光线的距离如何,朗伯照明都具有均匀的强度。 当光照射到表面时,通过光在表面上的入射角计算反射的光量。 当光直接照射在表面上时,它显示出以最大强度反射所有光。 然而,随着光的角度增加,光的强度将逐渐消失。

为了计算光在表面上的强度,必须计算光方向与表面法线之间的角度。 表面的法线定义为垂直于表面的矢量。 角度的计算可以通过简单的点积来完成,该点积将光方向矢量的投影返回到法线上。 角度越宽,投影越小。 因此,这为我们提供了调制漫射光的正确功能。

Direct3D 11 Tutorial 6:Lighting_Direct3D 11 教程6:灯光

本教程中使用的光源是定向照明的近似值。 描述光源的矢量确定光的方向。 由于它是近似值,无论物体在哪里,光线照射到它的方向都是相同的。 这种光源的一个例子是太阳。 对于场景中的所有物体,总是看到太阳朝同一方向发光。 另外,不考虑单个物体上的光强度。

其他类型的光包括从中心辐射均匀光的点光源和在所有物体上方向但不均匀的聚光灯。

初始化灯光

在本教程中,将有两个光源。 一个将静态地放置在立方体的上方和后方,另一个将围绕中心立方体进行轨道运行。 请注意,上一个教程中的轨道立方体已替换为此光源。

由于光照是由着色器计算的,因此必须声明变量,然后将其绑定到技术中的变量。 在此示例中,我们只需要光源的方向以及颜色值。 第一盏灯是灰色而不移动,而第二盏是轨道红灯。

// Setup our lighting parameters
XMFLOAT4 vLightDirs[2] =
{
XMFLOAT4( -0.577f, 0.577f, -0.577f, 1.0f ),
XMFLOAT4( 0.0f, 0.0f, -1.0f, 1.0f ),
};
XMFLOAT4 vLightColors[2] =
{
XMFLOAT4( 0.5f, 0.5f, 0.5f, 1.0f ),
XMFLOAT4( 0.5f, 0.0f, 0.0f, 1.0f )
};

  

在上一个教程中,轨道光的旋转方式与立方体一样。 应用的旋转矩阵将改变光的方向,以显示它始终朝向中心发光的效果。 注意,函数XMVector3Transform用于将矩阵与向量相乘。 在上一个教程中,我们将转换矩阵乘以世界矩阵,然后传递到着色器进行转换。 但是,为简单起见,在这种情况下,我们实际上正在对CPU中的光进行世界变换。

// Rotate the second light around the origin
XMMATRIX mRotate = XMMatrixRotationY( -2.0f * t );
XMVECTOR vLightDir = XMLoadFloat4( &vLightDirs[1] );
vLightDir = XMVector3Transform( vLightDir, mRotate );
XMStoreFloat4( &vLightDirs[1], vLightDir );

  

灯光的方向和颜色都像矩阵一样传递到着色器。 调用关联变量进行设置,并传入参数。

    //
// Update matrix variables and lighting variables
//
ConstantBuffer cb1;
cb1.mWorld = XMMatrixTranspose( g_World );
cb1.mView = XMMatrixTranspose( g_View );
cb1.mProjection = XMMatrixTranspose( g_Projection );
cb1.vLightDir[0] = vLightDirs[0];
cb1.vLightDir[1] = vLightDirs[1];
cb1.vLightColor[0] = vLightColors[0];
cb1.vLightColor[1] = vLightColors[1];
cb1.vOutputColor = XMFLOAT4(0, 0, 0, 0);
g_pImmediateContext->UpdateSubresource( g_pConstantBuffer, 0, NULL, &cb1, 0, 0 );

  

渲染像素着色器中的灯光

一旦我们设置了所有数据并且着色器正确地提供了数据,我们就可以计算来自光源的每个像素的朗伯照明术语。 我们将使用之前讨论过的点积规则。

一旦我们将光线与正常光线的点积相乘,就可以将其与光线的颜色相乘,以计算光线的效果。 该值通过饱和函数传递,该函数将范围转换为[0,1]。 最后,将两个单独的灯的结果相加在一起以创建最终的像素颜色。

考虑到表面本身的材料没有考虑到这个光计算中。 表面的最终颜色是灯光颜色的结果。

    //
// Pixel Shader
//
float4 PS( PS_INPUT input) : SV_Target
{
float4 finalColor = 0; //do NdotL lighting for 2 lights
for(int i=0; i<2; i++)
{
finalColor += saturate( dot( (float3)vLightDir[i],input.Norm) * vLightColor[i] );
}
return finalColor;
}

  

一旦通过像素着色器,像素将被光调制,您可以看到每个光在立方体表面上的效果。 请注意,在这种情况下,光看起来很平,因为同一表面上的像素将具有相同的法线。 漫反射是一种非常简单易用的计算照明模型。 您可以使用更复杂的照明模型来获得更丰富,更真实的材料。

最终效果

Direct3D 11 Tutorial 6:Lighting_Direct3D 11 教程6:灯光