1. 程式人生 > >Direct3D 11 Tutorial 6:Lighting_Direct3D 11 教程6:燈光

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

概述

在之前的教程中,世界看起來很無聊,因為所有物件都以相同的方式點亮。 本教程將介紹簡單照明的概念及其應用方法。 使用的技術將是朗伯照明。

本教程的結果將修改前面的示例以包含光源。 該光源將附在軌道上的立方體上。 可以在中心立方體上看到光的影響。

 

資源目錄

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

Github

 

燈光

在本教程中,將介紹最基本的照明型別:朗伯照明。 無論距離光線的距離如何,朗伯照明都具有均勻的強度。 當光照射到表面時,通過光在表面上的入射角計算反射的光量。 當光直接照射在表面上時,它顯示出以最大強度反射所有光。 然而,隨著光的角度增加,光的強度將逐漸消失。

為了計算光在表面上的強度,必須計算光方向與表面法線之間的角度。 表面的法線定義為垂直於表面的向量。 角度的計算可以通過簡單的點積來完成,該點積將光方向向量的投影返回到法線上。 角度越寬,投影越小。 因此,這為我們提供了調製漫射光的正確功能。

本教程中使用的光源是定向照明的近似值。 描述光源的向量確定光的方向。 由於它是近似值,無論物體在哪裡,光線照射到它的方向都是相同的。 這種光源的一個例子是太陽。 對於場景中的所有物體,總是看到太陽朝同一方向發光。 另外,不考慮單個物體上的光強度。

其他型別的光包括從中心輻射均勻光的點光源和在所有物體上方向但不均勻的聚光燈。

 

初始化燈光

在本教程中,將有兩個光源。 一個將靜態地放置在立方體的上方和後方,另一個將圍繞中心立方體進行軌道執行。 請注意,上一個教程中的軌道立方體已替換為此光源。

由於光照是由著色器計算的,因此必須宣告變數,然後將其繫結到技術中的變數。 在此示例中,我們只需要光源的方向以及顏色值。 第一盞燈是灰色而不移動,而第二盞是軌道紅燈。

// 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;
    }

  

 一旦通過畫素著色器,畫素將被光調製,您可以看到每個光在立方體表面上的效果。 請注意,在這種情況下,光看起來很平,因為同一表面上的畫素將具有相同的法線。 漫反射是一種非常簡單易用的計算照明模型。 您可以使用更復雜的照明模型來獲得更豐富,更真實的材料。

 

最終效果