【问题标题】:LibGDX - How do I render two textures with different scales in a single shader?LibGDX - 如何在单个着色器中渲染两个具有不同比例的纹理?
【发布时间】:2014-03-14 19:52:11
【问题描述】:

我正在尝试以与另一个纹理不同的比例缩放在 FBO 中生成的纹理,并在片段着色器中同时渲染这两个纹理,以便我可以将它们混合在一起。

我按照this tutorial 为我的游戏制作了一个光照贴图,我得到了一个完美运行的光照贴图,但我想在着色器中转换光照贴图,以便背景中的视差对象接收按比例缩放的光照贴图,以便它们的到光源的“3D”距离似乎不会影响它们的照明,但它们的 2D 距离会。

对于如何做到这一点,我最好的猜测是根据视差对象的 Z 距离在适当的位置缩放光照贴图。我游戏中的 Z 距离与物体到相机的 2D 距离成正比。

以下示例描述了常规光照贴图发生的情况以及所需的效果。此处的光照贴图由黄色径向渐变组成,该渐变位于右侧的黄色非视差“太阳”:

Description of lightmap scaling

Animated example of current lighting

Animated example of intended lighting

通常在我的游戏中,我通过在 LibGDX 中通过类似这样的方式缩放连接到 SpriteBatch 的相机来缩放视差对象:

Array<Something> objects = new Array<Something>();
SpriteBatch batch = new SpriteBatch();
OrthographicCamera camera = new OrthographicCamera(screenWidth, screenHeight);

//{start loop, add objects, run logic, etc.}
...

for(int i=0;i<objects.size;i++){
   camera.zoom = objects.get(i).z;
   camera.update();
   batch.setProjectionMatrix(camera.combined);
   objects.get(i).sprite.draw(batch);
}

根据我的研究,应该有一种方法可以在顶点着色器中进行此类转换。通过默认的 LibGDX 顶点着色器可以直接进行这样的转换,该着色器设置为将顶点位置与相机的转换矩阵相乘,如下所示:

void main() {
    v_color = a_color;
    v_texCoords = a_texCoord0; 

    gl_Position =  u_projTrans * a_position; 
}

但我不确定当变换绑定到共享顶点位置时,如何仅缩放片段着色器中使用的两个纹理之一。作为参考,这里是教程中实现的片段着色器:

void main() {
    vec4 diffuseColor = texture2D(u_texture, v_texCoords);

    vec2 lightCoord = (gl_FragCoord.xy / resolution.xy);
    vec4 light = texture2D(u_lightmap, lightCoord);

    vec3 ambient = ambientColor.rgb * ambientColor.a;
    vec3 intensity = ambient + light.rgb;
    vec3 finalColor = diffuseColor.rgb * intensity;

    gl_FragColor = v_color * vec4(finalColor, diffuseColor.a);
}

我认为这与转换纹理坐标而不是位置并将它们传递给光照贴图而不是使用屏幕分辨率来获取纹理坐标有关,但是我这样做的尝试最终变得一团糟,因为我对 OpenGL ES 2 或 GLSL 不是很了解或经验丰富。

有没有一种很好的方法可以在两个纹理片段着色器中缩放一个纹理而不是另一个?或者有没有更好的方法来实现我想要用光照贴图做的事情?

编辑:

所以我可以通过为每个视差对象以不同比例重新创建 FBO 光照贴图来完成我想要做的事情,但这会给我的目标平台之一(Android)带来巨大的性能问题。

我发现 some links 表明我正在尝试做的事情是可能的,但它们都描述了将两个不同的纹理坐标传递给顶点着色器,这似乎不适用于 SpriteBatch。是否可以在顶点着色器中转换纹理坐标,以便将光照贴图的转换坐标传递给片段着色器?还是我误解了纹理坐标的工作原理?

【问题讨论】:

    标签: java opengl-es-2.0 libgdx


    【解决方案1】:

    首先,以防万一我假设Something 代表单个精灵是正确的:每次调用batch.setProjectionMatrix() 时,精灵批次都会被刷新,导致另一个绘图调用,这将是太多如果你有超过几十个精灵。使用您将对象数组发送到精灵批处理的方法,您应该让它们按 z 位置排序,然后仅在自上次调用 batch.draw() 后 z 已更改时才调用 batch.setProjectionMatrix()。我个人会为每个视差层保留一个单独的对象数组,以保持简单而不用担心排序。

    从那里开始: 首先,在绘制光照贴图时,请使用游戏支持的最小缩放比例,这样您就不必缩小光照贴图并冒险显示光照贴图的剪裁边缘。因此,在 fbo.begin() 之后,请确保将您的 cam.zoom 更改为场景的最低 cam.zoom

    要获得缩放的光照贴图坐标,您需要在顶点着色器中计算它并将其传递给片段着色器。首先,您需要输入的缩放级别,因此您需要将其作为制服传递。你可以这样做:

    //including the changes explained way above, where objectLayers is an 
    //Array<LayerWrapper>, where LayerWrapper contains a zoom value and 
    //an Array<Something>
    for(int i=0;i<objectLayers.size;i++){
        LayerWrapper layer = objectLayers.get(i);
        Array<Something> layerObjects = layer.objects;
    
        camera.zoom = layer.zoom;
        camera.update();
        batch.setProjectionMatrix(camera.combined);
    
        //update the zoom level in the shader you're using with the sprite batch
        customShader.bind();
        customShader.setUniformf("u_zoom", layer.zoom);
    
        for(Something object : layerObjects ){
            object.sprite.draw(batch);
        }
    }
    

    然后更新您的顶点着色器以声明uniform float u_zoom;varying vec2 v_lightCoords;。您的灯光坐标将根据缩放比例放大。而且只要我们传递了一个变量,我们还应该预先计算确切的纹理坐标,所以你不必在片段着色器中这样做,这会导致依赖纹理读取(可能是在教程中完成)。

    顶点着色器现在看起来像这样:

    void main()
    {
        v_color = a_color;
        v_texCoords = a_texCoord0;
        vec4 screenSpacePosition = u_projTrans * a_position;
        gl_Position =  screenSpacePosition;
    
        v_lightCoords = (screenSpacePosition.xy * u_zoom) * 0.5 + 0.5;
    }
    

    由于v_lightCoords已经内置了纹理坐标,您可以简化片段着色器来进行纹理查找,如下所示: vec4 Light = texture2D(u_lightmap, v_lightCoords); 并删除不再需要的uniform vec2 resolution;

    【讨论】:

    • 非常感谢您的详细回答,您的解决方案完美解决了我的问题。我遇到的两个小问题是 screenSpacePosition 需要是 vec4 和 zoom / 2 对于 Android 需要是 zoom / 2.0,但是您的解决方案让我可以按照我想要的方式缩放光照贴图。还感谢有关性能改进的其他建议;我切换到手动计算 z 距离,通过将绘制的精灵的宽度、高度和位置除以“z 因子”来创建相同的视差效果,并显着提高了性能。
    • 谢谢,解决了你的问题。我想u_zoom 可能只是u_halfZoom 在顶点着色器中保存一条指令,但我怀疑这是否重要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多