从网格位置计算纹理坐标

时间:2012-02-22 03:46:42

标签: c# xna shader hlsl

我们的游戏使用“块图集”,这是一种方形纹理网格,对应于游戏中块的特定面。我们的目标是通过将顶点中的纹理数据存储为short而不是float2来简化顶点数据内存。这是我们的顶点定义(XNA,C#):

public struct BlockVertex : IVertexType
{
    public Vector3 Position;
    public Vector3 Normal;
    /// <summary>
    /// The texture coordinate of this block in reference to the top left corner of an ID's square on the atlas
    /// </summary>
    public short TextureID;
    public short DecalID;


    // Describe the layout of this vertex structure.
    public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration
    (
        new VertexElement(0, VertexElementFormat.Vector3,
                             VertexElementUsage.Position, 0),

        new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector3,
                              VertexElementUsage.Normal, 0),

        new VertexElement(sizeof(float) * 6, VertexElementFormat.Short2,
                              VertexElementUsage.TextureCoordinate, 0)
    );


    public BlockVertex(Vector3 Position, Vector3 Normal, short TexID)
    {
        this.Position = Position;
        this.Normal = Normal;
        this.TextureID = TexID;
        this.DecalID = TexID;
    }

    // Describe the size of this vertex structure.
    public const int SizeInBytes = (sizeof(float) * 6) + (sizeof(short) * 2);

    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return VertexDeclaration; }
    }
}

我对HLSL没什么经验,但是当我尝试调整当前着色器来使用这个顶点时(而不是那个将Texture和Decal存储为Vector2坐标的旧版本),我只得到透明的蓝色模型,我相信这意味着面部的纹理坐标都是一样的吗?

这是我尝试解释顶点数据的HLSL:

int AtlasWidth = 25;
int SquareSize = 32;
float TexturePercent = 0.04; //percent of the entire texture taken up by 1 pixel

[snipped]

struct VSInputTx
{
    float4 Position             : POSITION0;
    float3 Normal               : NORMAL0;
    short2 BlockAndDecalID      : TEXCOORD0;
};

struct VSOutputTx
{
    float4 Position     : POSITION0;
    float3 Normal       : TEXCOORD0;
    float3 CameraView   : TEXCOORD1;
    short2 TexCoords    : TEXCOORD2;
    short2 DecalCoords  : TEXCOORD3;
    float FogAmt        : TEXCOORD4;
};

[snipped]

VSOutputTx VSBasicTx( VSInputTx input )
{
    VSOutputTx output;

    float4 worldPosition = mul( input.Position, World );
    float4 viewPosition = mul( worldPosition, View );
    output.Position = mul( viewPosition, Projection );

    output.Normal = mul( input.Normal, World );
    output.CameraView = normalize( CameraPosition - worldPosition );

    output.FogAmt = 1 - saturate((distance(worldPosition,CameraPosition)-FogBegin)/(FogEnd-FogBegin)); //This sets the fog amount (since it needs position data)

    // Convert texture coordinates to short2 from blockID
    // When they transfer to the pixel shader, they will be interpolated
    // per pixel.
    output.TexCoords = short2((short)(((input.BlockAndDecalID.x) % (AtlasWidth)) * TexturePercent), (short)(((input.BlockAndDecalID.x) / (AtlasWidth)) * TexturePercent));
    output.DecalCoords = short2((short)(((input.BlockAndDecalID.y) % (AtlasWidth)) * TexturePercent), (short)(((input.BlockAndDecalID.y) / (AtlasWidth)) * TexturePercent));

    return output;
}

我在原始着色器中没有更改任何其他内容,显示一切正常,但您可以看到整个.fx文件here

如果我可以调试我可以得到它的东西,但是......好吧,不管怎样,我认为这与我对着色器如何工作的有限知识有关。我想我尝试使用整数运算效果不佳。此外,这是很多演员阵容,如果价值在某处被强制为0,我可以相信它。如果我不合时宜,这就是我的目标:

  1. Shader得到一个存储两个短片以及其他数据的顶点。短裤表示方形纹理网格的某个角的ID。一个用于块面,另一个用于贴在那个面上的贴花。
  2. 着色器使用这些ID,以及一些定义纹理网格大小的常量(以下称为“Atlas”),以确定此特定角的实际纹理坐标。
  3. 纹理坐标的X是(ID%AtlasWidth)* TexturePercent,其中TexturePercent是一个常量,表示由1个像素表示的整个纹理的百分比。
  4. 纹理坐标的Y是(ID / AtlasWidth)* TexturePercent。
  5. 我该如何解决这个问题?

    更新:我在某个时候收到错误“vs_1_1不支持8位或16位整数”这可能是问题的一部分吗?

2 个答案:

答案 0 :(得分:1)

output.TexCoords = short2((short)(((input.BlockAndDecalID.x) % (AtlasWidth)) * TexturePercent), (short)(((input.BlockAndDecalID.x) / (AtlasWidth)) * TexturePercent));
output.DecalCoords = short2((short)(((input.BlockAndDecalID.y) % (AtlasWidth)) * TexturePercent), (short)(((input.BlockAndDecalID.y) / (AtlasWidth)) * TexturePercent));

这两行包含以下几个部分:

(input.BlockAndDecalID.x) / (AtlasWidth)

(input.BlockAndDecalID.y) / (AtlasWidth)

这些都是整数除法。整数除法会切断所有小数点。我假设AtlasWidth总是大于每个坐标,因此这两个分区总是会产生0.如果这个假设不正确,我假设你仍然想要某种十进制数据吗?

你可能想要一个浮点除法,它会返回一个十进制结果,因此你需要先将至少一个(或两个)操作数转换为float,例如:

((float)input.BlockAndDecalID.x) / ((float)AtlasWidth)

编辑:我会改用NormalizedShort2;它将您的值缩放最大短值(32767),允许使用短值的“十进制”(换句话说,“0.5”= 16383)。当然,如果您的意图只是将tex coord数据减半,则可以通过使用HalfVector2仍然使用浮点数来实现此目的。如果你仍然坚持Short2,你可能需要像NormalizedShort2那样在将它作为坐标提交之前进行缩放。

答案 1 :(得分:0)

好吧,在我尝试在其中一个着色器函数中创建一个short并面临编译器错误后,表明vs_1_1不支持8/16位整数(为什么你之前没告诉我?) ,我把功能改成了这样:

float texX = ((float)((int)input.BlockAndDecalID.x % AtlasWidth) * (float)TexturePercent);
float texY = ((float)((int)input.BlockAndDecalID.x / AtlasWidth) * (float)TexturePercent);
output.TexCoords = float2(texX, texY);
float decX = ((float)((int)input.BlockAndDecalID.y % AtlasWidth) * (float)TexturePercent);
float decY = ((float)((int)input.BlockAndDecalID.y / AtlasWidth) * (float)TexturePercent);
output.DecalCoords = float2(decX, decY);

这似乎工作得很好。我不确定它是在改变内部的东西还是在前面投射(浮动)。我认为这可能是浮动,这意味着斯科特肯定会有所作为。我的伙伴以某种方式编写了这个着色器,使用short2作为纹理坐标;他是怎么做的我不知道。