2015-11-16 76 views
1

我正在研究DirectX11中的3D項目,並且目前正在使用帶有DirectX11書籍的Frank Luna 3D遊戲編程和現有代碼實現不同的燈光。如何在DirectX11中正確創建聚光燈?

目前,我正在開發一個聚光燈,它應該跟隨相機的位置並朝相同的方向看,但是,被點亮的位置奇怪地移動。當位置正在改變時,光的方向矢量似乎在(+ x,+ y,0)方向上跟蹤。最好用圖片解釋。

Boxes lit "properly"

它看這裏像他們適當地被點燃,如果相機保持它在哪裏,聚光燈可以移動爲你所期望的,並且它跟蹤攝像機的方向。

Boxes misbehaving

在此圖像中,我搬到了相機接近箱,沿z軸,光點應該控制一下最近的盒子小,但它不是跟蹤向上。

這是聚光燈結構正在建立要傳遞到所述恆定緩衝器內的代碼,也就是所有的值在結構,除了一個浮子被用作在端部的墊:

cb.spotLight = SpotLight(); 
cb.spotLight.ambient = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f); 
cb.spotLight.specular = XMFLOAT4(0.5, 0.5, 0.5, 10.0); 
cb.spotLight.diffuse = XMFLOAT4(0.5, 0.5, 0.5, 1.0); 
cb.spotLight.attenuation = XMFLOAT3(1, 1, 1); 
cb.spotLight.range = 15; 

XMVECTOR cameraP = XMLoadFloat3(&cameraPos); 
XMVECTOR s = XMVectorReplicate(cb.spotLight.range); 
XMVECTOR l = XMLoadFloat3(&camera.getForwards()); 
XMVECTOR lookat = XMVectorMultiplyAdd(s, l, cameraP); 

XMStoreFloat3(&cb.spotLight.direction, XMVector3Normalize(lookat - XMVectorSet(cameraPos.x, cameraPos.y, cameraPos.z, 1.0f))); 
cb.spotLight.position = cameraPos; 

cb.spotLight.spot = 96; 

這裏是被用來計算着色器中的聚光燈的周圍,漫射和鏡面值功能:

void calculateSpotLight(Material mat, SpotLight light, float3 position, float3 normal, float3 toEye, 
out float4 ambient, out float4 diffuse, out float4 specular) 
{ 
ambient = float4(0, 0, 0, 0); 
specular = float4(0, 0, 0, 0); 
diffuse = float4(0, 0, 0, 0); 

float3 lightV = light.position - position; 

float distance = length(lightV); 

if (distance > light.range) 
{ 
    return; 
} 

lightV /= distance; 

ambient = mat.ambient * light.ambient; 

float diffuseFact = dot(lightV, normal); 

[flatten] 
if (diffuseFact > 0.0f) 
{ 
    float3 vect = reflect(-lightV, normal); 

    float specularFact = pow(max(dot(vect, toEye), 0.0f), mat.specular.w); 

    diffuse = diffuseFact * mat.diffuse * light.diffuse; 
    specular = specularFact * mat.specular * light.specular; 
} 

float spot = pow(max(dot(-lightV, float3(-light.direction.x, -light.direction.y, light.direction.z)), 0.0f), light.spot); 

float attenuation = spot/dot(light.attenuation, float3(1.0f, distance, distance*distance)); 

ambient *= spot; 
diffuse *= attenuation; 
specular *= attenuation; 
} 

而對於completenesses起見,頂點和像素着色器的相關部分。

VS_OUTPUT VS(float4 Pos : POSITION, float3 NormalL : NORMAL, float2 TexC : TEXCOORD) 
{ 
    VS_OUTPUT output = (VS_OUTPUT)0; 
    output.Pos = mul(Pos, World); 

    //Get normalised vector to camera position in world coordinates 
    output.PosW = normalize(eyePos - output.Pos.xyz); 

    output.Pos = mul(output.Pos, View); 
    output.Pos = mul(output.Pos, Projection); 

    //Getting normalised surface normal 
    float3 normalW = mul(float4(NormalL, 0.0f), World).xyz; 
    normalW = normalize(normalW); 
    output.Norm = normalW; 

    output.TexC = TexC; 

    return output; 
} 

float4 PS(VS_OUTPUT input) : SV_Target 
{ 
input.Norm = normalize(input.Norm); 
Material newMat; 
newMat.ambient = material.ambient; 
newMat.diffuse = texCol; 
newMat.specular = specCol; 

float4 ambient = (0.0f, 0.0f, 0.0f, 0.0f); 
float4 specular = (0.0f, 0.0f, 0.0f, 0.0f); 
float4 diffuse = (0.0f, 0.0f, 0.0f, 0.0f); 

float4 amb, spec, diff; 

calculateSpotLight(newMat, spotLight, input.PosW, input.Norm, input.PosW, amb, diff, spec); 

ambient += amb; 
specular += spec; 
diffuse += diff; 
//Other light types 
float4 colour; 
    colour = ambient + specular + diffuse; 
    colour.a = material.diffuse.a; 
    return colour; 
} 

我哪裏出錯了?

+0

@NicoSchertler哎呀,知道我忘了什麼東西(在WS,沒有投影空間input.Pos)。在'float4 colour'被設置之前,就在該部分的底部。 – Yann

+0

您的變量命名似乎存在差異。 'output.PosW'看起來像一個位置,但它是相機的方向。你將'calculateSpotlight()'作爲'position'參數傳遞給它,它被用作位置。這兩個部分之一必須改變。此外,當你計算'spot'時,你在'light.direction.z'之前缺少減號。無論如何,在那裏似乎有太多的缺點('reflect(-lightV,normal)','dot(-lightV,...','-light.direction') –

+0

你可能想看看[ FixedFuncEMUFX11](https://github.com/walbourn/directx-sdk-samples)示例,該示例具有實現所有標準「固定功能」渲染(包括投射燈)的HLSLshaders。 –

回答

2

第三個參數input.PosW在這裏不正確。你必須在世界空間中使用位置。 input.PosW歸一化的載體。從光的位置減去標準化的矢量沒有任何意義。

你有

calculateSpotLight(newMat, spotLight, input.PosW, input.Norm, input.PosW, amb, diff, spec); 

你需要

calculateSpotLight(newMat, spotLight, input.Pos, input.Norm, input.PosW, amb, diff, spec); 
+0

這不是正確的答案,但它讓我走上正確的軌道,在投入世界視圖投影座標之前,我需要input.Pos。 – Yann

+0

它可以是任何正交空間,但是與'light.pos'相同的空間。例如,它們可以是相機空間。 –

+0

是的,它應該,但是input.Pos是在頂點着色器結束之前放入投影空間的,所以我只是在頂點着色器的輸出中添加了另一個變量,它給出了世界空間中的位置。 – Yann