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.
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:
Fixed-function Transformation Pipeline
Fixed-function Lighting Pipeline
AlphaTest
User Clip Planes
Pixel Fog
Gouraud and Flat shade modes
Projected texture lookups (texldp)
Multi-Texturing
D3DFILL_POINT fillmode
Screen space UI rendering
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.
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;
}
I cannot get the following structure to work when using it in Shader Storage Object:
Definitions in shader:
struct Object
{
vec4 color;
mat3 transform;
float depth;
float pObjIndex;
//float align1;
//float align2;
};
layout (std430, binding = 0) buffer Objects
{
Object objects[];
};
It's my understanding that the Object structure is aligned in GLSL by its largest member which is vec4, i.e. by 16-byte boundary. (mat3 is treated as 3-element array of vec3's and is aligned by 12-bytes boundary). The vec4, mat3 and 2 floats give 72 bytes. When I pad the structure to the nearest 16-byte multiple - 80 - with two floats, it's not working properly just as without padding.
Are both variants of this structure's layout - with padding with 2 floats and without any padding - incorrect?
the mat3 is actually 3 vec4s as each vec3 is 3 floats aligned to 16 bytes (or at least it may not straddle a 16 byte boundary)
struct Object
{
float[4] color;
float[4][3] transform;
float depth;
float pObjIndex;
//float align1;
//float align2;
};
I have a pixel shader buffer
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding; //padding to align to 16 bytes
};
That corresponds to a struct in my shader class:
struct InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding;
};
Which looks all fine, since its 16-byte aligned. But when I render I get this warning:
The size of the Constant Buffer at slot 0 of the Pixel Shader unit is too small (16 bytes provided, 32 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL]
Why would the buffer expect 32 bytes? I thought 16 was the minimum and mine clearly is?
EDIT 1: I've added the output of D3DReflect and my shader code.
After running D3DReflect on the pixel shader and retrieving a ID3D11ShaderReflectionConstantBuffer pointer to the constant buffer I ran the GetDesc method on the buffer and got what I expected:
Type: D3D_CT_BUFFER(0)
Variables: 4
Size: 16
Flags: 0
As requested, here is the pixel shader code:
/////////////
// GLOBALS //
/////////////
Texture2D shaderTextures[2]; // 0 is the velocity field texture and 1 is the field that is to be advected
SamplerState SampleType;
/////////////
// BUFFERS //
/////////////
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float fDissipation;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType {
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
// Pixel Shader
float4 AdvectionPixelShader(PixelInputType input) : SV_TARGET {
float2 u = shaderTextures[0].Sample(SampleType, input.tex).xy; // velocity
float2 rdFactors = float2(1.0f/fTextureWidth,1.0f/fTextureHeight);
float2 prevPos = float2(input.tex.x - (fTimeStep*rdFactors.x*u.x),input.tex.y - (fTimeStep*rdFactors.y*u.y) );
float2 final = shaderTextures[1].Sample(SampleType,prevPos).xy;
return float4(final,0.0f,1.0f)*fDissipation;
}
I am passing in a constant buffer with the following layout:
struct
{
float spread;
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
} m_constants;
for debugging sake, dimension and dimension2 have the same values.
In the shader i have:
cbuffer constants
{
float spread;
float2 dimension;
float2 dimension2;
};
float4 main(
float4 pos : SV_POSITION,
float4 posScene : SCENE_POSITION,
float4 uv0 : TEXCOORD0
) : SV_Target
{
float width = dimension.x;
float height = dimension.y;
float2 uv2 = float2(posScene.x / width, posScene.y / height);
color.rgb = float3(uv2.xy, 0);
return color;
}
this, in theory, should output a gradient with green on the bottom left and red at the top right. And it does.
But if, in the shader i have the width and height to use dimension2 instead. i get a horizontal gradient from green on the left to yellow on the right.
Why is that? both dimensions have the same value when i passed the m_constants to the shader
Constant buffers data is aligned by 16 bytes by default, so this means:
cbuffer constants
{
float spread;
float2 dimension;
float2 dimension2;
};
will be
cbuffer constants
{
float spread; // 4 bytes
float2 dimension; // 4 + 8 = 12 bytes
float dummy; //12+8 = 20, which means we cross 16 for dimension 2, hence a dummy 4 bytes element is added
float2 dimension2;
};
here is a link that describes this.
So a better way to arrange your structure would be:
struct
{
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
float spread;
} m_constants;
and modify the hlsl counterpart accordingly:
cbuffer constants
{
float2 dimension;
float2 dimension2;
float spread; // No more 16 bytes crossing problem
};
Another way, without modifying initial layout, in c++ side, either declare your structure like:
#pragma pack(push)
#pragma pack(16)
struct
{
float spread;
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
} m_constants;
#pragma pack(pop)
That will force structure to be 16 bytes aligned.
You can also use /Zp16 compiler flag , but that will then apply to every structure in your program (which is not always desirable). In visual studio go to project properties -> c/c++ -> Code Generation, then you have option "Struct Member Alignment", where you can set it from.
You can also use packoffset on hlsl side, but then it means that the c++ layout needs to match the packed hlsl one (which means you keep same order in hlsl constant buffer, but still have to modify the c++ version).
I'm currently programming a phong spot-light shader that uses the new ogl3 feature, ubos. My buffer object in the shader is :
`
Uniform Light {
vec3 origin;
vec3 color;
vec3 direction;
float intensity;
float linear_dissipation;
float illu_angle;
float max_illu_angle;
} Light[8];
`
When I calculate the various offsets via the function
glGetActiveUniformsiv()
my program returns
origin : 0
color : 16
intensity : 48
direction : 32
illu_angle : 48
max_illu_angle : 48
linear_dissipation : 48
I can't use my intensity, illu_angle, max_illu_angle and linear_dissipation variables (well, only one of them :D ).
Given that origin, color, and direction are all aligned on 16 bytes, I assume that OpenGL is combining the last four floats into a single vec4.
Did you actually try using one of them to verify that they are or are not aliased?