1

我已经编写了一个基本的 2d 像素着色器,但我似乎无法让它工作。如果我在激活效果的情况下进行绘制,那么屏幕上不会出现任何内容。但是如果我禁用它,那么纹理会按预期绘制到屏幕上。

我的目标是能够在屏幕上绘制任意纹理,然后让这个像素着色器“雕刻”出圆形像素块,用于范围等的覆盖系统。

这是我的像素着色器代码:

sampler TextureSampler : register(s0);
//A list of positions for circles. They are specified in texture space rather than screen space.
float2 PositionData[64];
//A matching list of radiuses. These are specified in pixels, though.
float Radii[64];
//how much of the array is filled with data.
int DataSize;
//the size of the texture being drawn.
float2 TextureSize;

float4 RenderSolidCircles(float2 texCoord : TEXCOORD0) : COLOR
{
    float opacityAcc = 1;
    float2 screenSpaceTexCoord = texCoord * TextureSize;
    for (int i = 0; i < DataSize; i++)
    {
        float2 properPosCoordinate = PositionData[i] * TextureSize;
        float dist = length(screenSpaceTexCoord - properPosCoordinate) - Radii[i];
        if (dist < 0)
        {
            opacityAcc -= min(abs(dist), 1);
        }
    }
    opacityAcc = max(0, opacityAcc);
    float4 outPix = tex2D(TextureSampler, texCoord);
    outPix.a *= opacityAcc;
    return outPix;
}


technique SolidCircles
{
    pass P0
    {
        PixelShader = compile ps_3_0 RenderSolidCircles();
    }
}

float4 PassThrough(float2 texCoord : TEXCOORD0) : COLOR
{
    return tex2D(TextureSampler, texCoord);
}
technique PassThrough
{
    pass P0
    {
        PixelShader = compile ps_3_0 PassThrough();
    }
}

这是 SolidCircles 技术的 ASM 版本:

//
// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000
//
// Parameters:
//
//   int DataSize;
//   float2 PositionData[64];
//   float Radii[64];
//   sampler2D TextureSampler;
//   float2 TextureSize;
//
//
// Registers:
//
//   Name           Reg   Size
//   -------------- ----- ----
//   PositionData   c0      64
//   Radii          c64     64
//   DataSize       c128     1
//   TextureSize    c129     1
//   TextureSampler s0       1
//
//
// Default values:
//snipped comments here

    ps_3_0
    def c130, 1, 0, -1, 2
    def c131, 3, 4, 5, 6
    def c132, 7, 8, 9, 10
    def c133, 11, 12, 13, 14
    def c134, 15, 16, 17, 18
    def c135, 19, 20, 21, 22
    def c136, 23, 24, 25, 26
    def c137, 27, 28, 29, 30
    def c138, 31, 32, 33, 34
    def c139, 35, 36, 37, 38
    def c140, 39, 40, 41, 42
    def c141, 43, 44, 45, 46
    def c142, 47, 48, 49, 50
    def c143, 51, 52, 53, 54
    def c144, 55, 56, 57, 58
    def c145, 59, 60, 61, 62
    def c146, 63, 0, 0, 0
    dcl_texcoord v0.xy  // texCoord<0,1>
    dcl_2d s0

#line 22 "C:\Users\RCIX\Documents\Visual Studio 2008\Projects\2DFXFilesTest\2DFXFilesTest\Content\OverlayFx.fx"
    mov r0.w, c130.x  // opacityAcc<0>
    mul r2.xy, v0, c129  // screenSpaceTexCoord<0,1>
    mov r5.w, -c128.x
    add r0.z, r5.w, c130.y
    cmp r12.w, r0.z, c130.y, c130.x
    mul r11.w, r12.w, c130.x
    if_ne r11.w, -r11.w
      mov r13.xy, c129  // ::TextureSize<0,1>
      mul r12.xy, r13, c0  // properPosCoordinate<0,1>
      mov r12.xy, -r12
      add r11.xy, r2, r12
      mul r16.xy, r11, r11
      add r11.z, r16.x, r16.y
      rsq r10.w, r11.z
      rcp r8.w, r10.w
      mov r9.w, -c64.x
      add r4.w, r8.w, r9.w  // dist<0>
      add r7.w, r4.w, c130.y
      cmp r6.w, r7.w, c130.y, c130.x
      mov r3.w, -r4.w
      mov r5.z, -r3.w
      add r1.w, r4.w, r5.z
      cmp r15.w, r1.w, r4.w, r3.w
      add r14.w, r15.w, c130.z
      cmp r2.w, r14.w, c130.x, r15.w
      mov r2.w, -r2.w
      add r13.w, r2.w, c130.x  // opacityAcc<0>
      mov r6.w, -r6.w
      cmp r0.w, r6.w, r0.w, r13.w  // opacityAcc<0>

#line 24
    endif

//snipped 63 blocks of unrolled loop code

#line 33
    mov r1.w, -r0.w
    add r15.w, r1.w, c130.y
    cmp r14.w, r15.w, c130.y, r0.w  // opacityAcc<0>
    texld r0, v0, s0  // outPix<0,1,2,3>
    mul r2.x, r14.w, r0.w  // outPix<3>
    mov oC0.xyz, r0  // ::RenderSolidCircles<0,1,2>
    mov oC0.w, r2.x  // ::RenderSolidCircles<3>

