使用全屏四边形对渲染目标进行完美像素渲染


9

将一堆值渲染到rendertarget时遇到一些麻烦。这些值永远不会在我想要它们的确切范围内。基本上,我使用全屏四边形和像素着色器渲染到我的rendertarget纹理,然后打算将纹理坐标用作着色器中某些计算的基础。四边形的纹理坐标范围从左上角的(0,0)到右下角的(1,1)...问题是,插值后,这些值没有像这样到达像素着色器。

一个示例: 我渲染为4x4纹理,在这种情况下,着色器仅在红色和绿色通道中输出纹理坐标(u,v):

return float4(texCoord.rg, 0, 1);

我要摆脱的是一种纹理,其中左上角的像素是RGB(0,0,0),因此是黑色,右上角的像素是RGB(255,0,0),因此是明亮的红色,左下角的像素为RGB(0,255,0)-亮绿色。

但是,相反,我在这里得到此:

(直四边形渲染,无校正)
四边形的直接渲染,不应用校正

左上方的像素是黑色,但在其他角落我只能得到相对深红色和深绿色。它们的RGB值为(191,0,0)和(0,191,0)。我强烈怀疑它与四边形的采样位置有关:左上像素正确地对四边形的左上角进行采样,并获得(0,0)作为UV坐标,但其他角点像素未从中采样四边形的其他角。我已经在左图中说明了这一点,蓝色框代表四边形,白色点代表上采样坐标。

现在,我知道在Direct3D9中渲染屏幕对齐的四边形时应将半像素偏移应用于坐标。让我们看看我从中得到什么样的结果:

(以DX9的半像素偏移量渲染的四进制)
四分之一像素偏移渲染

红色和绿色变亮,但仍然不正确:223是我在红色或绿色通道上获得的最大值。但是现在,我什至没有纯黑色,而是带有RGB(32,32,0)的深黄灰色!

我真正需要的是这种渲染:

(目标渲染,减小的四边形尺寸)
具有正确插值的纯黑色,绿色和红色

与第一个图形相比,看起来我必须将四边形的右边界和底边界恰好向左移动一个像素。然后,像素的右列和底行都应正确地从四边形的边界正确获取UV坐标:

VertexPositionTexture[] quad = new VertexPositionTexture[]
{
    new VertexPositionTexture(new Vector3(-1.0f,  1.0f, 1f), new Vector2(0,0)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X,  1.0f, 1f), new Vector2(1,0)),
    new VertexPositionTexture(new Vector3(-1.0f, -1.0f + pixelSize.Y, 1f), new Vector2(0,1)),
    new VertexPositionTexture(new Vector3(1.0f - pixelSize.X, -1.0f + pixelSize.Y, 1f), new Vector2(1,1)),
};

但是,这还没有完全解决,导致底部和右侧像素根本无法渲染。我想这些像素的中心不再被四边形覆盖,因此不会被着色器处理。如果我将pixelSize的计算量更改为微小的量,以使四边形稍大一些,则至少在4x4纹理上有效。它不适用于较小的纹理,并且我担心它会巧妙地扭曲uv值在较大纹理上的均匀分布:

Vector2 pixelSize = new Vector2((2f / textureWidth) - 0.001f, (2f / textureHeight) - 0.001f);

(我将pixelSize的计算值修改为0.001f-对于较小的纹理,例如1D查找表,此方法无效,我必须将其增大到0.01f或更大的值)

当然,这是一个简单的示例,我可以在CPU上更轻松地执行此计算,而不必担心将UV映射到像素中心……不过,仍然必须有一种方法可以实际渲染完整,完整的[0, 1]在rendertarget上的像素范围!


并不是说有什么帮助,但是我遇到了完全相同的问题。
IUsedToBeAPygmy 2012年

1
别忘了禁用线性采样。
r2d2rigo 2013年

它看起来不像您使用的坐标系匹配屏幕的像素,如果匹配,则看起来您只在这里绘制2×2像素。我要解决的第一个问题是缩放项目和Modelview矩阵,使坐标空间中的+1偏移屏幕上1个像素。
Slipp D. Thompson

Answers:


4

您的问题是UV设计为纹理坐标。坐标0,0是纹理中左上像素的左上角,这不是您通常要读取的纹理。对于2D,您要读取该像素中间的纹理。

如果我的数学正确,那么您需要做的工作是:

return float4((texCoord.rg - (0.5f/textureSize)) * (textureSize/(textureSize-1)), 0, 1);

也就是说,您减去适当的偏移量即可获得0,0处的左上方像素,然后应用比例尺以使右下方的像素正确。

通过将几何图形的UV坐标扩展到0-1范围之外,也可以实现相同的目的。


详细说明最后一句话:您可以将此变换应用于四边形四个角的顶点缓冲区中的UV,然后在像素着色器中执行return float4(texCoord.rg, 0, 1);
内森·里德

我尝试了上面建议的公式,但效果并不理想:在上面的4x4样本rendertarget中,我在R和G通道中从左到右或从上到下分别得到0、42、127和212。但是,我希望等距间隔的值介于0到255之间(对于4x4纹理,其值为0、85、170和255)。我还尝试更改UV坐标,但尚未找到正确的偏移量。
马里奥(Mario)2012年

0

与第一个图形相比,看起来我必须将四边形的右边界和底边界恰好向左移动一个像素。

几乎,但只有像素的一半。只需从四边形顶点减去0.5。例如,您想要具有相同纹理的256x256四边形,这是您的要点:

p1 = (-0.5, -0.5)
p2 = (-0.5, 255-0.5)
p3 = (255-0.5, 255-0.5)
p4 = (255-0.5, -0.5)

或者,您也可以在像素着色器中添加一半的纹理像素(texCoord.rg + 0.5 *(1.0 / texSize))

半像素偏移是DX9中的已知事实。这篇文章也可能对您有帮助。


0

这肯定是由线性采样引起的。如果您查看全黑左上像素下方和右侧的纹理像素,您会发现它们在R和/或G通道中有63个,其中2个在B中有2个。浑浊的深黄色 在R和G中为31,在B中为1。这绝对是在2x2组上平均纹理像素的结果,因此解决方案是将纹理滤镜设置为指向。


-2

这仅仅是因为您正在使用来自顶点输出的texCoords,该顶点输出是在处理顶点着色器后由硬盘驱动器插入的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.