Cell Shading LibGdx

时间:2016-01-30 18:37:44

标签: java libgdx fragment shader vertex

我正在使用LibGdx处理我的3D Game 在看了一些其他的Threads和posts以及一些非常好的教程后,我得到了第一个着色器工作。我现在的问题是让Cel / Outline / Toon Shader工作。 因此,我发现了一个教程和一个项目,但他们仍然有效。

在阅读了一些帖子之后如何解决这个着色问题(将对象渲染两次,......)我试过这个方法但是有一些副作用。

实际上我得到了一个darfred渲染场景。 我的问题现在,如果我的模型只需要一些其他材料或为什么我得到这些结果。

1 个答案:

答案 0 :(得分:0)

cel-shaded 我根据KBAL tutorial编写了一个cel着色器,它会产生如上所示的渲染效果。自从图书馆从那时起经历了很多变化以来,我一直想在其上写些东西。您似乎陷入了深度着色器,这是原始教程中需要进行最多更新的部分之一。

除了兼容性更新,我删除了一个渲染过程,修改了LibGDX附带的超级着色器,以便在几何体的初始渲染过程中执行KBAL教程toonify()函数的离散化,而不是在帖子中通过。除此之外,它遵循相同的模式。

下面的代码是我的cel着色器代码的简单实现。该类派生为extends AbstractScreen,它为LibGDX的Screen接口实现了一些基本功能。详细了解Screen here并查看完整项目背景here中的CelTutorialScreen来源。

package com.hh.ghoststory.screen;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.utils.Array;
import com.hh.ghoststory.GhostStory;
import com.hh.ghoststory.render.shaders.CelDepthShaderProvider;
import com.hh.ghoststory.render.shaders.CelLineShaderProgram;

public class CelTutorialScreen extends AbstractScreen {
    private PerspectiveCamera camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

    private AssetManager assetManager = new AssetManager();
    private Array<ModelInstance> instances = new Array<ModelInstance>();

    private FrameBuffer fbo;
    private TextureRegion textureRegion;
    private ShaderProgram lineShader = new CelLineShaderProgram();

    private SpriteBatch spriteBatch = new SpriteBatch();
    private ModelBatch modelBatch = new ModelBatch(Gdx.files.classpath("com/badlogic/gdx/graphics/g3d/shaders/default.vertex.glsl").readString(), Gdx.files.internal("shaders/cel.main.fragment.glsl").readString());
    private ModelBatch depthBatch = new ModelBatch(new CelDepthShaderProvider());
    private Environment environment = new Environment();

    public CelTutorialScreen(GhostStory game) {
        super(game);

        Gdx.gl.glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
        Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);

        // setup camera
        camera.position.set(5, 5, 5);
        camera.lookAt(0, 0, 0);
        camera.near = 1;
        camera.far = 1000;
        camera.update();

        // add a light
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 1.8f, -1f, -0.8f, 0.2f));

        // load our model
        assetManager.load("models/spider.g3dj", Model.class);
        loading = true;
    }
    @Override
    public void render(float delta) {
        if (loading && assetManager.update())
            doneLoading();

        camera.update();
        Gdx.gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);

        // render depth map to fbo
        captureDepth();
        // draw the scene
        renderScene();
        // put fbo texture in a TextureRegion and flip it
        prepTextureRegion();
        // draw the cel outlines
        drawOutlines();
    }
    /*
     * Draws the cel outlines using the CelLineShaderProgram
     */
    protected void drawOutlines() {
        spriteBatch.setShader(lineShader);
        lineShader.setUniformf("u_size", Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        spriteBatch.begin();
        spriteBatch.draw(textureRegion, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        spriteBatch.end();
        spriteBatch.setShader(null);
    }
    /*
     * Stores fbo texture in a TextureRegion and flips it vertically.
     */
    protected void prepTextureRegion() {
        textureRegion = new TextureRegion(fbo.getColorBufferTexture());
        textureRegion.flip(false, true);
    }
    /*
     * Draws the depth pass to an fbo, using a ModelBatch created with CelDepthShaderProvider()
     */
    protected void captureDepth() {
        fbo.begin();
        Gdx.gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
        depthBatch.begin(camera);
        depthBatch.render(instances);
        depthBatch.end();
        fbo.end();
    }
    /*
     * Renders the scene.
     */
    protected void renderScene() {
        Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
        modelBatch.begin(camera);
        modelBatch.render(instances, environment);
        modelBatch.end();
    }
    @Override
    protected void doneLoading() {
        loading = false;
        instances.add(new ModelInstance(assetManager.get("models/spider.g3dj", Model.class)));
    }
    /*
     * Set camera width and height, SpriteBatch projection matrix, and reinit the FBOs
     */
    @Override
    public void resize(int width, int height) {
        camera.position.set(camera.position);
        camera.viewportWidth = width;
        camera.viewportHeight = height;
        camera.update();

        if (fbo != null) fbo.dispose();
        fbo = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);

        spriteBatch.setProjectionMatrix(new Matrix4().setToOrtho2D(0, 0, width, height));
    }
    @Override
    public void dispose() {
        assetManager.dispose();
        modelBatch.dispose();
        depthBatch.dispose();
        spriteBatch.dispose();
        fbo.dispose();
        lineShader.dispose();
    }
}