// approximately 1866 instruction slots used (1 texture, 1865 arithmetic)

这是我的 Game 类中 Draw 函数的相关部分:

GraphicsDevice.Clear(Color.CornflowerBlue);
overlayEffect.Parameters["PositionData"].SetValue(Positions.ToArray());
overlayEffect.Parameters["Radii"].SetValue(Radii.ToArray());
overlayEffect.Parameters["DataSize"].SetValue(64);
overlayEffect.Parameters["TextureSize"].SetValue(new Vector2(500));

spriteBatch.Begin(SpriteBlendMode.AlphaBlend,
    SpriteSortMode.Immediate,
    SaveStateMode.None);

overlayEffect.Begin();
overlayEffect.CurrentTechnique.Passes[0].Begin();

spriteBatch.Draw(pixTex, new Rectangle(0, 0, 500, 500), Color.White);
spriteBatch.End();

overlayEffect.CurrentTechnique.Passes[0].End();
overlayEffect.End();

base.Draw(gameTime);

最后,这是我构建位置和半径列表的函数:

private void RebuildPositionsList()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    Positions = new List<Vector2>();
    Radii = new List<float>();
    for (int i = 0; i < 64; i++)
    {
        Positions.Add(
            new Vector2(
                (float)r.NextDouble(),
                (float)r.NextDouble())
                );
        Radii.Add(((float)r.NextDouble() * 100) + 40);
    }
}

构成我纹理的线条:

pixTex = new Texture2D(GraphicsDevice, 1, 1);
pixTex.SetData<Color>(new Color[] { new Color(0f, 0f, 0f, 1f) });

PositionsRadii分别是向量列表和浮点数,大小为 64。 pixTex 是一个 1 像素纯黑色纹理。

为什么着色器不起作用?

4

3 回答 3

2

因此,在此答案的评论中,确定这里的另一个问题是 XNA 3.1 中 Sprite Batch 的顶点着色器是 vs_1_1。严格来说,您不能将 SM 3.0 顶点或像素着色器与不同版本的着色器混合使用。看来,在实践中,大多数显卡都可以让你侥幸逃脱,但显然 RCIX 的显卡(Radeon HD 4850)不会。

(这就是为什么手头有 DirectX 调试运行时(来自SDK )是值得的,因为它们会警告你这样的事情。你可以使用DebugView来查看它的输出。)

这个问题有多种解决方案:

1)到目前为止,最简单的解决方案是升级到 XNA 4.0(缺点是重大更改以及它目前仅处于测试阶段的事实)。在这个版本的 XNA 中,您可以轻松地为 SpriteBatch 指定您自己的顶点着色器

2)您可以在 XNA 3.1 中使用带有 SpriteBatch 的自定义顶点着色器,但这并不容易。一个好的起点是XNA 使用的顶点着色器的源代码(直到 XNA 4.0,见上文)。

3)最后:也许你可以让你的着色器使用 ps_2_0?每个纹理真的需要 64 个切口吗?

于 2010-07-24T02:23:15.103 回答
1

在打电话之前effect.End()你需要打电话spriteBatch.End()

其原因(以及它在 XNA 4.0 中被“修复”的事实)在 Shawn Hargreaves 博客上的这篇文章中进行了描述。(这篇文章可能也值得一读。)

基本上(在 XNA 3.1 中):SpriteSortMode.Immediate并不像您期望的那样直接。在结束效果之前,您需要调用spriteBatch.End()以将最后一批精灵实际推送到 GPU 。

Sprite Effects 示例展示了如何正确地将效果应用到 sprite 。

于 2010-07-21T06:40:07.363 回答
1

好的,首先你需要在我的另一个答案中应用这两个修复(在结束效果之前结束批处理,并且(来自评论)使用SpriteBlendMode.AlphaBlend)。

现在 - 你的着色器中的问题。它实际上正在工作 - 但可能不是您期望的方式。您似乎混淆了您的精灵在(宽度 = 500,高度 = 500)处绘制的屏幕空间坐标与像素着色器工作的纹理空间坐标(宽度 = 1,高度 = 1)。

所以首先 - 当你在你的精灵上切洞时,你需要在纹理空间中进行,如下所示:

Positions.Add(new Vector2(0.5f, 0.5f));
Radii.Add(0.25f);

Positions.Add(new Vector2(0.25f, 0.25f));
Radii.Add(0.1f);

其次,看起来像抗锯齿的尝试会导致您的剪裁更像是淡出。它需要考虑纹理在屏幕上绘制时的大小。最简单的解决方法是更改​​此行:

opacityAcc -= min(abs(dist), 1);

对此:

opacityAcc -= min(abs(dist * 500), 1);

当然 - 这假设您的精灵是在 500 x 500 处绘制的。您应该将实际值作为着色器参数传递。

如果您要绘制非正方形的精灵,那么您需要做一些额外的数学运算以使坐标系“对齐”。我会把它留作练习。

于 2010-07-22T06:28:25.073 回答