Loading and using an HLSL shader?
I've been looking everywhere and all I can find are tutorials on writing the shaders. None of them showed me how to incorporate them into my scene.
So essentially:
Given an hlsl shader, if I were to have a function called drawTexturedQuad() and I wanted the shader to be applied to the result, how exactly could I do this?
开发者_JAVA百科Thanks
ID3DXEffect provides Begin()
and BeginPass()
methods. Simply call drawQuad()
during that time. Any basic tutorial on shaders should show such a sample.
Just an additional note- if in doubt, ask MSDN.
The answer to this is surprisingly complex, and has been getting more difficult as the GPU hardware has been getting more and more powerful. The D3DX FX system is an example of all the work that needs to be done, so using that is a good step to just getting things working for short-term usage.
Shaders are code, but they live on another machine from the CPU, so need all of their data marshalled over. The fixed parts: basic render states like depth states, stencil states, blending modes, drawing commands; are extremely easy to implement. The hard part is making a bridge for the programmable parts: shaders, buffers, samplers, and textures.
Index buffers just work, since you can only have one, or none in the case of rendering un-indexed geometry.
Vertex buffers are more or less fairly easy to deal with, since the hardware can be programmed to read the vertex buffers procedurally. Your vertex buffers only needs to provide at least as much information as the vertex shader wants to access. Modifications to the shader's vertex input, or the vertex format requiring editing both sides at the same time, and so is reasonably easy to work with.
Samplers and Textures are the next 'easier' of the hard parts to hook: they have a variable name and a rather rigid type. For instance, When compiling shader 'foo', texture 'myNormalMap', is assigned texture slot 3. You need to look up (via the reflection APIs) which slot the texture was assigned, and set the texture your engine considers 'myNormalMap' to be to slot 3 at runtime, and of course also use the API to determine if the texture is even needed in the first place. This is where starting to have naming conventions for shader variables starts to matter, so multiple shaders can be made compatible with the same C++ code.
Constant buffers (or raw shader constants in D3D9) are a lot trickier, especially with a programmable shader framework like you can find in engines like Unreal. The constants any given shader uses is a subset of the full list, but the C++ side must generally be written as if all of them are needed. The reflection APIs again are needed to determine not only which variables are actually referenced in a shader, but where they are located. This became a bit more manageable in D3D10 and newer as the cbuffers are structs and less fluid than the D3D9 system which was heavily limited by register count, but it also adds the step of also needing to use the reflection APIs to determine the order of cbuffer bindings (and which cbuffers themselves are also referenced).
In the end there is one design to make it all work:
Make a class that drives a specific shader archetype. For each variable this class exposes to a shader (be it a texture, constant buffer, etc), look up in the reflection info if it is used, and find out its location, and set it. Keeping this fast, flexible, and extensible is all a difficult challenge.
精彩评论