我正在使用LibGdx处理我的3D Game 在看了一些其他的Threads和posts以及一些非常好的教程后,我得到了第一个着色器工作。我现在的问题是让Cel / Outline / Toon Shader工作。 因此,我发现了一个教程和一个项目,但他们仍然有效。
在阅读了一些帖子之后如何解决这个着色问题(将对象渲染两次,......)我试过这个方法但是有一些副作用。
实际上我得到了一个darfred渲染场景。 我的问题现在,如果我的模型只需要一些其他材料或为什么我得到这些结果。
答案 0 :(得分:0)
我根据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}},提供CelDepthShader
。 CellDepthShaderProvider
是一个小类,它扩展BaseShaderProvider
并覆盖createShader
以返回CelDepthShader
的实例,该实例注册并设置u_near
和u_far
个制服以及设置cel深度vertex和fragment 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 shader和slightly 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.glsl
和cel.line.fragment.glsl
。
此着色器程序运行拉普拉斯滤波器。从KBAL边缘着色器复制vertex shader并更新以使用更新版本的LibGDX,它将深度贴图的采样坐标及其顶部,底部,左右相邻纹理像素传递给片段着色器,如varyings。
fragment shader使用更新的方法,根据getShadowness()
函数here中recommended by Xoppa的代码解析深度值。
可以对此过程进行一些改进。首先,我还没有在原始教程中实现超级采样。
此外,它在这张静止图像中并不是很明显,但是一旦你在场景中有一个可控制的摄像机,或者几何形状四处移动,你会注意到每个像素的光照看起来有点奇怪,尤其是在几何体中使用有限的多边形。 LibGDX影子系统测试中有一个per-pixel lighting fragment shader可用作基础,通过cel着色实现这一点。影子系统甚至可能是为cel着色创建多遍渲染系统的良好基础。毫无疑问,代码可以从我使用的修改后的基础LibGDX着色器中删除,以及其他优化和清理。
希望这可以帮助您或其他任何寻找多通道着色信息的人。