HLSL着色器:调用函数更改返回值

时间:2012-12-06 23:42:38

标签: struct directx shader hlsl

我一直在研究着色器程序,其中一个最奇怪的事情正在发生。 我有一个名为PositionLightPS的方法,它基本上为位置灯(全向和聚光灯)执行计算,并返回一个包含灯光强度和漫反射颜色的结构。

它曾用于该程序的先前版本,但由于某种原因它停止工作......

让我解释一下原因。但首先,这是PositionPS方法的代码。我毫不怀疑这种方法的计算是正确的,但稍后会更多。

struct LightFragment {
    float4 diffuse;
    float intensity;
};

LightFragment PositionLightPS(PositionLightVOut pin, float3 lightToPixelVec)
{
    LightFragment result;

    pin.normal = normalize(pin.normal);

    float4 diffuse = shaderTexture.Sample( textureSampler, pin.tex0 );
    float3 finalDiffuse = float3(0.0f, 0.0f, 0.0f);

    float d = length(lightToPixelVec);
    if( d > plRange )
    {
        result.diffuse = float4(finalDiffuse, 0);
        result.intensity = 0; 
        return result;
    }

    lightToPixelVec /= d; 

    result.intensity = dot(lightToPixelVec, pin.normal);

    if( result.intensity > 0.0f )
    {   
        finalDiffuse = result.intensity * diffuse.xyz;
        finalDiffuse.xyz *= clColour.xyz;
        finalDiffuse.xyz *= clColour.w;

        finalDiffuse /= plAttenuation[0] + (plAttenuation[1] * d) + (plAttenuation[2] * (d*d));
    }   

    result.diffuse = float4(finalDiffuse, diffuse.a) * diffuseColour;
    return result;
}

这是全向光像素着色器。

float4 OmniLightDiffusePS(PositionLightVOut pin) : SV_TARGET0
{
    float3 lightToPixelVec = plPosition.xyz - pin.worldPos.xyz;
    return = PositionLightPS(pin, lightToPixelVec).diffuse;
}

出于某种原因,虽然已经很长时间了,但这不起作用。但这不是我的问题的焦点。奇怪的是,这两种方法会产生不同的结果。

float4 OmniLightDiffusePS(PositionLightVOut pin) : SV_TARGET0
{
    //return float4(1,1,1,1);
    float3 lightToPixelVec = plPosition.xyz - pin.worldPos.xyz;
    LightFragment fragment = PositionLightPS(pin, lightToPixelVec);
    return float4(1,1,1,1);
}

这个会返回一个黑色像素

float4 OmniLightDiffusePS(PositionLightVOut pin) : SV_TARGET0
{
    return float4(1,1,1,1);
    float3 lightToPixelVec = plPosition.xyz - pin.worldPos.xyz;
    LightFragment fragment = PositionLightPS(pin, lightToPixelVec);
    //return float4(1,1,1,1);
}

这个会返回一个白色像素

所以这意味着调用PositionLightPS方法会对返回的值产生影响......但为什么呢? 为什么会这样?怎么了?这是内存错误吗?

(注意:直接在OmniLightDiffuse方法中复制PositionLightPS方法代码可以解决问题,并且灯光会正常显示。这意味着在PositionLightPS中执行的计算是正确的。问题是, 确实在使用LightFragment结构会以某种方式对着色器程序产生影响 ?)

3 个答案:

答案 0 :(得分:2)

我找到了问题的原因。我的计算机使用CUDA并在我不知情的情况下锁定自己使用集成图形芯片。强制NVidia卡解决了这个问题。

但是,我确实找到了这个非常有用的博客文章,详细说明了本质上我的问题。 当两个特定的编译器标志同时处于活动状态 时,调用用户定义函数会导致编译器内联删除它所处理的像素。 我不知道集成图形芯片是否再现了这个确切的错误,但症状似乎完全对应(即调用一个没有发生返回值的函数会改变返回的内容)。

有关详细信息,请参阅Nico Shertler撰写的博客文章链接:http://nicoschertler.wordpress.com/2012/01/02/hlsl-4-calls-to-user-defined-functions-will-crash/

答案 1 :(得分:1)

通常,这是由于零除错误。 ATI和NVIDIA在处理这种NaN方面可能有所不同。检查输入向量的长度,或添加一些epsilon。

答案 2 :(得分:0)

Bjorkes帖子为我提供了解决问题的线索。 我的代码输入了一个自定义函数,该函数返回白色像素(错误),并且调试器不允许我单步执行代码。 罪魁祸首是以下几行。 float4 p = texture.Sample(...) return(float4)(1 / p.x,1 / p.y,1 / p.z,1);

为0个已解决的问题添加检查。