代码之家  ›  专栏  ›  技术社区  ›  RCIX

xna+像素着色困难

  •  1
  • RCIX  · 技术社区  · 15 年前

    我已经编写了一个基本的二维像素着色程序,但似乎无法使其正常工作。如果我画的效果是激活的,那么屏幕上就没有任何东西。但是如果我禁用它,那么纹理会按预期绘制到屏幕上。

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

    这是我的像素着色代码:

    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)
    

    这是我在游戏课上的抽签功能的相关部分:

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

    Positions Radii 分别是向量和浮点的列表,大小为64。Pixtex是一种1像素的纯黑纹理。

    为什么明暗器不工作?

    3 回复  |  直到 12 年前
        1
  •  2
  •   Community Mohan Dere    8 年前

    所以在评论中 this answer 决定 另一种 这里的问题是xna 3.1中sprite批处理的顶点着色是vs_1_1。和 strictly speaking 不能将SM 3.0顶点或像素明暗器与不同版本的明暗器混合。实际上,大多数卡都能让你逃脱惩罚,但很明显,RCIX的卡(Radeon HD 4850)不会。

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

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

    1) 到目前为止,最简单的解决方案是升级到XNA 4.0(缺点是有突破性的变化,而且它目前只在测试版中)。在这个版本的XNA中,您可以 easily specify your own vertex shader for SpriteBatch .

    2) 在XNA3.1中,您可以使用带有spriteBatch的自定义顶点明暗器,但这并不容易。一个好的起点是 source code for the vertex shader used by XNA (直至XNA 4.0,见上文)。

    3) 最后:也许你可以让你的明暗器使用ps_2_0?你真的需要每个纹理64个裁剪吗?

        2
  •  1
  •   Andrew Russell    15 年前

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

    原因(以及XNA 4.0中“固定”的事实)在 this article on Shawn Hargreaves' blog . ( This entry 可能也值得一读。)

    基本上(XNA 3.1中): SpriteSortMode.Immediate 并不像你想象的那么直接。你需要打电话 spriteBatch.end()。 把最后一批雪碧推到GPU上, 之前 你结束了你的效果。

    这个 Sprite Effects sample 演示如何正确地对精灵应用效果。

        3
  •  1
  •   Community Mohan Dere    8 年前

    好的,首先你需要在我的 other answer (在结束效果之前结束批,并(从注释中)使用 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乘500。您应该将实际值作为一个明暗器参数传入。

    如果你要画一个非正方形的雪碧,那么你需要做一些额外的数学来使坐标系“成一条直线”。我把它留作练习。