"I tried with all the models from the Spacewar starter kit and they seem to all have the same problem. The cockpit and engine exhausts never get the correct material/texture/lighting, although the fuselage looks fine."
Short answer: the bodies of the Spacewar ships have texture maps, but the cockpits do not. To render these correctly, your shader should disable texturing and just use the material color instead.
Longer answer: this provides a great example of why BasicEffect is actually not so basic after all! Shader permutations are the bane of graphics engine developers, and BasicEffect supports many permutations:
- Simple shader applies one texture plus directional lighting
- But what if there is no texture?
- What if your model has vertex colors?
- What if you want to disable lighting?
- What if you want the lighting to be per-pixel rather than per-vertex?
Two texture modes (on, off) plus two vertex color modes (on, off) and three lighting modes (off, per-vertex, per-pixel) ends up requiring 2*2*3 = 12 different versions of the BasicEffect shader.
Not all of these shaders can be used with every model. If the shader does not match its input data, things will render incorrectly. You will get unpredictable results if:
- Shader uses texturing, but no texture is set
- Shader uses texturing, but the model does not contain texture coordinate data
- Shader uses vertex colors, but the model does not contain vertex color data
- Shader uses lighting, but the model does not contain normal data
What exactly do I mean by unpredictable? On some graphics cards the rendering will come out white, while on others it may be black. If you use the debug DirectX runtime, you will get an error. This is not a good place to be!
People using BasicEffect through the Content Pipeline are often unaware of these shader variations, because the build process takes care of them for you:
- The pipeline always generates vertex normals, so you can safely turn on lighting even if your original model file did not include normal data.
- Any time ModelProcessor encounters a BasicMaterialContent, it checks whether this material includes a texture, and automatically sets BasicEffect.TextureEnabled to match.
- ModelProcessor warns if a material has a texture but the geometry that uses it does not include texture coordinate data.
- Any time ModelProcessor encounters a BasicMaterialContent, it checks whether the geometry that uses this material includes vertex color data, and automatically sets BasicEffect.VertexColorEnabled to match.
Thanks to this processor magic, most models "just work" without you having to think too hard about it. But if you do stop to think, you will realize that what looks like a single BasicEffect is in fact 12 different shaders masquerading as one. This only works because the Content Pipeline has some smarts to examine your model data and automatically choose the right version of BasicEffect for each object.
If you use a custom shader instead of BasicEffect, the default content processors will no longer understand how to set it up for you. This leaves you with two choices:
- You could make your shader as complicated as BasicEffect, creating several versions to match different source data, and then write some code (either in a custom processor or while your game is loading) to choose the right shader for each object.
- Or you can simplify things by imposing restrictions on your source artwork. For instance many games decide something like "all our objects are going to be lit, and will be textured, and will not use vertex colors". This can make it dramatically easier to write and manage your shaders.