是否可以在不使用FrameBuffer的情况下应用扫描线着色器?

时间:2016-07-27 18:01:58

标签: libgdx glsl

我正在寻找一个与libGDX一起使用的最小扫描线着色器,最好选择改变效果的强度。

这里有一个libGDX示例(缺少vert和frag文件):

Shaders in libgdx have no effect [Desktop]

但是,这需要使用FrameBuffer。有没有更优雅的解决方案,我可以将vert和frag文件放入我的着色器文件夹,然后在我的代码中设置如下:

private String vertexShader;
private String fragmentShader;
private ShaderProgram shaderProgram;

@Override
public void create()
{
  vertexShader = Gdx.files.internal("shaders/vertex.glsl").readString();
  fragmentShader = Gdx.files.internal("shaders/fragment.glsl").readString();
  shaderProgram = new ShaderProgram(vertexShader, fragmentShader);
  spriteBatch.setShader(shaderProgram);
}

我的游戏针对的是低端Android手机。我目前得到一个相当稳定的60fps,并希望保持这种表现。

编辑1: 在Tenfour04的片段之后,我的顶点文件目前看起来像这样:

attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;

uniform mat4 u_projTrans;
uniform float u_screenHeight;

varying vec4 v_color;
varying vec2 v_texCoords;

void main()
{
  v_color = a_color;
  v_texCoords = a_texCoord0;
  gl_Position =  u_projTrans * a_position;
}

编辑2: 新的,可能更简单的方法,但渲染失去透明度:

#ifdef GL_ES
  precision mediump float;
#endif

varying vec4 v_color;
varying vec2 v_texCoords;

uniform sampler2D u_sampler2D;
uniform mat4 u_projTrans;

void main(void)
{
  vec2 p = vec2(floor(gl_FragCoord.x), floor(gl_FragCoord.y));
  if (mod(p.y, 2.0)==0.0)
    gl_FragColor = vec4(texture2D(u_sampler2D,v_texCoords).xyz ,1.0);
  else
    gl_FragColor = vec4(0.0,0.0,0.0 ,1.0);
}

2 个答案:

答案 0 :(得分:1)

您可以保持方法不变,仍然保持透明。

这是片段着色器中的main():

        string filePath = @"C:\Folder\file.txt";
        StreamReader sr = new StreamReader(filePath);
        string s = sr.ReadToEndAsync().GetAwaiter().GetResult();
        Console.WriteLine(s);

有了这个,我有了透明的纹理显示便宜的扫描线。请注意,我使用4.0作为除数,只是个人喜好。

无论如何,关键是只能修改gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates); if (mod(floor(gl_FragCoord.y),4.0) == 0.0) gl_FragColor.rgb *= 0.5; 并保留'gl_FragColor.a`(alpha通道)。

我也正在研究快速而肮脏的扫描线效果,并在搜索gl_FragColor.rgbgl_FragCoord.y之后到达了这个问题。您问题中的mod解决了我的问题。

答案 1 :(得分:0)

这是我认为可行的着色器。我没有测试它,所以你可能需要调试它。您可以自定义线数和强度常数以获得所需的外观。这非常简单地基于正弦曲线,并且它仅导致变暗。通过使用正弦波使颜色变亮和变暗,您可以做更精细的效果。您还可以使用步进函数截断正弦函数,以增加其真实性。

std::rotate
//Vertex shader (same as SpriteBatch's default)
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;

varying vec2 v_texCoords;
varying vec4 v_color;

uniform mat4 u_projTrans;

void main()
{
    v_texCoords = a_texCoord0;
    v_color = a_color;
    v_color.a = v_color.a * (255.0/254.0);
    gl_Position =  u_projTrans * a_position;
}

它使用屏幕高度作为参数,因此您必须在调整屏幕大小时设置屏幕高度:

//Fragment shader
#ifdef GL_ES
    precision mediump float;
#endif

const float LINE_COUNT = 90.0;
const float FREQ = LINE_COUNT * 2.0 * 3.14159;
const float INTENSITY = 0.4;

varying vec2 v_texCoords;
varying vec3 v_color;

uniform sampler2D u_texture;
uniform float u_screenHeight;

void main()
{
    vec4 texture = texture2D(u_texture, v_texCoords);
    float screenV = gl_FragCoord.y / u_screenHeight;
    float scanLine = 1.0 - INTENSITY * (sin(FREQ * screenV) * 0.5 + 0.5);

    //Possibly cheaper methods, in increasing realism / performance hit
    //float scanLine = 1.0 - INTENSITY * mod(screenV * LINE_COUNT, 1.0);
    //float scanLine = 1.0 - INTENSITY * step(0.5, mod(screenV * LINE_COUNT, 1.0));
    //float scanLine = 1.0 - INTENSITY * abs(mod(screenV * LINE_COUNT, 1.0) - 0.5);

    gl_FragColor = v_color * vec4(texture.rgb * scanLine, texture.a);
}

如果您使用我上面注释的public void resize (int width, int height){ //... shader.begin(); shader.setUniformf("u_screenHeight", height); shader.end(); } 扫描线计算而不是基于mod()的计算,您可以通过将其更改为:

进一步优化
sin()

并将着色器的shader.setUniformf("u_screenHeight", LINE_COUNT / (float)height); screenV更改为/,然后从scanLine计算中删除*。这样可以节省操作,而且我认为*比/稍快一些。 (如果这样做,您可以考虑将u_screenHeight变量重命名为有意义的变量。)