Reputation: 111
Our software currently uses the fixed function pipeline in Direct3D9 to offer our users an easily scriptable way of throwing lights and objects into a simple scene. We allow directional, point and spot lights. I'm trying to move us over to Direct3D11, but I want it to be as close to the Direct3D9 fixed function pipeline as possible as a baseline. We can add stuff like per-pixel lighting later. I'm brand new to shader coding and even though I've been doing C++ for years, I feel out of my element. I'm wondering if there is a DX11 supported shader out there which perfectly emulates the lighting offered by DX9 fixed function pipeline. I have seen the old point-light emulation example in FixedFuncEMUFX11, and I'll be trying to use that as a base, but I just know I'm not doing this as efficiently as it could be, and the math for proper spot light emulation is beyond me. Is there any open source out there which emulates the fixed function pipeline, or would contain directional, point, and spot light implementations for DirectX11/10? I think all I'd need are the .fx or .hlsl files. Even if it tops out at 8 light sources, that'd be fine.
I'm currently using DirectXTK as a guide to proper DirectX11 programming, but if there is a better standard to code to, I'd love to see an example of industry standard DirectX11 rendering engine programming methodology. Thank you for any advice you can give.
Upvotes: 2
Views: 2209
Reputation: 41127
For basic rendering with Direct3D 11, the DirectX Tool Kit built-in shaders are based on the XNA Game Studio 4 which provides a good set of basic features including directional lighting and per-pixel lighting. They are designed to work with all Direct3D Feature Level hardware, so they don't implement things like spotlights which are easily done with more modern hardware.
The FixedFuncEMU11 sample is a Direct3D 11 port of the legacy DirectX SDK's FixedFuncEMU Direct3D 10 sample. The shaders are useful for understanding the various Direct3D 9 specific fixed-function pipeline, but doesn't cover 'standard' stuff like implementing standard lighting models in HLSL. Also note that this sample uses the Effects system for Direct3D 11 which has it's own issues. Still, it's useful for seeing:
You might want to try some of the old Direct3D 9 era introductions to HLSL shader programming. While not 100% compatible with Direct3D 11, they are pretty close and HLSL itself is basically the same. I found this article for example.
There are also a number of excellent Direct3D 11 books all of which cover HLSL shaders since there's no fixed-function pipeline in Direct3D 11. See Book Recommendations for some details and notes as some of those books were written before the DirectX SDK was retired. In particular, you should look at Real-Time 3D Rendering with DirectX and HLSL by Varcholik as it's heavily focused on HLSL authoring.
Upvotes: 4
Reputation: 111
For anyone who wound up here by searching for a Fixed Function Pipeline emulation in hlsl, this is pretty much exactly what I was looking for: http://www.3dgep.com/texturing-lighting-directx-11/ The shader in there is not very optimized, but is written for clarity and a good introduction for beginners to hlsl. There is very little extra cruft to sift through in order to see exactly what is going on and the bare minimum to get your scene running. The spotlight isn't an exact replication of DX9's FFP spotlight, but it is easily modified to become such.
#define MAX_LIGHTS 8
// Light types.
#define DIRECTIONAL_LIGHT 0
#define POINT_LIGHT 1
#define SPOT_LIGHT 2
Texture2D Texture : register(t0);
sampler Sampler : register(s0);
struct _Material
{
float4 Emissive; // 16 bytes
//----------------------------------- (16 byte boundary)
float4 Ambient; // 16 bytes
//------------------------------------(16 byte boundary)
float4 Diffuse; // 16 bytes
//----------------------------------- (16 byte boundary)
float4 Specular; // 16 bytes
//----------------------------------- (16 byte boundary)
float SpecularPower; // 4 bytes
bool UseTexture; // 4 bytes
float2 Padding; // 8 bytes
//----------------------------------- (16 byte boundary)
}; // Total: // 80 bytes ( 5 * 16 )
cbuffer MaterialProperties : register(b0)
{
_Material Material;
};
struct Light
{
float4 Position; // 16 bytes
//----------------------------------- (16 byte boundary)
float4 Direction; // 16 bytes
//----------------------------------- (16 byte boundary)
float4 Color; // 16 bytes
//----------------------------------- (16 byte boundary)
float SpotAngle; // 4 bytes
float ConstantAttenuation; // 4 bytes
float LinearAttenuation; // 4 bytes
float QuadraticAttenuation; // 4 bytes
//----------------------------------- (16 byte boundary)
int LightType; // 4 bytes
bool Enabled; // 4 bytes
int2 Padding; // 8 bytes
//----------------------------------- (16 byte boundary)
}; // Total: // 80 bytes (5 * 16 byte boundary)
cbuffer LightProperties : register(b1)
{
float4 EyePosition; // 16 bytes
//----------------------------------- (16 byte boundary)
float4 GlobalAmbient; // 16 bytes
//----------------------------------- (16 byte boundary)
Light Lights[MAX_LIGHTS]; // 80 * 8 = 640 bytes
}; // Total: // 672 bytes (42 * 16 byte boundary)
float4 DoDiffuse( Light light, float3 L, float3 N )
{
float NdotL = max( 0, dot( N, L ) );
return light.Color * NdotL;
}
float4 DoSpecular( Light light, float3 V, float3 L, float3 N )
{
// Phong lighting.
float3 R = normalize( reflect( -L, N ) );
float RdotV = max( 0, dot( R, V ) );
// Blinn-Phong lighting
float3 H = normalize( L + V );
float NdotH = max( 0, dot( N, H ) );
return light.Color * pow( RdotV, Material.SpecularPower );
}
float DoAttenuation( Light light, float d )
{
return 1.0f / ( light.ConstantAttenuation + light.LinearAttenuation * d + light.QuadraticAttenuation * d * d );
}
struct LightingResult
{
float4 Diffuse;
float4 Specular;
};
LightingResult DoPointLight( Light light, float3 V, float4 P, float3 N )
{
LightingResult result;
float3 L = ( light.Position - P ).xyz;
float distance = length(L);
L = L / distance;
float attenuation = DoAttenuation( light, distance );
result.Diffuse = DoDiffuse( light, L, N ) * attenuation;
result.Specular = DoSpecular( light, V, L, N ) * attenuation;
return result;
}
LightingResult DoDirectionalLight( Light light, float3 V, float4 P, float3 N )
{
LightingResult result;
float3 L = -light.Direction.xyz;
result.Diffuse = DoDiffuse( light, L, N );
result.Specular = DoSpecular( light, V, L, N );
return result;
}
float DoSpotCone( Light light, float3 L )
{
float spotMinAngle = cos( light.SpotAngle );
float spotMaxAngle = ( spotMinAngle + 1.0f ) / 2.0f;
float cosAngle = dot( light.Direction.xyz, L );
return smoothstep( spotMinAngle, spotMaxAngle, cosAngle );
}
LightingResult DoSpotLight( Light light, float3 V, float4 P, float3 N )
{
LightingResult result;
float3 L = ( light.Position - P ).xyz;
float distance = length(L);
L = L / distance;
float attenuation = DoAttenuation( light, distance );
float spotIntensity = DoSpotCone( light, -L );
result.Diffuse = DoDiffuse( light, L, N ) * attenuation * spotIntensity;
result.Specular = DoSpecular( light, V, L, N ) * attenuation * spotIntensity;
return result;
}
LightingResult ComputeLighting( float4 P, float3 N )
{
float3 V = normalize( EyePosition - P ).xyz;
LightingResult totalResult = { {0, 0, 0, 0}, {0, 0, 0, 0} };
[unroll]
for( int i = 0; i < MAX_LIGHTS; ++i )
{
LightingResult result = { {0, 0, 0, 0}, {0, 0, 0, 0} };
if ( !Lights[i].Enabled ) continue;
switch( Lights[i].LightType )
{
case DIRECTIONAL_LIGHT:
{
result = DoDirectionalLight( Lights[i], V, P, N );
}
break;
case POINT_LIGHT:
{
result = DoPointLight( Lights[i], V, P, N );
}
break;
case SPOT_LIGHT:
{
result = DoSpotLight( Lights[i], V, P, N );
}
break;
}
totalResult.Diffuse += result.Diffuse;
totalResult.Specular += result.Specular;
}
totalResult.Diffuse = saturate(totalResult.Diffuse);
totalResult.Specular = saturate(totalResult.Specular);
return totalResult;
}
struct PixelShaderInput
{
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float2 TexCoord : TEXCOORD0;
};
float4 TexturedLitPixelShader( PixelShaderInput IN ) : SV_TARGET
{
LightingResult lit = ComputeLighting( IN.PositionWS, normalize(IN.NormalWS) );
float4 emissive = Material.Emissive;
float4 ambient = Material.Ambient * GlobalAmbient;
float4 diffuse = Material.Diffuse * lit.Diffuse;
float4 specular = Material.Specular * lit.Specular;
float4 texColor = { 1, 1, 1, 1 };
if ( Material.UseTexture )
{
texColor = Texture.Sample( Sampler, IN.TexCoord );
}
float4 finalColor = ( emissive + ambient + diffuse + specular ) * texColor;
return finalColor;
}
Upvotes: 2