【问题标题】:How do I properly create a spotlight in DirectX11?如何在 DirectX11 中正确创建聚光灯?
【发布时间】:2015-11-17 20:38:01
【问题描述】:

我正在使用 DirectX11 开发一个 3D 项目,目前正在使用 Frank Luna 3D Game Programming with DirectX11 一书和我现有的代码来实现不同的灯光。

目前,我正在开发一个聚光灯,它应该跟随相机的位置并朝同一个方向看,但是,被点亮的位置正在奇怪地移动。当位置发生变化时,光的方向矢量似乎在 (+x, +y, 0) 方向上跟踪。最好用图片来解释。

在这里看起来它们被正确点亮,如果相机保持在原位,聚光灯可以按照您的预期移动,并跟踪相机方向。

在这张图片中,我沿着 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;
}

为了完整起见,顶点和像素着色器的相关部分。

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

我哪里做错了?

【问题讨论】:

  • @NicoSchertler 哎呀,我知道我会忘记一些事情。它就在该部分的底部,就在设置 float4 colour 之前。
  • 您的变量命名似乎存在差异。 output.PosW 看起来像一个位置,但它是相机的方向。您将其作为position 参数传递给calculateSpotlight(),用作位置。必须更改两个部分之一。此外,当您计算spot 时,您缺少light.direction.z 之前的减号。无论如何,那里似乎有太多的缺点(reflect(-lightV, normal)dot(-lightV, ...-light.direction)。
  • 您可能想看看FixedFuncEMUFX11 示例,它具有实现所有标准“固定功能”渲染(包括聚光灯)的 HLSLshader。

标签: c++ directx directx-11 lighting


【解决方案1】:

这里的第三个参数input.PosW 不正确。您必须使用世界空间中的位置。 input.PosW 是一个标准化向量。从光照位置减去归一化向量没有任何意义。

你有

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

你需要(WS中的input.Pos,不是投影空间)

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

【讨论】:

  • 这不是正确的答案,但它让我走上了正确的道路。在将其放入世界视图投影坐标之前,我需要 input.Pos。
  • 可以是任意正交空间,但空间与light.pos相同。例如,它们可以是相机空间。
  • 是的,它应该,但是 input.Pos 在顶点着色器结束之前被放入投影空间,所以我只是在顶点着色器的输出中添加了另一个变量,它给出了在世界空间中的位置.
  • 是的,我第一眼就错过了。我习惯了SM3.0,在像素着色器中不能使用投影位置。
猜你喜欢
  • 2021-11-24
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
  • 2019-10-12
相关资源
最近更新 更多