gpt4 book ai didi

c++ - 如何手动包装纹理坐标?

转载 作者:行者123 更新时间:2023-11-28 00:19:08 25 4
gpt4 key购买 nike

我正在使用 C++ 和 HLSL,需要让我的纹理坐标换行,以便纹理平铺在一个三角形上。

坐标被“包裹”到 0-1 范围后,它们将被旋转,所以我不能简单地使用纹理采样器 AddressU 和 AddressV 属性设置为包裹,因为它们需要包裹然后旋转,所以它无法在采样器内完成。

这里的解决方案很简单,只需要使用纹理坐标的小数部分,它就会环绕。

下面是一个像素着色器的示例,它将纹理平铺 36 次(6 * 6):

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 轴的接缝。

我尝试使用模数运算符 input.tex = input.tex % 1 来包装坐标,但我得到了完全相同的结果。我还尝试过更改纹理过滤方法以及 AddressU 和 AddressV 属性以及无数不同的调试方法。

我很幸运地使用了这段代码。如果 x 坐标太高,它会被设置为 0,如果它太低,它会被设置为 1。

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);

虽然这只能解决某些地方的问题,但绝对不是解决方案。

这是一张显示纹理(左)和手动包裹时的外观(右)的图片。你可以看到,并不是寄宿生接触到的所有地方都有这个错误。

我也尝试过不将纹理坐标更改为 0-1 范围并将它们围绕每个图 block 的中心而不是 (0.5, 0.5) 旋转,但我得到了相同的结果。此外,我的纹理坐标完全独立于顶点并在像素着色器内部计算。

我所看到的与此问题相关的任何事情都与在一个像素处具有高值然后在下一个像素处具有低值有关,例如 u = 0.95 和下一个像素 u = 0.03,这导致它向后插值穿过纹理。但是当我旋转我的纹理坐标时,什么都没有改变。即使每个图 block 都应用了随机旋转。在这种情况下,边缘有各种不同的值相互接壤,不仅仅是左侧的高值和右侧的低值,而且接缝出现的区域永远不会改变。

最佳答案

正如 MuertoExcobito 所说,主要问题是在边界处,纹理坐标在单个像素中从 1 跳到 0。从语义上讲,整个纹理在这个像素中得到平均是正确的,但这不是由在这个像素中将纹理从 1 插值到 0 造成的。真正的原因是 mipmapping。

对于您的纹理,在您加载它时会生成 mipmap。这意味着纹理获得多个 mipmap 级别,这些级别的大小都是之前的一半。

enter image description here

如果纹理变得扭曲,最高级别的采样将导致过采样(像伪像一样的点过滤)。为了对抗过采样,纹理查找根据屏幕空间中纹理坐标的变化选择适当的 mipmaplevel。在您的情况下,边界在一个小地方发生了非常大的变化,这导致尽可能使用最低的 mipmap(就像您看到一个小红点,这就是红色边界的原因)。

回到您的问题,您应该通过使用纹理查找方法 SampleGrad ( Docs ) 来控制 mipmapping。要获取像素纹理坐标的当前变化,您可以使用内部方法 ddxddy。它们返回着色器中的任意变量,它如何在局部更改为相邻像素(正确的解释将深入本主题)。所以使用下面的代码不应该改变任何东西,因为它在语义上应该是相同的:

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。这应该会移除您的工件。

如果您的纹理不需要 mipmap,因为您渲染的纹理屏幕对齐并且纹理大小不会导致过采样,您可以使用更简单的替代方法 sampleLevel ( Docs )在那里你可以传递一个参数,它会选择一个特定的 mipmap,你可以自己确定。

关于c++ - 如何手动包装纹理坐标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28445733/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com