Get Triangle Primitive in Vertex Shader

I’m writing a shader that draws the shadow of a character by drawing it again with an offset depending on the positions of the lights in the scene. This offset is calculated from the middle of each character.

This is a job for a Geometry Shader, but since you can’t use those in MonoGame I have to use a Vertex Shader. I can’t seem to find a way to get the world middle position of the character without passing it in with C#.

The problem with that is I can’t batch multiple characters together with a VertexBuffer.

So my question is, is there a way to get the current primitive in the Vertex Shader, or some form of context of the primitive being drawn?

I don’t really understand why you need to access the entire triangle in your vertex shader, but generally the answer is no. You can’t access other vertices. If you really want to do something like that, you have to include that information in your vertex data. So in addition to the normal vertex position, you have two additional vertex positions for the other vertices in the triangle.

As for batching multiple meshes into a single vertex buffer, while still having per-mesh parameters. You can do that by adding an index to your vertex data. So all vertices from the first mesh have index 0, all vertices from the second mesh have index 1 etc. You can then do an array lookup using that index. In your case that would be an array of center positions.

If the meshes in your batch are all the same, hardware instancing is probably a better solution, because you don’t need to create this giant vertex buffer, and you don’t need to add any extra information to every single vertex.

1 Like

I guess I don’t need the triangle, I just need the middle position and I figured I could calculate it if I had three vertices instead of one. Do you mean I can pass in the middle position for example for every vertex along with each VertexPositionTexture, or whatever and access it from the shader using a semantic? In that case how? This is for a 2D game BTW.

Oh it’s for 2D quads, in that case you don’t even have to be concerned about per-vertex overhead, as it’s just 4 vertices.

Absolutely, you just have to use a different vertex type that has space for the extra information. You can make a new vertex type just like VertexPositionTexture, but with a second texture coordinate for the center position.
If you don’t want to make a new vertex type you could also just use VertexPositionNormalTexture, and use the normal for your center position. Nobody is going to sue you for abusing semantics like that.

Say I wanted to use Position1, what would the vertex type look like?

This works:

public struct VertexPosition2Texture : IVertexType
{
    public Vector3 Position0;
    public Vector2 Position1;
    public Vector2 TextureCoordinate;
    public static readonly VertexDeclaration VertexDeclaration;

    public VertexPosition2Texture(Vector3 position0, Vector2 position1, Vector2 textureCoordinate)
    {
        this.Position0 = position0;
        this.Position1 = position1;
        this.TextureCoordinate = textureCoordinate;
    }

    VertexDeclaration IVertexType.VertexDeclaration
    {
        get
        {
            return VertexDeclaration;
        }
    }
    public override int GetHashCode()
    {
        // TODO: Fix get hashcode
        return 0;
    }

    public override string ToString()
    {
        return string.Format("{{Position0:{0} Position1:{1} TextureCoordinate:{1}}}", new object[] { this.Position0, this.Position1, this.TextureCoordinate });
    }

    public static bool operator ==(VertexPosition2Texture left, VertexPosition2Texture right)
    {
        return left.Position0 == right.Position0 && left.Position1 == right.Position1 && left.TextureCoordinate == right.TextureCoordinate;
    }

    public static bool operator !=(VertexPosition2Texture left, VertexPosition2Texture right)
    {
        return !(left == right);
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }
        if (obj.GetType() != base.GetType())
        {
            return false;
        }
        return this == (VertexPosition2Texture)obj;
    }

    static VertexPosition2Texture()
    {
        var elements = new VertexElement[] { new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.Position, 1), new VertexElement(20, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0) };
        VertexDeclaration = new VertexDeclaration(elements);
    }
}