Search code examples
c#3dxnahlslsketchup

HLSL shaders, how to draw a model with it's original colors?


I made this very simple 3D model exported to .x format with Google SketchUp, the model is a simple cube and has no textures, only different colors for each of it's face.

I have written this HLSL code to try to render this model with it's original colors, but it doesn't work, I get a "The current vertex declaration does not include all the elements required by the current vertex shader. Color0 is missing" when calling the ModelMesh.Draw() method. Here is my HLSL Code:

float4x4 World;
float4x4 View;
float4x4 Projection;

struct AppToVertex
{
    float4 Position : POSITION0;
    float4 Color    : COLOR0;
};

struct VertexToPixel
{
    float4 Position : POSITION0;
    float4 Color    : COLOR0;
};

VertexToPixel ColoredVS(AppToVertex input)
{
    VertexToPixel output = (VertexToPixel)0;

    float4 iTransformed = mul(input.Position, World);
    float4 iView = mul(iTransformed, View);
    float4 iProjection = mul(iView, Projection);

    output.Position = iProjection;
    output.Color = input.Color;

    return output;
}

float4 ColoredPS(VertexToPixel input) : COLOR
{
    return input.Color;
}

technique Colored
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 ColoredVS();
        PixelShader = compile ps_2_0 ColoredPS();
    }
}

This is probably a noob question and I am aware that I could draw this model by simply using the BasicEffect class(which works), but I am doing this just for learning HLSL, and up to now all I was able to do was to draw a model with another color defined in the shader all over the model :[


Solution

  • This is the first time i've looked at a .X but I don't believe that Sketchup is exporting vertex colours for your model.

    (Someone who is more experienced with this format please correct me if you see anything wrong!)

    template Mesh {
    <3D82AB44-62DA-11cf-AB39-0020AF71E433>
    DWORD nVertices;
    array Vector vertices[nVertices];
    DWORD nFaces;
    array MeshFace faces[nFaces];
    [...]
    }
    
    template MeshVertexColors {
    <1630B821-7842-11cf-8F52-0040333594A3>
    DWORD nVertexColors;
    array IndexedColor vertexColors[nVertexColors];
    }
    

    Looking at your model's .X 'declarations', I can see the mesh's geometry, channels such as colours & normals, etc are stored as seperate 'objects' (just like they are in the DOM in XNAs Content Pipeline).

    Whereas your model has definitions for position data (Mesh), normal data (MeshNormals) and texture coords (MeshTextureCoords) (all defined below the templates section) I can see no section of the type MeshVertexColors.

    Instead it appears that four Materials are defined, and applied to your models faces using the

     template MeshMaterialList {
    <F6F23F42-7686-11cf-8F52-0040333594A3>
    DWORD nMaterials;
    DWORD nFaceIndexes;
    array DWORD faceIndexes[nFaceIndexes];
    [Material]
    }
    

    section.

    So you don't have any vertex colours for ModelProcessor to import, or your shader to display.

    There is probably an option in Sketchup to enable Vertex Colours. However if you can't find/don't want to look for it, you can add and remove vertex channels in the Content Pipeline by creating a Content Processor in a Content Pipeline Extension project.

    (I know this sounds really unpleasant but acctually this is a really great feature when you get used to it!)

    There are quite a few tutorials online about this but in a nutshell:

    1. Go into VS, right click, Add New Project > Content Pipeline Extension Library
    2. In that new project, add a new Content Processor item, something along the lines of that below.
    3. Recompile and then in the Content Processor field of your models .X file entry in the Content project, select your new importer (by the name you give it in the DisplayName attribute)

    What this is doing is essentially intercepting the data from your .X after it has been imported into XNAs Document Object Model, and before it is processed into a new Model class, the processor checks for the presence of a vertex colour channel, and if one is not found, generates and adds it, before the ModelProcessor creates the VertexDeclaration and packs all the vertex data into a VertexBuffer to be loaded by the GPU.

    This will just the display the cube as a solid purple but at least you can see if your shader is working.

    [ContentProcessor(DisplayName = "Processor to make sure we have vertex colours in all models")]
    public class Character_Model_Processor : ModelProcessor
    {
        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            foreach(NodeContent c in input.Children)
            {
                if(c is MeshContent)
                {
                    foreach(GeometryContent g in (c as MeshContent).Geometry)
                    {
                        //Stop here and check out the VertexContent object (g.Vertices) and the Channels member of it to see how
                        //vertex data is stored in the DOM, and what you can do with it.
                        System.Diagnostics.Debugger.Launch();
    
                        AddVertexColorChannel(g.Vertices);
                    }
                }
            }
    
            ModelContent model = base.Process(input, context);
    
            return model;
        }
    
        private void AddVertexColorChannel(VertexContent content)
        {
            if(content.Channels.Contains(VertexChannelNames.Color(0)) == false)
            {
                List<Microsoft.Xna.Framework.Color> VertexColors = new List<Microsoft.Xna.Framework.Color>();
    
                for (int i = 0; i < content.VertexCount; i++)
                {
                    VertexColors.Add(Color.Purple);
                }
    
                content.Channels.Add(VertexChannelNames.Color(0), VertexColors);
            }
        }
    }
    

    Good luck, sorry if you already knew most of that, I guess you probably know all about the pipeline modifications if you are writing your own shaders but better safe :)

    EDIT: Just a note about that System.Diag... line, if you break into the debugger, make sure you when you're done exploring that you hit F5 and let it run its course otherwise it takes your main VS window with it when it exits - not a problem just really annoying when you forget to comment it out ;)