【问题标题】:Fade texture inner borders to transparent in LibGDX using openGL shaders (glsl)使用 openGL 着色器 (glsl) 在 LibGDX 中将纹理内边框淡化为透明
【发布时间】:2022-08-16 13:03:25
【问题描述】:

我目前正在 LibGDX 中开发一款瓷砖游戏,并且试图通过遮盖未开发的瓷砖来获得“战争迷雾”效果。我从中得到的结果是动态生成的屏幕大小的黑色纹理,它只覆盖未探索的瓷砖,而其余的背景可见。这是在白色背景上渲染的雾纹理示例:

我现在想要实现的是动态淡化这个纹理的内部边界,使它看起来更像是一个慢慢变厚的雾,而不是一堆黑盒子放在背景上。

谷歌搜索问题我发现我可以使用着色器来做到这一点,所以我尝试学习一些 glsl (我从着色器开始),我想出了这个着色器:

顶点着色器:

//attributes passed from openGL
attribute vec3 a_position;
attribute vec2 a_texCoord0;

//variables visible from java
uniform mat4 u_projTrans;

//variables shared between fragment and vertex shader
varying vec2 v_texCoord0;

void main() {

    v_texCoord0 = a_texCoord0;
    gl_Position = u_projTrans * vec4(a_position, 1f);
}

片段着色器:

//variables shared between fragment and vertex shader
varying vec2 v_texCoord0;

//variables visible from java
uniform sampler2D u_texture;
uniform vec2 u_textureSize;
uniform int u_length;

void main() {

    vec4 texColor = texture2D(u_texture, v_texCoord0);
    vec2 step = 1.0 / u_textureSize;

    if(texColor.a > 0) {

        int maxNearPixels = (u_length * 2 + 1) * (u_length * 2 + 1) - 1;
        for(int i = 0; i <= u_length; i++) {

            for(float j = 0; j <= u_length; j++) {

                if(i != 0 || j != 0) {

                    texColor.a -= (1 - texture2D(u_texture, v_texCoord0 + vec2(step.x * float(i), step.y * float(j))).a) / float(maxNearPixels);
                    texColor.a -= (1 - texture2D(u_texture, v_texCoord0 + vec2(-step.x * float(i), step.y * float(j))).a) / float(maxNearPixels);
                    texColor.a -= (1 - texture2D(u_texture, v_texCoord0 + vec2(step.x * float(i), -step.y * float(j))).a) / float(maxNearPixels);
                    texColor.a -= (1 - texture2D(u_texture, v_texCoord0 + vec2(-step.x * float(i), -step.y * float(j))).a) / float(maxNearPixels);
                }
            }
        }
    }

    gl_FragColor = texColor;
}

这是我设置长度为 20 的结果:

所以我写的着色器有点工作,但性能很差,因为它是 O(n^2),其中 n 是以像素为单位的淡入淡出的长度(所以它可以非常高,比如 60 甚至 80)。它也有一些问题,比如边缘仍然有点太尖锐(我想要一个令人窒息的过渡),并且边框的一些角度比其他角度更少褪色(我想要一个淡化制服到处)。

在这一点上我有点迷茫:我能做些什么来让它变得更好更快?就像我说的我是着色器的新手,所以:它甚至是使用着色器的正确方法吗?

  • 它总是居中吗?也许你可以做一个径向模糊?
  • 边框是否与游戏的图块网格对齐?也许您可以将其渲染为每个图块 1 个像素的纹理,然后免费获得 1 个图块的模糊值,您还可以在每个着色器循环中执行 1 个整个图块,而不仅仅是 1 个像素。注意:如果你每像素做 1 个平铺,则纹理像素必须相对于屏幕像素进行旋转和拉伸,因此你必须计算出纹理坐标
  • @vtastek 雾可以有各种形状,并不总是居中,所以我不能使用径向模糊
  • 也许您可以使用根本不模糊的技巧,而只是使用淡入淡出的纹理,您可以在所有边界上渲染它​​。
  • @SirNiculino 每个采样点都将被双线性插值,因为这就是 GPU 在像素之间采样时对纹理所做的事情(假设您使用 GL_LINEAR 创建纹理)。因此,瓦片中的一个采样点会告诉您边缘有多远。

标签: opengl libgdx glsl shader fade


【解决方案1】:

正如 cmets 中的其他人所提到的,您应该在平铺空间中进行过滤,同时潜在地利用 GPU 双线性过滤,而不是在屏幕空间中进行模糊处理。让我们通过图像来了解它。

首先定义一个纹理,使每个像素对应一个单独的瓦片,黑/白取决于该瓦片上的雾。这是一个被炸毁的纹理:

在应用屏幕到瓷砖坐标变换并使用GL_NEAREST 插值对该纹理进行采样后,我们得到类似于您所拥有的块状结果:

float t = texture2D(u_tiles, M*uv).r;
gl_FragColor = vec4(t,t,t,1.0);

如果我们切换到GL_LINEAR 而不是GL_NEAREST,我们会得到更好的结果:

这看起来仍然有点块状。为了改进这一点,我们可以申请smoothstep

float t = texture2D(u_tiles, M*uv).r;
t = smoothstep(0.0, 1.0, t);
gl_FragColor = vec4(t,t,t,1.0);

或者这里有一个带有线性阴影映射功能的版本:

float t = texture2D(u_tiles, M*uv).r;
t = clamp((t-0.5)*1.5 + 0.5, 0.0, 1.0);
gl_FragColor = vec4(t,t,t,1.0);

注意:这些图像是在伽马校正管道中生成的(即启用 sRGB 帧缓冲区)。然而,这是少数几个场景之一,忽略 gamma 可能会产生更好的结果,因此欢迎您进行实验。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-20
    • 2022-09-16
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多