【问题标题】:Convert OpenGL HQX shader to LibGDX将 OpenGL HQX 着色器转换为 LibGDX
【发布时间】:2016-08-07 23:29:43
【问题描述】:

我正在研究 LibGDX 的着色器,并注意到有些属性只在 LibGDX 中使用。

来自https://github.com/libgdx/libgdx/wiki/Shaders 的标准顶点和片段着色器完美运行并应用于我的 SpriteBatch。

当我尝试使用像 https://github.com/SupSuper/OpenXcom/blob/master/bin/common/Shaders/HQ2x.OpenGL.shader 这样的 HQX 着色器时,我得到了很多错误。

可能是因为我需要将一些 LibGDX 因变量发送到着色器,但我不知道应该是哪个。

我想在具有大屏幕的桌面上使用这些着色器,以便游戏在这些屏幕上保持出色的外观。

我使用这段代码来加载着色器:

try { shaderProgram = new ShaderProgram(Gdx.files.internal("vertex.glsl").readString(), Gdx.files.internal("fragment.glsl").readString()); shaderProgram.pedantic = false; System.out.println("Shader Log:"); System.out.println(shaderProgram.getLog()); } catch(Exception ex) { }

着色器日志输出: No errors.

提前致谢。

【问题讨论】:

  • 你用它画的怎么样?您是构建网格还是使用 SpriteBatch?我假设您正在使用 FrameBuffer 中的纹理?
  • 我在我的 render() 方法中使用了batch.setShader(shaderProgram);,所以着色器被应用到 SpriteBatch,然后我绘制了我所有的纹理

标签: libgdx glsl shader


【解决方案1】:

这是一个后期处理着色器,所以你的流程应该是这样的:

  1. 使用 SpriteBatch 的默认着色器以像素完美分辨率将场景绘制到 FBO。

  2. 使用放大着色器将 FBO 的纹理绘制到屏幕的帧缓冲区。如果您修改着色器以匹配 SpriteBatch 使用的属性和制服,则可以使用 SpriteBatch 执行此操作。 (您也可以使用着色器期望的属性名称创建一个简单的网格,但 SpriteBatch 可能是最简单的。)

首先,我们没有使用带有 SpriteBatch 的典型着色器,因此您需要在加载任何内容之前在某处调用 ShaderProgram.pedantic = false;

现在您需要一个大小合适的 FrameBuffer。它的大小应该使您的精灵完美像素(纹理的一个像素缩放到世界的一个像素)。像这样的:

public void resize (int width, int height){
    float ratio = (float)width / (float) height;
    int gameWidth = (int)(GAME_HEIGHT / ratio);
    boolean needNewFrameBuffer = false;
    if (frameBuffer != null && (frameBuffer.getWidth() != gameWidth || frameBuffer.getHeight() != GAME_HEIGHT)){
        frameBuffer.dispose();
        needNewFrameBuffer = true;
    }
    if (frameBuffer == null || needNewFrameBuffer)
        frameBuffer = new FrameBuffer(Format.RGBA8888, gameWidth, GAME_HEIGHT);

    camera.viewportWidth = gameWidth;
    camera.viewportHeight = GAME_HEIGHT;
    camera.update();
}

然后,您可以将其绘制到帧缓冲区,就好像它是您的屏幕一样。之后,将帧缓冲区的纹理绘制到屏幕上。

public void render (){
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    frameBuffer.begin();
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    batch.setProjectionMatrix(camera.combined);
    batch.setShader(null); //use default shader
    batch.begin();
    //draw your game
    batch.end();

    frameBuffer.end();

    batch.setShader(upscaleShader);
    batch.begin();
    upscaleShader.setUniformf("rubyTextureSize", frameBuffer.getWidth(), frameBuffer.getHeight());//this is the uniform in your shader. I assume it's wanting the scene size in pixels
    batch.draw(frameBuffer.getColorBufferTexture(), -1, 1, 2, -2); //full screen quad for no projection matrix, with Y flipped as needed for frame buffer textures
    batch.end();
}

您还需要对着色器进行一些更改,以便它可以与 OpenGL ES 一起使用,并且因为 SpriteBatch 已连接到特定属性和统一名称:

在您的顶点着色器顶部,添加它以定义您的顶点属性和变量(您的链接着色器不需要这些,因为它依赖于 GL ES 中不可用的内置变量):

attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord[5];

然后在顶点着色器中,将gl_Position这一行改为

gl_Position = a_position; //since we can't rely on built-in variables

出于同样的原因,将所有出现的gl_TexCoord 替换为v_texCoord

在片段着色器中,要与 OpenGL ES 兼容,您需要声明精度。您还需要声明相同的变量,因此将其添加到顶部:

#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord[5];

与顶点着色器一样,将所有出现的gl_TexCoord 替换为v_texCoord。并且还将所有出现的rubyTexture 替换为u_texture,这是SpriteBatch 使用的纹理名称。

我认为这就是一切。我实际上并没有对此进行测试,而且我的内存不足,但希望它能让你接近。

【讨论】:

  • 感谢您的帮助,我得到了像 pastebin.com/88rmNgVf 这样的着色器并且不再出现错误,但我得到的只是黑屏,您可以测试一下吗?
  • 假设您按照我的方式将四边形绘制到屏幕上,您需要从着色器中删除 u_projTrans,或者将 SpriteBatch 的投影矩阵设置为标识。这是我最初看到的唯一问题。
  • 着色器现在可以工作了,但是除了整个屏幕被着色之外,纹理仍然彼此分开着色,这可能是因为 TiledMapRenderer。整个目的是屏幕上渲染的所有东西都需要一个纹理,我设法做到了,而那个纹理需要一个着色器。我认为这是因为 TiledMap 被跳过了。
  • 您是否使用默认着色器绘制到 FrameBuffer,然后将其绘制到屏幕上?
  • 它现在可以工作了,着色器没有将 Alpha 设置为 1.0,所以纹理不会得到 Alpha 混合,谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-05-07
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
相关资源
最近更新 更多