【问题标题】:HLSL Integer Texture CoordinatesHLSL 整数纹理坐标
【发布时间】:2018-12-13 22:49:13
【问题描述】:

我试图在整数像素坐标之间而不是在 0-1 之间进行插值,因为我使用的是点采样,所以我对像素的分数不感兴趣,但纹理坐标仍会进入像素着色器即使数据类型为 int2,也为 float2。

pixelSize 是 1 除以纹理大小

matrix WorldViewProjection;

float2 pixelSize;

Texture2D SpriteTexture;

sampler2D SpriteTextureSampler = sampler_state
{
    Texture = <SpriteTexture>;
    AddressU = clamp;
    AddressV = clamp;
    magfilter = POINT;
    minfilter = POINT;
    mipfilter = POINT;
};

struct VertexShaderOutput
{
    float4 Position : SV_POSITION;
    float4 Color : COLOR0;
    int2 TextureCoordinates : TEXCOORD0;
};

VertexShaderOutput SpriteVertexShader(float4 position : POSITION0, float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
    VertexShaderOutput output;
    output.Position = mul(position, WorldViewProjection);
    output.Color = color;
    output.TextureCoordinates = texCoord * (1 / pixelSize);
    return output;
}

float4 SpritePixelShader(VertexShaderOutput input) : COLOR
{
    float2 texCoords = input.TextureCoordinates * pixelSize;
    return tex2D(SpriteTextureSampler, texCoords) * input.Color;
}

technique SpriteDrawing
{
    pass P0
    {
        VertexShader = compile vs_2_0 SpriteVertexShader();
        PixelShader = compile ps_2_0 SpritePixelShader();
    }
};

【问题讨论】:

  • 如果pixelSize已经是1除以纹理大小,在计算纹理坐标时不应该再除。所以应该是output.TextureCoordinates = texCoord * pixelSize;
  • 没有。 1 / 1 / x = x。因此 1 / 1 / 纹理大小 = 纹理大小。我必须再次划分它以获得纹理大小,而不必设置两个变量。将纹理坐标乘以纹理大小可以得到整数纹理坐标。
  • 哦,我忽略了像素着色器中的* pixelSize。我认为您的顶点数据包含整数纹理坐标。你的问题到底是什么?在帖子中是没有问题的,我看不出将 texCoord 转换为整数并返回浮动而不做任何事情的意义。
  • 你问转换成整数然后再转换成浮点数的意义何在?你丢失了十进制值,我对像素的分数不感兴趣,正如我在我的问题中所说的那样。因此,与其获取插值纹理坐标,例如:(368.4 * (1 / textureWidth), 175.8 * (1 / textureHeight)),我希望它为 (368, 175)。我的问题是它保留了十进制值,即使它是 int2,所以我把它当作 (368.4, 175.8),这是没有意义的。
  • 所以我想向下取整,添加半个像素(为简单起见,我将其排除在外)并转换回 0-1 之间的纹理坐标。这样我总是在每个像素的中心采样。在点采样中,如果您的采样距离像素的边界太近,它将对相邻像素进行采样。

标签: shader hlsl


【解决方案1】:

我的理解是,您需要将0-1 的范围调整为0-w:h,然后再调整为0-1。对此非常有用的技术称为feature scaling

特征缩放是一种用于标准化自变量或数据特征范围的方法。在数据处理中,也称为数据归一化,一般在数据预处理阶段进行。

有几种方法可以解决这个问题,但我将重点关注rescaling approach。最初,这种方法侧重于获取诸如200-300 之类的范围并将其缩放到0-1。数学很简单:

其中x 是原始值,x' 是标准化值。


在我们的例子中,我们想要反方向,从0-1 缩放回200-300,所以我们必须重新设计它;但是,既然我们已经做到了,为什么不把它放在我们可以满足您要求的任何一个方向:

将其转换为HLSL 是一项简单的任务,我建议将其放入通用方法中以便以后重用:

float RescaleInRange(float value, float oldMin, float oldMax, float newMin, float newMax) {
    if (value < oldMin) value = oldMin;
    else if (value > oldMax) value = oldMax;
    return ((value - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin;
}

我会将您的TextureCoordinates 保留为float2,以与大多数使用float2 的行业标准保持一致。然后在您的顶点着色器中为其分配提供的texCoord。稍后,在您的像素着色器中,您可以使用上面提供的重新缩放来单独处理TextureCoordinates 的单位(在我下面的代码中称为UV):

float w = textureSize.x;
float h = textureSize.y;
float x = RescaleInRange(UV.x, 0, 1, 0, w);
float y = RescaleInRange(UV.y, 0, 1, 0, h);
return tex2D(Sampler, float2(RescaleInRange(x, 0, w, 0, 1), RescaleInRange(y, 0, h, 0, 1))) * input.Color;

【讨论】:

  • 我发布的代码已经完成了您的代码的工作,只是以更简单、更高效的方式。你完全错过了重点。我想使用 int2 让它自动向下舍入,然后添加半个纹素,这样我总是从纹素的中间取样。不四舍五入的采样会导致采样相邻的纹素。手动四舍五入会导致浮点精度误差大到足以给出错误结果。
  • 我认为我使用 int2 很聪明,但无论如何它都强制它为 float2。做我想做的事是不可能的。它试图解决这样的问题:community.monogame.net/t/gaps-between-tiles/11144/14
  • @Martin 只需将float2 的每个单元转换为int,如下所示:int2((int)texCoord.x, (int)texCoord.y);
  • 像这样转换为 int 会产生上述浮点精度错误,使整个努力完全毫无意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多