0
\$\begingroup\$

At the moment I am implementing Specular Lighting for my engine in DirectX on top of Diffuse Lighting. I am using a physically based technique and as you can see from the image below something is clearly wrong. Can somebody please help me?

enter image description here

One thing I think that may be causing the issue is a buffer packing error (I only started learning to do this yesterday) so some values may not be sent across to HLSL properly. Here's my cbuffer struct + the light struct.

struct cbPerFrame
{
    Light  light;
    XMVECTOR cameraPosition;
    float pad;
    float pad2;
    float pad3;
    float Roughness;
    float Metalness;
};

struct Light
{
    Light()
    {
        ZeroMemory(this, sizeof(Light));
    }
    XMFLOAT3 dir;
    float pad;
    XMFLOAT4 ambient;
    XMFLOAT4 diffuse;
};

Or is it how I'm sending my Camera Position over to HLSL?

constbuffPerFrame.light = light;
constbuffPerFrame.Roughness = 10.0f;
constbuffPerFrame.Metalness = 10.0f;
constbuffPerFrame.cameraPosition = camPosition; //camPosition is a XMVector

If that's fine then there must be something dead stupid & simple I've done in my shader:

Effects.fx

#include "common.hlsli"

struct Light
{
    float3 dir;
    float4 ambient;
    float4 diffuse;
};

cbuffer cbPerFrame
{
    Light light;
    float3 cameraPosition;
    float Roughness;
    float Metalness;
};

cbuffer cbPerObject
{
    float4x4 WVP;
    float4x4 World;
};

Texture2D ObjTexture;
SamplerState ObjSamplerState
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 16;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float2 TexCoord : TEXCOORD;
    float3 normal : NORMAL;
};

VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL)
{
    VS_OUTPUT output;

    output.Pos = mul(inPos, WVP);

    output.normal = mul(normal, World);

    output.TexCoord = inTexCoord;

    return output;
}

float4 PS(VS_OUTPUT input) : SV_TARGET
{
    input.normal = normalize(input.normal);

    float4 textureColor = ObjTexture.Sample(ObjSamplerState, input.TexCoord);
    float3 specularAlbedo = float3(Metalness, Metalness, Metalness);

    float3 lighting = 0.0f;

    float3 lightPos = float3(100.0f, 100.0f, 0.0f);
    float3 pixelToLight = lightPos - input.Pos;
    float  lightDist = length(pixelToLight);
    float3 lightDir = pixelToLight / lightDist;
    float nDotL = saturate(dot(input.normal, light.dir));

    float3 diffuseLighting = textureColor * light.ambient * light.diffuse;
    diffuseLighting += saturate(DirectDiffuseBRDF(textureColor, nDotL));

    float3 specularLighting = textureColor * light.ambient * light.diffuse;
    specularLighting += DirectSpecularBRDF(specularAlbedo, input.Pos, input.normal, lightDir, cameraPosition, Roughness);

    lighting = diffuseLighting + specularLighting;

    return float4(lighting, textureColor.a);
}

common.hlsli

//=================================================================================================
// Constant Variables
//=================================================================================================
static const float Pi = 3.141592654f;
static const float Pi2 = 6.283185307f;
static const float Pi_2 = 1.570796327f;
static const float Pi_4 = 0.7853981635f;
static const float InvPi = 0.318309886f;
static const float InvPi2 = 0.159154943f;

//=================================================================================================
// Sampler States
//=================================================================================================
SamplerState SamplerLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState SamplerAnisotropic
{
    Filter = ANISOTROPIC;
    MaxAnisotropy = 16;
    AddressU = Wrap;
    AddressV = Wrap;
};

// ===============================================================================================
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// ===============================================================================================
float2 Hammersley(uint i, uint N)
{
    float ri = reversebits(i) * 2.3283064365386963e-10f;
    return float2(float(i) / float(N), ri);
}

// ===============================================================================================
// http://graphicrants.blogspot.com.au/2013/08/specular-brdf-reference.html
// ===============================================================================================
float GGX(float NdotV, float a)
{
    float k = a / 2;
    return NdotV / (NdotV * (1.0f - k) + k);
}

// ===============================================================================================
// Geometry Term
// -----------------------------------------------------------------------------------------------
// Defines the shadowing from the microfacets.
//
// Smith approximation:
// http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
// http://graphicrants.blogspot.fr/2013/08/specular-brdf-reference.html
//
// ===============================================================================================
float G_Smith(float a, float nDotV, float nDotL)
{
    return GGX(nDotL, a * a) * GGX(nDotV, a * a);
}

