How to wrap texture coordinates manually?
我正在使用C和HLSL,并且需要package我的纹理坐标,以便将纹理平铺在一个三角形上。
将坐标"package"到0-1范围后,它们将被旋转,所以我不能简单地使用纹理采样器的AddressU和AddressV属性设置为环绕,因为它们需要被环绕然后旋转,因此无法在采样器内部完成。
这里的解决方案很简单,只需使用纹理坐标的小数部分,它将进行package。
这是一个像素着色器的示例,它将对纹理进行36次平铺(
1 2 3 | input.tex *= 6.0f; //number of times to tile ^ 2 input.tex = frac(input.tex); //wraps texCoords to 0-1 range return shaderTexture.Sample(SampleType, input.tex); |
这确实会平铺纹理,但会在包裹纹理的边界上产生问题。瓷砖必须均匀地划分为要在其上显示的空间,否则会在边沿汇合处形成接缝。绘制纹理的正方形是800x600像素,因此按5进行平铺将平分,但按6进行平铺将不平整,并且将导致沿Y轴的接缝。
我尝试使用模运算符
我很幸运使用此代码。如果
1 2 3 4 5 | input.tex *= 6.0f; input.tex = frac(input.tex); if (input.tex.x > 0.999f) input.tex.x = 0; if (input.tex.x < 0.001f) input.tex.x = 1; return shaderTexture.Sample(SampleType, input.tex); |
这只能解决某些问题,因此绝对不是解决方案。
这是一张显示纹理的图片(左)和手动包裹时的外观(右)。您可以看到并非所有寄宿生都碰到此错误。
如果纹理变形,则最高级别的采样将导致过度采样(像伪像一样进行点过滤)。为了防止纹理查找过采样,请根据屏幕空间中纹理坐标的变化选择适当的mipmaplevel。在您的情况下,边框在一个很小的地方变化很大,这导致使用了尽可能低的mipmap(这就是您看到的小红点,这是红色边框的原因)。
回到您的问题,您应该使用纹理查找方法
1 2 3 4 5 | input.tex *= 6.0f; //number of times to tile ^ 2 input.tex = frac(input.tex); //wraps texCoords to 0-1 range float2 xchange = ddx(input.tex); float2 ychange = ddy(input.tex); return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange); |
现在,您可以应用代码来防止xchange和ychange发生重大变化,从而迫使图形设备使用更高的mipmap。这应该删除您的工件。
如果不需要纹理贴图,因为您正在对齐纹理屏幕,并且纹理尺寸不会导致过采样,则可以使用更简单的替代方法
此处的代码导致对单个像素范围内的整个纹理进行采样。例如,对于接缝处的两个相邻像素,一个'u'样本可能是
如果您需要在每个范围内旋转纹理坐标(例如,分别旋转0..1、1..2),则有几种方法可以解决。首先,您可以将插值从线性更改为点,这样可以避免纹理像素之间的插值。但是,如果需要双线性插值,则可能无法接受。在这种情况下,您可以构造一个"网格"网格,并在每个图块上映射输入纹理0..1,而图块纹理坐标在着色器中独立旋转。
另一种可能的解决方案是将坐标转换为0..1空间,执行旋转,然后将其转换回其原始空间。例如,您可以这样做:
1 2 3 4 5 | // pseudo-code: int2 whole; input.tex = modf(input.tex, whole); input.tex = Rotate(input.tex); // do whatever rotation is needed input.tex += whole; |
这将确保package没有任何间断。或者,您可以让您的旋转代码考虑非统一空间纹理坐标。
Gnietschow发布了该问题的答案,但我将添加一个答案,以确切说明我如何使用该答案。
实际上,我什至不知道为什么这行得通,我什至知道即使使用其他多个平铺,各种纹理和随机旋转也可以做到这一点。
1 2 3 4 5 6 7 | input.tex *= 6.0f; //number of times to tile ^ 2 input.tex = frac(input.tex); //wraps texCoords to 0-1 range float2 xchange = ddx(input.tex); float2 ychange = ddy(input.tex); if ((xchange.x < 0)) xchange = float2(0, 0); if ((ychange.y < 0)) ychange = float2(0, 0); return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange); |
如果您根本不需要进行映射,则Gniet提到的另一种方法也可以正常工作
1 2 3 | input.tex *= 6.0f; //number of times to tile ^ 2 input.tex = frac(input.tex); //wraps texCoords to 0-1 range return shaderTexture.SampleLevel(SampleType, input.tex, 0); |