shader - HLSL 6+ 统一变量和编译

标签 shader hlsl direct3d dxc

我来自 DX9 背景,使用现已过时的 FX 框架,其中统一变量可以用作更改着色器编译的一种预处理器值。这使得开发人员能够轻松、毫不费力地将单个着色器编译成其自身的多个版本,其中每个版本都接受每个统一变量的不同值。然后,在运行时根据发送到 API 的数据自动选择这些着色器。例如,当编写根据灯光或骨骼数量发生显着变化的着色器时,这非常有用。

我现在正在尝试构建一个非 FX 系统,使用直接 HLSL 6 和自定义 D3D12 游戏引擎,如果不是为了生成多个变量,我很难理解统一变量的意义是什么。来自一个源代码的着色器?我能找到的所有信息听起来好像统一变量只是需要在运行时提供的东西,这表明它不会修改编译的代码。

我目前正在使用带有 Visual Studio 2022 的最新 dxc 编译器,并且看不到如何定义依赖于统一变量甚至宏的任何类型的多着色器输出系统事情。看起来一个项目文件等于一个着色器输出,就这样。我在这里可能缺少什么吗?除了最简单的着色器或者从不使用统一值的着色器之外,这似乎没有任何用处。

我正在考虑编写自己的运行时编译器,它在运行时使用 dxc,它可以在编译之前以编程方式用每个可能的值替换统一变量,然后将其输出为单独的 *.cso。然而,想出一种基于非 int 值(如浮点值)查找所需着色器的有效方法似乎是一场噩梦,需要某种类型的哈希表。除此之外,一个着色器可以有多个统一值,这似乎会变得非常复杂。乍一看,要在运行时将一组特定的输入快速连接到特定的编译着色器,需要类似的东西......

csoObject FindCompiledShader(name,arg1,arg2,arg3,...)
{
    auto hash_index = Hash(arg1*S1) + Hash(arg2*S2) + Hash(arg3*S3) + ...;
    int table_index = hash_index % size_of_table;
    return Table[table_index].SearchFor(name,arg1,arg2,arg3,...);
}

每当每个渲染元素的动态值发生变化时,就需要发生这种情况。这似乎很快就会变得粘稠。特别是如果有许多元素每帧都会更改统一值,这似乎很常见。

有人能帮我了解现在这种事情是如何处理的吗?我只是不明白统一变量是如何工作的吗?或者有一些更简单的方法来管理它吗?一般而言,似乎没有太多关于编译 HLSL 的信息。我假设是因为现在每个人都使用流行的游戏引擎。

最佳答案

通常,同一个着色器源文件会被编译多次,以生成 HLSL 着色器的各种排列。例如,在 DirectX Tool Kit for DX12EnvironmentMapEffect.fx 着色器文件对函数使用一些统一参数:

VSOutputTxEnvMap ComputeEnvMapVSOutput(VSInputNmTx vin, float3 normal, uniform bool useFresnel, uniform int numLights)
{
...
}

有四种不同的调用方式:

// Vertex shader: basic.
[RootSignature(DualTextureRS)]
VSOutputTxEnvMap VSEnvMap(VSInputNmTx vin)
{
    return ComputeEnvMapVSOutput(vin, vin.Normal, false, 3);
}

[RootSignature(DualTextureRS)]
VSOutputTxEnvMap VSEnvMapBn(VSInputNmTx vin)
{
    float3 normal = BiasX2(vin.Normal);

    return ComputeEnvMapVSOutput(vin, normal, false, 3);
}

// Vertex shader: fresnel.
[RootSignature(DualTextureRS)]
VSOutputTxEnvMap VSEnvMapFresnel(VSInputNmTx vin)
{
    return ComputeEnvMapVSOutput(vin, vin.Normal, true, 3);
}

[RootSignature(DualTextureRS)]
VSOutputTxEnvMap VSEnvMapFresnelBn(VSInputNmTx vin)
{
    float3 normal = BiasX2(vin.Normal);

    return ComputeEnvMapVSOutput(vin, normal, true, 3);
}

然后使用此批处理文件完成编译:

call :CompileShader%1 EnvironmentMapEffect vs VSEnvMap
call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapBn
call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapFresnel
call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapFresnelBn
call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapPixelLighting
call :CompileShader%1 EnvironmentMapEffect vs VSEnvMapPixelLightingBn

call :CompileShader%1 EnvironmentMapEffect ps PSEnvMap
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapNoFog
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpecular
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpecularNoFog
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLighting
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingNoFog
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingFresnel
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapPixelLightingFresnelNoFog

call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLighting
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingNoFog
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingFresnel
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapSpherePixelLightingFresnelNoFog

call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLighting
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingNoFog
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingFresnel
call :CompileShader%1 EnvironmentMapEffect ps PSEnvMapDualParabolaPixelLightingFresnelNoFog

...

:CompileShaderdxil
set dxc=%PCDXC% "%1.fx" %FXCOPTS% /T%2_6_0 /E%3 "/Fh%CompileShadersOutput%\%1_%3.inc" "/Fd%CompileShadersOutput%\%1_%3.pdb" /Vn%1_%3
echo.
echo %dxc%
%dxc% || set error=1
exit /b

有关完整的着色器源代码和编译脚本,请参阅 GitHub .

The legacy Effects system did all these permutations for you automatically. With DirectX 12, the root signature management makes it hard to make a fully general solution that's also efficient. The need to match root signatures also often results in additional permutations of the same shaders being compiled for different contexts.

关于shader - HLSL 6+ 统一变量和编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74088740/

相关文章:

opengl-es-2.0 - 为什么不为OpenGL ES 2.0 gl_Position使用vec3?

opengl - 编译着色器失败,我需要英特尔平台上的 ubuntu 的 openGL 驱动程序吗?

c++ - Windows xp 32 位 d3d9.dll 与 Windows 7 64 位 d3d9.dll

c++ - Direct3D10:没有 32 位 ARGB 格式?

c++ - DirectX 8.1 中来自表面的纹理

ios - OpenGL - 计算着色器 - iOS - 选项?

hlsl - 如何在 HLSL 中处理 8 位字符数据?

silverlight - HLSL 用于获得圆柱体效果

hlsl - DXR : How to identify the geometry instance of the bottom level AS inside the closest hit shader

java - 朗伯着色器仍然无法工作