Advertisement

Issue with HLSL function parameter

Started by September 06, 2023 02:12 PM
2 comments, last by wizbear9 1 year, 1 month ago

I implemented directional, point and spot light. But HLSL shader does not work as I expected. There is a problem in passing light color parameter to a function. 16-byte alignment of constant buffer is correct. there is nothing wrong. All members of constant buffer are set well. I can see something wrong in registers(r0, r1, …) once it passes light color parameter to ComputeLambert(). it seems lightColor must be r#.xyz correct. but its value is stored in r0.w. I post my shader code also. Does anyone have an idea? Even I tried to find the document of hlsl function parameter specification. I could not find it.

struct VSOutput
{
    float4 Position          : SV_POSITION;
    float3 Normal            : NORMAL;
    float2 TexCoord          : TEXCOORD;
    float4 WorldPos          : WORLD_POS;
    float3x3 InvTangentBasis : INV_TANGENT_BASIS;
};

struct PSOutput
{
    float4 Color : SV_TARGET0;
};

cbuffer LightBuffer : register(b1)
{
    float3 CameraPosition    : packoffset(c0);
    
    float3 DirLightDirection : packoffset(c1);
    float3 DirLightColor     : packoffset(c2);
    
    float3 PtLightPosition   : packoffset(c3);
    float3 PtLightColor      : packoffset(c4);
    float PtLightRange       : packoffset(c4.w);
    
    float3 SptLightPosition  : packoffset(c5);
    float3 SptLightColor     : packoffset(c6);
    float SptLightRange      : packoffset(c6.w);
    float3 SptLightDirection : packoffset(c7);
    float SptAngle           : packoffset(c7.w);
}

cbuffer MaterialBuffer : register(b2)
{
    float3 Diffuse  : packoffset(c0);
    float  Alpha    : packoffset(c0.w);
    float3 Specular : packoffset(c1);
    float Shininess : packoffset(c1.w);
}

cbuffer ConfigureBuffer : register(b3)
{
    int DiffuseMapUsable   : packoffset(c0.x);
    int SpecularMapUsable  : packoffset(c0.y);
    int ShininessMapUsable : packoffset(c0.z);
    int NormalMapUsable    : packoffset(c0.w);
}

SamplerState ColorSmp  : register(s0);
Texture2D    ColorMap  : register(t0);
Texture2D    NormalMap : register(t1);

float3 GetLightFromDirLight(VSOutput input, float3 normal);
float3 GetLightFromPointLight(VSOutput input, float3 normal);
float3 GetLightFromSpotLight(VSOutput input, float3 normal);

// diffuse
float3 ComputeLambert(
    float3 lightDir,
    float3 lightColor,
    float3 normal);

// specular
float3 ComputePhong(
    float4 worldPos,
    float3 lightDir,
    float3 lightColor,
    float3 normal);

PSOutput main(VSOutput input)
{
    PSOutput output = (PSOutput) 0;

    // Normal vector
    float3 N;
    
    if(NormalMapUsable)
    {
        N = NormalMap.Sample(ColorSmp, input.TexCoord).xyz * 2.0f - 1.0f;
        N = mul(input.InvTangentBasis, N);
    }
    else
    {
        N = input.Normal;
    }

    float3 dirLight = GetLightFromDirLight(input, N);
    float3 ptLight  = GetLightFromPointLight(input, N);
    float3 sptLight = GetLightFromSpotLight(input, N);
    
    float4 color;
    
    if(DiffuseMapUsable)
    {
        color = ColorMap.Sample(ColorSmp, input.TexCoord);
    }
    else
    {
        color = float4(1.0f, 0.0f, 0.0f, 1.0f);
    }
    
    float light = dirLight + ptLight + sptLight;
    output.Color = float4(color.rgb * light, color.a * Alpha);
    return output;
}

float3 GetLightFromDirLight(VSOutput input, float3 normal)
{
    float3 diffuse = ComputeLambert(-DirLightDirection, DirLightColor, normal);
    float3 specular = ComputePhong(input.WorldPos, -DirLightDirection, DirLightColor, normal);
    
    return diffuse + specular;
}

float3 GetLightFromPointLight(VSOutput input, float3 normal)
{
    float3 L = normalize(input.WorldPos.xyz - PtLightPosition);
    
    float3 diffuse = ComputeLambert(L, PtLightColor, normal);
    float3 specular = ComputePhong(input.WorldPos, L, PtLightColor, normal);
    
    float dist = length(input.WorldPos.xyz - PtLightPosition);
    float affect = saturate(1.0f - 1.0f / PtLightRange * dist);
    affect = pow(affect, 3.0f);
    
    diffuse *= affect;
    specular *= affect;
    
    return diffuse + specular;
}

float3 GetLightFromSpotLight(VSOutput input, float3 normal)
{
    float3 L = normalize(input.WorldPos.xyz - SptLightPosition);
    
    float3 diffuse = ComputeLambert(L, SptLightColor, normal);
    float3 specular = ComputePhong(input.WorldPos, L, SptLightColor, normal);
    
    float dist = length(input.WorldPos.wyz - SptLightPosition);
    float affect = saturate(1.0f - 1.0f / SptLightRange * dist);
    affect = pow(affect, 3.0f);
    
    diffuse *= affect;
    specular *= affect;
    
    float angle = dot(L, SptLightDirection);
    angle = abs(acos(angle));
    affect = saturate(1.0f - 1.0f / SptAngle * angle);
    
    affect = pow(affect, 0.5f);
    
    diffuse *= affect;
    specular *= affect;
    
    return diffuse + specular;
}

float3 ComputeLambert
(
    float3 lightDir,
    float3 lightColor,
    float3 normal
)
{
    return lightColor * Diffuse * saturate(dot(lightDir, normal) * -1);
}

float3 ComputePhong
(
    float4 worldPos,
    float3 lightDir,
    float3 lightColor,
    float3 normal
)
{
    float3 V = normalize(CameraPosition - worldPos.xyz);
    float3 R = normalize(reflect(lightDir, normal));
    return lightColor * Specular * pow(saturate(dot(V, R)), Shininess);
}

wizbear9 said:
I implemented directional, point and spot light. But HLSL shader does not work as I expected. There is a problem in passing light color parameter to a function. 16-byte alignment of constant buffer is correct. there is nothing wrong. All members of constant buffer are set well. I can see something wrong in registers(r0, r1, …) once it passes light color parameter to ComputeLambert(). it seems lightColor must be r#.xyz correct. but its value is stored in r0.w. I post my shader code also. Does anyone have an idea? Even I tried to find the document of hlsl function parameter specification. I could not find it.

So what I think is happening (not 100% sure as tracing the whole code is a bit complex):

float light = dirLight + ptLight + sptLight;

Up until this point, those 3 variables are “float3”. At that line, you implicitely cast them to a float, which will remove all components but “x”. As HLSL does not really execute functions (it always inlines everything and has no “call” instruction), it probably retroactively performs the optimization of dropping “yz” back to the function you are inspecting, which means that while it is declared to work on “float3”, it only does its calculation on “float1”, which mean it will show up like that in your debugger.

Again, only speculation, but I'm pretty sure that's the problem (and I don't think the cast to float is by design).

Advertisement

@Juliean Wow. I could not notice the fact that I defined light as float. lol. I remember that I declared light as float3 definitely but it was not. It works well now. Thanks for your help, you saved my days !

This topic is closed to new replies.

Advertisement