Вычисление координат текстуры из позиции сетки - PullRequest
0 голосов
/ 22 февраля 2012

Наша игра использует «блочный атлас», сетку квадратных текстур, которые соответствуют определенным граням блоков в игре. Мы стремимся оптимизировать память данных вершин, сохраняя данные текстуры в вершине в виде шорт вместо float2s. Вот наше определение вершины (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, но когда я попытался адаптировать наш текущий шейдер для использования этой вершины (в отличие от старого, в котором текстуры и декаль сохранялись как координаты 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 здесь .

Если бы я мог просто отладить то, что смог бы получить, но ... в любом случае, я думаю, это связано с моим ограниченным знанием того, как работают шейдеры. Я полагаю, что моя попытка использовать целочисленную арифметику менее эффективна. Кроме того, это много бросков, я мог бы поверить, если бы значения были вынуждены равны 0 где-то там. В случае, если я далеко от цели, вот что я стремлюсь достичь:

  1. Шейдер получает вершину, в которой хранятся две шорты, а также другие данные. Шорты представляют собой идентификатор определенного угла сетки из квадратных текстур. Один для лицевой стороны блока, другой для декали, которая нарисована поверх этой грани.
  2. Шейдер использует эти идентификаторы, а также некоторые константы, которые определяют размер текстурной сетки (далее именуемой «Атлас»), для определения фактической координаты текстуры этого конкретного угла.
  3. X координаты текстуры - это (ID% AtlasWidth) * TexturePercent, где TexturePercent - это константа, представляющая процент всей текстуры, представленной 1 пикселем.
  4. Y координаты текстуры - это (ID / AtlasWidth) * TexturePercent.

Как мне это сделать?

Обновление: в какой-то момент я получил сообщение об ошибке "vs_1_1 не поддерживает 8- или 16-разрядные целые числа", может ли это быть частью проблемы?

Ответы [ 2 ]

1 голос
/ 22 февраля 2012
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)input.BlockAndDecalID.x) / ((float)AtlasWidth)

РЕДАКТИРОВАТЬ: я бы использовал NormalizedShort2 вместо;он масштабирует ваше значение по максимальному короткому значению (32767), что позволяет использовать короткие значения как «десятичное» (другими словами, «0,5» = 16383).Конечно, если вы намеревались просто сократить вдвое ваши координаты текс, вы можете достичь этого, все еще используя плавающие точки с помощью HalfVector2.Если вы по-прежнему настаиваете на Short2, вам, вероятно, придется масштабировать его, как это делает NormalizedShort2, прежде чем отправлять его в качестве координаты.

0 голосов
/ 22 февраля 2012

Хорошо, после того, как я попытался создать короткое замыкание в одной из функций шейдера и столкнулся с ошибкой компилятора, которая говорила, что 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 в качестве координаты текстуры;как он это сделал я не знаю.

...