render执行3次传递以创建最终产品。

第一个包含在captureDepth()函数中。

    protected void captureDepth() {
        fbo.begin();
        Gdx.gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        Gdx.gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT);
        depthBatch.begin(camera);
        depthBatch.render(instances);
        depthBatch.end();
        fbo.end();
    }

启动帧缓冲,调用glClear然后depthBatch ModelBatch()在帧缓冲结束之前渲染模型实例(在这种情况下只有一个)。

depthBatch是使用CelDepthShaderProvider ModelBatch的{​​{1}},提供CelDepthShaderCellDepthShaderProvider是一个小类,它扩展BaseShaderProvider并覆盖createShader以返回CelDepthShader的实例,该实例注册并设置u_nearu_far个制服以及设置cel深度vertexfragment GLSL着色器的使用。

我猜测GLSL文件是您遇到问题的地方。我链接到的顶点着色器与KBAL顶点着色器相同,但第125行除外,它删除了cel边缘上的一些瑕疵:

v_depth = (pos.z + u_near) / (u_far - u_near);

片段着色器与KBAL教程中的片段着色器非常相似,但实际上是从LibGDX&#39; s built in depth fragment shader复制的。很有可能当前的LigGDX DepthShader可以用来代替CelDepthShader,但我还没有时间研究这个。

第一次通过后,FBO捕获了打包的深度图。第二遍准备好了,并将用LibGDXs&#39;来绘制场景。 default vertex shaderslightly modified version of its fragment shader

默认片段着色器的更改位于行140-150中,其中镜面值在添加到gl_FragColor之前被离散化:

if (specIntensity > 0.6)
    specFactor = 1.0;
else if (specIntensity > 0.3)
    specFactor = 0.5;
else
    specFactor = 0.1;

specular *= specFactor;

173-182整体gl_FragColor被离散化:

float intensity = max(gl_FragColor.r, max(gl_FragColor.g, gl_FragColor.b));
float factor;
if (intensity > 0.8)
    factor = 1.0;
else if (intensity > 0.5)
    factor = 0.8;
else if (intensity > 0.25)
    factor = 0.3;
else
    factor = 0.1;

这就是主要的传球。

接下来在render()中调用prepTextureRegion()函数。这只是将捕获到我们的fbo的深度纹理放入纹理区域并垂直翻转,然后使用它在最终传递中绘制cel轮廓。

最终传递在drawOutlines()中执行并使用SpriteBatch,因为我们正在绘制2d纹理而不是几何体。对spriteBatch.setShader(lineshader)的调用会将SpriteBatch设置为使用CelLineShaderProgram的实例,这是另一个扩展ShaderProgram的类。它设置u_size制服并使用cel.line.vertex.glslcel.line.fragment.glsl

此着色器程序运行拉普拉斯滤波器。从KBAL边缘着色器复制vertex shader并更新以使用更新版本的LibGDX,它将深度贴图的采样坐标及其顶部,底部,左右相邻纹理像素传递给片段着色器,如varyings。

fragment shader使用更新的方法,根据getShadowness()函数hererecommended by Xoppa的代码解析深度值。

可以对此过程进行一些改进。首先,我还没有在原始教程中实现超级采样。

此外,它在这张静止图像中并不是很明显,但是一旦你在场景中有一个可控制的摄像机,或者几何形状四处移动,你会注意到每个像素的光照看起来有点奇怪,尤其是在几何体中使用有限的多边形。 LibGDX影子系统测试中有一个per-pixel lighting fragment shader可用作基础,通过cel着色实现这一点。影子系统甚至可能是为cel着色创建多遍渲染系统的良好基础。毫无疑问,代码可以从我使用的修改后的基础LibGDX着色器中删除,以及其他优化和清理。

希望这可以帮助您或其他任何寻找多通道着色信息的人。