// ================================================================================================
// Fresnel
// ------------------------------------------------------------------------------------------------
// The Fresnel function describes the amount of light that reflects from a mirror surface 
// given its index of refraction. 
//
// Schlick's approximation:
// http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
// http://graphicrants.blogspot.fr/2013/08/specular-brdf-reference.html
//
// ================================================================================================
float3 Schlick_Fresnel(float3 f0, float3 h, float3 l)
{
    return f0 + (1.0f - f0) * pow((1.0f - dot(l, h)), 5.0f);
}

// ===============================================================================================
// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf 
// ===============================================================================================
float3 ImportanceSampleGGX(float2 Xi, float Roughness, float3 N)
{
    float a = Roughness * Roughness; // DISNEY'S ROUGHNESS [see Burley'12 siggraph]

    float Phi = 2 * Pi * Xi.x;
    float CosTheta = sqrt((1 - Xi.y) / (1 + (a * a - 1) * Xi.y));
    float SinTheta = sqrt(1 - CosTheta * CosTheta);

    float3 H;
    H.x = SinTheta * cos(Phi);
    H.y = SinTheta * sin(Phi);
    H.z = CosTheta;

    float3 UpVector = abs(N.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
    float3 TangentX = normalize(cross(UpVector, N));
    float3 TangentY = cross(N, TangentX);

    // Tangent to world space
    return TangentX * H.x + TangentY * H.y + N * H.z;
}

//The Direct Diffuse BRDF
float3 DirectDiffuseBRDF(float3 diffuseAlbedo, float nDotL)
{
    return (diffuseAlbedo * nDotL);
}

//The Direct Specular BRDF
float3 DirectSpecularBRDF(float3 specularAlbedo, float3 positionWS, float3 normalWS, float3 lightDir, float3 CameraPos, float Roughness)
{
    float3 viewDir = normalize(CameraPos - positionWS);
    float3 halfVec = normalize(viewDir + lightDir);

    float nDotH = saturate(dot(normalWS, halfVec));
    float nDotL = saturate(dot(normalWS, lightDir));
    float nDotV = max(dot(normalWS, viewDir), 0.0001f);

    float alpha2 = Roughness * Roughness;

    // Computes the distribution of the microfacets for the shaded surface.
    // Trowbridge-Reitz/GGX normal distribution function.
    float  D = alpha2 / (Pi * pow(nDotH * nDotH * (alpha2 - 1) + 1, 2.0f));

    // Computes the amount of light that reflects from a mirror surface given its index of refraction. 
    // Schlick's approximation.
    float3 F = Schlick_Fresnel(specularAlbedo, halfVec, lightDir);

    // Computes the shadowing from the microfacets.
    // Smith's approximation.
    float  G = G_Smith(Roughness, nDotV, nDotL);

    return D * F * G;
}

EDIT :

cbuffer cbPerFrame
{
    Light light;
    float pad0;
    float pad1;
    float pad2;
    float Roughness;
    float Metalness;
    float4 cameraPosition;
};

Have I packed my cbuffer structure correctly now? If not can you guys tell me what to do to fix it (I'm clueless). If it is packed correctly what is the problem then?!

\$\endgroup\$
  • \$\begingroup\$ You haven't included the C++ definition of Light. Also, note that using XMVECTOR in a data structure means it must have 16-byte alignment. See MSDN \$\endgroup\$ – Chuck Walbourn Oct 11 '16 at 17:00
  • \$\begingroup\$ Have you read HLSL Packing Rules? It's quite likely that your float3 data is getting padded to float4 (i.e.) as defined, your Light struct element dir is actually 16 bytes long with 4 bytes of internal padding--it can't put your float3 and float4 together so the float4 starts a new 16-byte vector. \$\endgroup\$ – Chuck Walbourn Oct 11 '16 at 17:04
  • \$\begingroup\$ @ChuckWalbourn I've updated my post to include the Light structure. I've added padding within the Light structure in the Header file already. I've made an update to my buffer struct, could you tell me if it's 16 byte aligned? \$\endgroup\$ – Arjan Singh Oct 11 '16 at 17:25
1
\$\begingroup\$
struct Light
{
    XMFLOAT3 dir;
    float pad;
    XMFLOAT4 ambient;
    XMFLOAT4 diffuse;
};

struct cbPerFrame
{
    Light  light;
    XMVECTOR cameraPosition;
    float Roughness;
    float Metalness;
};

Should be a match for HLSL:

struct Light
{
    float3 dir;
    float4 ambient;
    float4 diffuse;
};

cbuffer cbPerFrame
{
    Light light;
    float4 cameraPosition;
    float Roughness;
    float Metalness;
};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.