【问题标题】:Using shaders to implement field of view on a 2D environment使用着色器在 2D 环境中实现视野
【发布时间】:2013-03-27 11:46:46
【问题描述】:

我正在实施动态视野。我决定使用着色器以使照明更好看以及它如何影响墙壁。这是我正在处理的场景: http://i.imgur.com/QxZVyo7.jpg

  1. 我有一张地图,有平坦的地板和墙壁。这里的一切都是 2d 的,没有 3d 几何,只有构成墙壁的 2d 多边形。

  2. 使用我投射阴影的多边形的顶点来定义可视区域。 (紫色线条是我下一步使用的蒙版的一部分)

  3. 在场景顶部绘制阴影时使用着色器,避免墙壁也被遮挡。

  4. 这样,随着视野的变化,阴影会沿着墙壁动态投射

我使用以下着色器来实现这一点。但我觉得这有点矫枉过正,而且效率真的很低:

uniform sampler2D texture;
uniform sampler2D filterTexture;
uniform vec2 textureSize;
uniform float cellSize;
uniform sampler2D shadowTexture;



void main()
{
    vec2 position;
    vec4 filterPixel;
    vec4 shadowPixel;
    vec4 pixel = texture2D(texture, gl_TexCoord[0].xy );


    for( float i=0 ; i<=cellSize*2 ; i++)
    {
        position = gl_TexCoord[0].xy;
        position.y = position.y - (i/textureSize.y);
        filterPixel = texture2D( filterTexture, position );

        position.y = position.y - (1/textureSize.y);
        shadowPixel = texture2D( texture, position );

        if (shadowPixel == 0){
            if( filterPixel.r == 1.0 )
            {
                if( filterPixel.b == 1.0 ){
                    pixel.a = 0;
                    break;
                }
                else if( i<=cellSize )
                {
                    pixel.a = 0;
                    break;
                }
            }
        }
    }

    gl_FragColor = pixel;
}

为了寻找掩码中的红色像素而对每个帧进行迭代似乎是一个巨大的过载,但我看不到如何使用着色器以任何其他方式完成此任务。

【问题讨论】:

  • 为什么要为此使用着色器?
  • 我使用的库是 SFML。我试图弄清楚如何读取图像上的像素信息(以便将点亮的像素上方的墙设置为可见),但操作非常缓慢,并且每个 cicle 都应该进行此过程,因此它并不真正可行。我遇到了着色器,并决定试一试。我喜欢结果,但我担心性能一点也不好,但我对着色器完全陌生,所以我想听听其他人对此的看法。

标签: opengl glsl shader


【解决方案1】:

这里的解决方案非常简单:使用阴影贴图。

您的情况可能是 2D 而不是 3D,但基本概念是相同的。您希望根据世界上某个点与“光源”(在您的情况下为玩家角色)之间是否存在障碍表面来“遮蔽”区域。

在 3D 中,阴影贴图的工作原理是从光源的角度渲染世界。这会产生一个 2D 纹理,其中的值表示从光(在特定方向)到最近的障碍物的深度。真实渲染场景时,通过将当前片段投影到 2D 深度纹理(阴影贴图)中来检查当前片段的位置。如果您为当前片段计算的深度值比阴影贴图中投影位置中最近的障碍物更近,则该片段在灯光下是可见的。如果不是,那就不是。

您的 2D 版本必须做同样的事情,只是少了一个维度。您从“光源”的角度渲染您的 2D 世界。在这种情况下,您的 2D 世界实际上只是阻碍四边形(您必须使用线多边形填充来渲染它们)。任何阻碍视线的四边形都应渲染到阴影贴图中。纹理访问是完全没有必要的;您需要的唯一信息是深度。您的着色器甚至不必编写颜色。您可以通过将 2D 空间投影到 1D 纹理来渲染这些对象。

这看起来像这样:

       X..X
XXXXXXXX..XXXXXXXXXXXXXXXXXXXX
X.............\.../..........X
X..............\./...........X
X...............C............X
X............../.\...........X
X............./...\..........X
X............/.....\.........X
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

C是角色的位置;这些点只是规则的,通畅的地板。 Xs 是墙壁。 C 中的线条代表您需要从中渲染 2D 线条的四个方向。

在 3D 中,要对点光源进行阴影贴图,您必须将场景在 6 个不同方向上渲染到立方体阴影贴图的面中 6 次。在 2D 中,您必须在 4 个不同的方向上将场景渲染 4 次,生成 4 个不同的 1D 阴影贴图。您可以为此使用一维数组纹理。

一旦您有了阴影贴图,您只需在着色器中使用它们来检测片段何时可​​见。为此,您需要将一组从窗口空间转换为 4 个不同的投影,这些投影代表您渲染到的 4 个视图方向。根据片段相对于目标的位置,只有其中一个将用于任何特定片段。

要实现这一点,我首先要让一个简单的定向“阴影”案例起作用。也就是说,不要使用位置;只是一个“光”的方向。这将测试您开发 2D 到 1D 投影矩阵的能力,以及将您的世界空间四边形转换为相机空间的适当相机空间矩阵的能力。一旦你掌握了这一点,你就可以用不同的预测来做 4 次。

【讨论】:

  • 我明白了,但是在确定应该显示墙壁的哪些部分时,看不到一维投影如何有用。我不应该对每个方向的每面墙进行投影以使它们正确吗?例如:i.imgur.com/CtuPu1t.jpg 红色矩形中的线是光/阴影的一维投影。但是该投影不适用于块 1 和 2,因为块 2 我完全可见,而块 1 大部分可见,但在投影中这些区域被标记为模糊......也许我的想法错了?
  • @user2215331:这不是二进制值;这是一个深度。您将获得 最近 遮挡表面的距离。来自块 1 的贡献将被父亲远离,因此块 1 和目标之间的任何片段都将被视为可见。
  • 我现在明白了!我只有一个问题。正如我所说,我是着色器的新手,并且几乎可以利用 GPU。我的问题是关于工作负载分布的:哪些任务由 GPU 完成,哪些由 CPU 正常处理?而对于 GPU 完成的任务,什么时候应该使用着色器?
  • @user2215331:这一切都是“由 GPU”完成的。您将需要使用 4 次单独的变换来渲染“阴影”场景 4 次。你必须渲染一次主场景,适当地使用阴影贴图。除了发出渲染命令、更改 FBO 和着色器等等之外,这里实际上并没有任何 CPU 工作。
  • 我在想......即使我设法得到 4 个投影......这如何帮助我掩盖隐藏的墙壁?由于墙壁的所有高度都应该被遮挡,我将使用 2D 表面。在下图中,两个坐标具有相同的 y 值,而右侧的坐标应被视为与其下方的紫色线具有相同的 Y 坐标,以便正确解释 1D 投影:i.imgur.com/fs8F2rN.gif跨度>
猜你喜欢
  • 1970-01-01
  • 2017-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 2017-12-18
  • 2013-01-12
相关资源
最近更新 更多