在 Direct3D11 中模拟 Direct3D9 固定功能管道
Emulating Direct3D9 fixed function pipeline in Direct3D11
我们的软件目前使用 Direct3D9 中的固定功能管道,为我们的用户提供一种易于编写脚本的方式,将灯光和对象投射到一个简单的场景中。我们允许平行光、点光和聚光灯。我试图将我们转移到 Direct3D11,但我希望它尽可能接近 Direct3D9 固定功能管道作为基准。我们可以稍后添加诸如逐像素照明之类的东西。我是着色器编码的新手,尽管我多年来一直在使用 C++,但我还是觉得自己不适应。我想知道是否有支持 DX11 的着色器可以完美模拟 DX9 固定功能管道提供的照明。我在 FixedFuncEMUFX11 中看到了旧的点光源模拟示例,我将尝试将其用作基础,但我只知道我没有尽可能高效地执行此操作,并且数学正确的聚光灯仿真超出了我的范围。是否有任何开源软件可以模拟固定功能管道,或者包含 DirectX11/10 的定向、点和聚光灯实现?我认为我只需要 .fx 或 .hlsl 文件。就算是8个光源顶多也无妨。
我目前使用 DirectXTK 作为正确 DirectX11 编程的指南,但如果有更好的编码标准,我很乐意看到行业标准 DirectX11 渲染引擎编程方法的示例。感谢您提供任何建议。
对于 Direct3D 11 的基本渲染,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 硬件,因此他们不实现诸如聚光灯之类的东西,而这些东西很容易用更现代的硬件完成。
FixedFuncEMU11 示例是旧版 DirectX SDK 的 FixedFuncEMU Direct3D 10 示例的 Direct3D 11 端口。着色器对于理解各种 Direct3D 9 特定的固定功能管道很有用,但不涵盖 'standard' 诸如在 HLSL 中实现标准照明模型之类的内容。另请注意,此示例使用 Effects system for Direct3D 11 which has it's own issues。尽管如此,它还是很有用的:
- 固定功能转换管道
- 固定功能光照管线
- AlphaTest
- 用户剪辑平面
- 像素雾
- Gouraud 和 Flat 阴影模式
- 投影纹理查找 (texldp)
- 多纹理
- D3DFILL_POINT 填充模式
- 屏幕spaceUI渲染
您可能想尝试一些旧的 Direct3D 9 时代对 HLSL 着色器编程的介绍。虽然不是 100% 兼容 Direct3D 11,但它们非常接近并且 HLSL 本身基本相同。例如,我找到 this article。
还有许多优秀的 Direct3D 11 书籍,所有这些书籍都涵盖了 HLSL 着色器,因为 Direct3D 11 中没有固定功能的管道。请参阅 Book Recommendations 了解其中一些书籍的一些细节和注释在 DirectX SDK 停用之前。特别是,您应该查看 Varcholik 的 Real-Time 3D Rendering with DirectX and HLSL,因为它非常专注于 HLSL 创作。
对于通过在 hlsl 中搜索固定功能管道仿真而来到这里的任何人来说,这几乎正是我要找的东西:http://www.3dgep.com/texturing-lighting-directx-11/ 那里的着色器不是很优化,但已编写为了清楚起见,并为 hlsl 初学者提供了一个很好的介绍。为了准确了解正在发生的事情以及获得场景的最低限度,几乎没有多余的东西需要筛选 运行。聚光灯不是 DX9 的 FFP 聚光灯的精确复制,但很容易修改成这样。
#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;
}
我们的软件目前使用 Direct3D9 中的固定功能管道,为我们的用户提供一种易于编写脚本的方式,将灯光和对象投射到一个简单的场景中。我们允许平行光、点光和聚光灯。我试图将我们转移到 Direct3D11,但我希望它尽可能接近 Direct3D9 固定功能管道作为基准。我们可以稍后添加诸如逐像素照明之类的东西。我是着色器编码的新手,尽管我多年来一直在使用 C++,但我还是觉得自己不适应。我想知道是否有支持 DX11 的着色器可以完美模拟 DX9 固定功能管道提供的照明。我在 FixedFuncEMUFX11 中看到了旧的点光源模拟示例,我将尝试将其用作基础,但我只知道我没有尽可能高效地执行此操作,并且数学正确的聚光灯仿真超出了我的范围。是否有任何开源软件可以模拟固定功能管道,或者包含 DirectX11/10 的定向、点和聚光灯实现?我认为我只需要 .fx 或 .hlsl 文件。就算是8个光源顶多也无妨。
我目前使用 DirectXTK 作为正确 DirectX11 编程的指南,但如果有更好的编码标准,我很乐意看到行业标准 DirectX11 渲染引擎编程方法的示例。感谢您提供任何建议。
对于 Direct3D 11 的基本渲染,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 硬件,因此他们不实现诸如聚光灯之类的东西,而这些东西很容易用更现代的硬件完成。
FixedFuncEMU11 示例是旧版 DirectX SDK 的 FixedFuncEMU Direct3D 10 示例的 Direct3D 11 端口。着色器对于理解各种 Direct3D 9 特定的固定功能管道很有用,但不涵盖 'standard' 诸如在 HLSL 中实现标准照明模型之类的内容。另请注意,此示例使用 Effects system for Direct3D 11 which has it's own issues。尽管如此,它还是很有用的:
- 固定功能转换管道
- 固定功能光照管线
- AlphaTest
- 用户剪辑平面
- 像素雾
- Gouraud 和 Flat 阴影模式
- 投影纹理查找 (texldp)
- 多纹理
- D3DFILL_POINT 填充模式
- 屏幕spaceUI渲染
您可能想尝试一些旧的 Direct3D 9 时代对 HLSL 着色器编程的介绍。虽然不是 100% 兼容 Direct3D 11,但它们非常接近并且 HLSL 本身基本相同。例如,我找到 this article。
还有许多优秀的 Direct3D 11 书籍,所有这些书籍都涵盖了 HLSL 着色器,因为 Direct3D 11 中没有固定功能的管道。请参阅 Book Recommendations 了解其中一些书籍的一些细节和注释在 DirectX SDK 停用之前。特别是,您应该查看 Varcholik 的 Real-Time 3D Rendering with DirectX and HLSL,因为它非常专注于 HLSL 创作。
对于通过在 hlsl 中搜索固定功能管道仿真而来到这里的任何人来说,这几乎正是我要找的东西:http://www.3dgep.com/texturing-lighting-directx-11/ 那里的着色器不是很优化,但已编写为了清楚起见,并为 hlsl 初学者提供了一个很好的介绍。为了准确了解正在发生的事情以及获得场景的最低限度,几乎没有多余的东西需要筛选 运行。聚光灯不是 DX9 的 FFP 聚光灯的精确复制,但很容易修改成这样。
#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;
}