点光源错误DirectX 11

3

我刚学习Directx 11,编写了一个距离相关点光源着色器,对于旋转和平移的物体效果很好,但是当我尝试缩放模型时,如果缩放的更大,光照就变得更暗,如果缩放的更小,光照就变得更亮。我以为这可能是法线的问题,但我确保将它们乘以世界矩阵的逆转置矩阵,并在像素着色器中插值后对其进行了归一化处理。以下是着色器代码:

Texture2D txDiffuse : register( t0 );
SamplerState samAnisotropic
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 4;
};

cbuffer ConstantBuffer : register( b0 )
{
    matrix World;
    matrix View;
    matrix Projection;
    matrix WorldInvTrans;
    float3 LightPos;
    float pad1;
    float3 EyePos;
    float pad2;
    float3 At;
    float pad3;
    float showNorms;
}


struct VS_INPUT
{
    float4 Pos : POSITION;
    float3 Norm : NORMAL;
    float2 TexCoor : TEXCOORD0; 
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : NORMAL;
    float3 LightDir : POSITION0;
    float3 EyeVector : POSITION1;
    float2 TexCoor : TEXCOORD0;
    float distance : FLOAT0;
    float showNorms : FLOAT1;

};

PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.LightDir = normalize( LightPos - output.Pos );
    output.EyeVector = normalize( EyePos - At );
    output.distance = distance( LightPos, output.Pos);
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Norm = mul( input.Norm, WorldInvTrans );
    output.TexCoor = input.TexCoor;
    output.showNorms = showNorms;

    return output;
}

float4 PS( PS_INPUT input) : SV_Target
{
    input.Norm = normalize( input.Norm );

    float specTerm = 0;

    float3 ReflVector = normalize( reflect( input.LightDir, input.Norm ) );

    [flatten]
    if ( dot( ReflVector, input.EyeVector ) >= 0 )
    {
        specTerm = pow(  dot( ReflVector, input.EyeVector ) , 50 );
    }
    float diffuseTerm = saturate( dot( input.LightDir, input.Norm ) );
    float4 ambient = float4( 0.25f, 0.25f, 0.25f, 1.0f );
    float4 lightColor = float4( 1.0f, 1.0f, 1.0f, 1.0f );
    return ( (ambient + (diffuseTerm + specTerm) / (pow( input.distance, 1 ) * 0.025f)) * lightColor * txDiffuse.Sample( samAnisotropic, input.TexCoor ) ) * ( 1 - input.showNorms ) + float4( input.Norm, 1.0f ) * input.showNorms;
}

我仍然怀疑法线向量不正确,所以我编辑了像素着色器中的最后一行,如果showNorms = 1.0f,则根据法线向量来着色模型。法线看起来已经被正确地转换了。仍然持怀疑态度,我将我的模型替换为XZ轴上的平面,并将其放大50倍。当我进行渲染时,灯光仍然暗淡,但当��将showNorms设置为1.0f时,平面变成了绿色,这意味着所有法线都指向上方的Y方向。如果我正确地变换并规范化我的法线,是什么导致了这些灯光错误吗?
如果有帮助的话,这是我为平面设置常量缓冲区的代码:
//Render Plane
    mWorld = XMMatrixIdentity();
    cb1.mWorld = XMMatrixTranspose( XMMatrixMultiply( XMMatrixMultiply( mWorld, XMMatrixScaling( 50.0f, 1.0f, 50.0f ) ), XMMatrixTranslation( 0.0f, -5.0f, 0.0f ) ) );
    XMMATRIX A = cb1.mWorld;
    A.r[3] = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);

det = XMMatrixDeterminant(A);
cb1.mWorldInvTrans = XMMatrixInverse(&det, A);

g_pImmediateContext->UpdateSubresource( g_pcBufferShader1, 0, NULL, &cb1, 0, 0 );

编辑:我稍微更改了代码以修复specTerm:

Texture2D txDiffuse : register( t0 );
SamplerState samAnisotropic
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 4;
};

cbuffer ConstantBuffer : register( b0 )
{
    matrix World;
    matrix View;
    matrix Projection;
    matrix WorldInvTrans;
    float3 LightPos;
    float pad1;
    float3 EyePos;
    float pad2;
    float3 At;
    float pad3;
    float showNorms;
}


struct VS_INPUT
{
    float4 Pos : POSITION;
    float3 Norm : NORMAL;
    float2 TexCoor : TEXCOORD0; 
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float3 Norm : NORMAL;
    float3 LightDir : POSITION0;
    float3 EyeVector : POSITION1;
    float2 TexCoor : TEXCOORD0;
    float distance : FLOAT0;
    float showNorms : FLOAT1;

};

PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos = mul( input.Pos, World );
    output.LightDir = LightPos - output.Pos;
    output.EyeVector = EyePos - At;
    output.distance = distance( LightPos, output.Pos );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Norm = mul( input.Norm, WorldInvTrans );
    output.TexCoor = input.TexCoor;
    output.showNorms = showNorms;

    return output;
}

float4 PS( PS_INPUT input) : SV_Target
{
    input.Norm = normalize( input.Norm );
    input.LightDir = normalize( input.LightDir );
    input.EyeVector = normalize( input.EyeVector );

    float specTerm = 0;

    float3 ReflVector = normalize( reflect( -input.LightDir, input.Norm ) );

    [flatten]
    if ( dot( ReflVector, input.EyeVector ) >= 0 )
    {
        specTerm = pow(  dot( ReflVector, input.EyeVector ) , 50 );
    }
    float diffuseTerm = saturate( dot( input.LightDir, input.Norm ) );
    float4 ambient = float4( 0.25f, 0.25f, 0.25f, 1.0f );
    float4 lightColor = float4( 1.0f, 1.0f, 1.0f, 1.0f );
    return ( (ambient + (diffuseTerm + specTerm) / (pow( input.distance, 1 ) * 0.025f)) * lightColor * txDiffuse.Sample( samAnisotropic, input.TexCoor ) ) * ( 1 - input.showNorms ) + float4( input.Norm, 1.0f ) * input.showNorms;
}
1个回答

1

我认为您应该在像素着色器中尝试对LightDir向量进行归一化处理。如果平面非常大,那么在这两个向量插值之后,在像素着色器中得到的向量可能不是已经归一化的。随着比例的增加,这种错误很可能会增加。请尝试一下。下面的图片显示了这个问题。

Light vector interpolation problem


我尝试了这个方法,确实让飞机亮了一点,这让我想到这可能是问题的一部分。然而,当我缩放飞机时,它仍然没有像原来那样亮起来。虽然我注意到如果我将相机移动到飞机的角落,角落处非常明亮并且像应该亮起来的那样,但是飞机的其余部分仍然没有亮起来。这让我认为这可能是插值中的其他问题。我读到了一些有关OpenGL插值问题的文章,当在大范围内插值少量顶点时会出现问题。 - Sully Chen
@Sully Chen 好的,现在我开始认为问题可能也与你的距离有关。看看我的例子。在平面的两端计算出的距离将是例如100。因此,在标记为绿色箭头的像素中,它仍将被插值到值100!而它应该是更小的值。只需将顶点的世界位置传递到像素着色器中,使得位置被插值并在像素着色器中计算距离。那应该可以解决问题。 - Elvithari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接