简体   繁体   English

细胞阴影LibGdx

[英]Cell Shading LibGdx

Im working on my 3D Game with LibGdx. 我正在使用LibGdx制作3D 游戏
After looking some other Threads and posts with some realy good tutorials I get the first shader working. 寻找一些其他的线程和后与一些真的很好的教程我得到的第一着色器的工作。 My Problem now is to get a Cel/Outline/Toon Shader working. 我现在的问题是使Cel / Outline / Toon Shader工作。 Therefore I found also a tutorial and a project but they havnt worked. 因此,我也找到了一个教程和一个项目,但是他们没有用。

After reading some posts how to solve this shading Problem (with rendering the Object twice,...) I tried this method but got some sideeffects. 在阅读了一些如何解决此着色问题的文章(两次渲染对象,...)之后,我尝试了此方法,但有一些副作用。

Actually I got as result a darfred rendering Scene. 实际上,我得到了一个darfred渲染场景。 My Question is now, If my Models just need some other Material or why I get these results. 现在的问题是,如果我的模型仅需要其他材料,或者为什么我得到这些结果。

CEL-阴影 I wrote a cel shader based on the KBAL tutorial that results in renderings like the one above. 我根据KBAL教程编写了cel着色器,其结果类似于上面的渲染。 I've been meaning to write up something on it since the library has changed a lot since then. 自从那时起库发生了很大变化以来,我一直想在上面写一些东西。 It seems like you got stuck on the depth shader, which is one of the parts from the original tutorial that needed the most updating. 似乎您陷入了深度着色器,这是原始教程中需要更新最多的部分之一。

Besides compatibility updates, I removed one render pass by modifying the uber shader that comes with LibGDX to perform the discretization in the KBAL tutorial's toonify() function during the initial rendering of geometry rather than in a post pass. 除了兼容性更新之外,我还修改了LibGDX随附的uber着色器,以在几何的初始渲染过程中而不是在后期渲染过程中通过KBAL教程的toonify()函数执行离散化操作,从而删除了一个渲染过程。 Aside from that it follows the same pattern. 除此之外,它遵循相同的模式。

The code below is a bare bones implementation of my cel shader code. 下面的代码是我的cel着色器代码的基本实现。 The class is derived extends AbstractScreen which implements some base functionality for LibGDX's Screen interface. 该类是派生自AbstractScreen扩展,它为LibGDX的Screen接口实现了一些基本功能。 Read more about Screen 's here and see the CelTutorialScreen source within a full project context here . 了解更多关于Screen位置 ,看到了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();
    }
}

The render performs 3 passes to create the end product. render执行3次传递以创建最终产品。

The first is contained in the captureDepth() function. 第一个包含在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();
    }

A framebuffer is started, glClear is called and then the depthBatch ModelBatch() renders the model instances (only one in this case) before the framebuffer is ended. 启动帧缓冲区, glClear ,然后depthBatch ModelBatch()在结束帧缓冲区之前渲染模型实例(在这种情况下,只有一个)。

The depthBatch is a ModelBatch that uses a CelDepthShaderProvider , which provides a CelDepthShader . depthBatch是使用ModelBatchCelDepthShaderProvider ,后者提供CelDepthShader CellDepthShaderProvider is a small class that extends BaseShaderProvider and overrides createShader to return an instance of CelDepthShader , which registers and sets u_near and u_far uniforms as well as sets up the use of the cel depth vertex and fragment GLSL shaders. CellDepthShaderProvider是延伸的小类BaseShaderProvider并重写createShader返回的实例CelDepthShader ,哪些寄存器和套u_nearu_far制服以及套起来使用CEL深度的顶点片段 GLSL着色器。

I'm guessing the GLSL files are where you're running into issues. 我猜想GLSL文件就是您遇到问题的地方。 The vertex shader I linked to is the same as the KBAL vertex shader with the exception of line 125, which removed some artifacts on the cel edges: 我链接到的顶点着色器与KBAL顶点着色器相同,但第125行除外,该行删除了cel边缘上的一些瑕疵:

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

The fragment shader is very similar to the one in the KBAL tutorial, but is actually copied from LibGDX's built in depth fragment shader . 片段着色器与KBAL教程中的片段着色器非常相似,但实际上是从LibGDX的内置深度片段着色器复制而来的。 It's quite possible that the current LigGDX DepthShader could be used instead of my CelDepthShader , but I haven't had time to look into this. 很有可能可以使用当前的LigGDX DepthShader代替我的CelDepthShader ,但是我没有时间对此进行研究。

After the first pass, the packed depth map has been captured by the FBO. 第一次通过后,FBO已捕获了打包的深度图。 The second pass is ready to be run and will draw the scene with LibGDXs' default vertex shader and a slightly modified version of its fragment shader . 第二遍准备就绪,可以使用LibGDXs的默认顶点着色器 及其片段着色器稍加修改的版本来绘制场景。

The changes from the default fragment shader are in lines 140-150 where the specular value is discretized before being added to gl_FragColor: 从默认片段着色器进行的更改在第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;

And 173-182 where the overall gl_FragColor is discretized: 以及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;

And that's it for the main cel pass. 这就是主要的cel通行证。

Next in render() the prepTextureRegion() function is called. 接下来在render()prepTextureRegion()函数。 This just puts the depth texture captured to our fbo into a texture region and flips it vertically before using it to draw the cel outlines in the final pass. 这只是将捕获到fbo的深度纹理放入纹理区域中,并垂直翻转它,然后使用它在最终遍历中绘制cel轮廓。

The final pass is performed in drawOutlines() and makes use of a SpriteBatch since we're drawing a 2d texture instead of geometry. 最后drawOutlines()是在drawOutlines()执行的,并利用了SpriteBatch因为我们要绘制2D纹理而不是几何图形。 The call to spriteBatch.setShader(lineshader) sets the SpriteBatch to use an instance of CelLineShaderProgram , another class that extends ShaderProgram . spriteBatch.setShader(lineshader)的调用将SpriteBatch设置为使用CelLineShaderProgram的实例,该实例是扩展ShaderProgram另一个类。 It sets a u_size uniform and uses cel.line.vertex.glsl and cel.line.fragment.glsl . 它设置一个u_size制服,并使用cel.line.vertex.glslcel.line.fragment.glsl

This shader program runs the Laplace filter. 此着色器程序运行拉普拉斯滤镜。 The vertex shader is copied from the KBAL edge shader and updated to work with newer versions of LibGDX, it passes the sampled coordinate of the depth map as well as its top, bottom, left and right neighboring texels to the fragment shader as varyings. 顶点着色器是从KBAL边缘着色复制而来的,并更新为可与LibGDX的较新版本一起使用,它会将深度图的采样坐标及其顶部,底部,左侧和右侧的相邻纹理作为变量传递给片段着色器。

The fragment shader uses an updated method of unpacking the depth values based on code from the getShadowness() function here as recommended by Xoppa . 所述片段着色器使用拆包基于从代码的深度值的更新方法getShadowness()函数在这里作为推荐Xoppa

There are some improvements to this process that could be made. 可以对此过程进行一些改进。 For one, I haven't implemented the super sampling in the original tutorial. 首先,我没有在原始教程中实现超级采样。

Also, it's not really noticeable in this still image, but once you have a controllable camera in the scene, or geometry moving around, you'll notice the per pixel lighting looks a little weird, especially with limited polygons in your geometry. 同样,在静止图像中并不是很明显,但是一旦场景中有可控制的相机或四处移动的几何体,您就会注意到每像素照明看起来有点怪异,尤其是在几何体中多边形有限的情况下。 There is a per-pixel lighting fragment shader in the LibGDX shadow system tests that could be used as a base to implement this with cel shading. LibGDX阴影系统测试中有一个按像素的照明片段着色器 ,可用作基于cel着色实现此效果的基础。 The shadow systems might even be a good base to create a multi-pass rendering system for cel shading. 阴影系统甚至可能是创建用于cel着色的多遍渲染系统的良好基础。 And there is undoubtedly code that could be removed from the modified base LibGDX shaders I've used, as well as other optimizations and cleanup. 毫无疑问,可以从我使用的经过修改的基本LibGDX着色器中删除代码,以及进行其他优化和清理。

Hope this helps you or anyone else looking for info on multipass cel shading. 希望这可以帮助您或其他任何人寻找有关多通道cel着色的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM