Im working on my 3D Game with LibGdx.
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.
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.
My Question is now, If my Models just need some other Material or why I get these results.
I wrote a cel shader based on the KBAL tutorial that results in renderings like the one above. 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. Aside from that it follows the same pattern.
The code below is a bare bones implementation of my cel shader code. The class is derived extends AbstractScreen which implements some base functionality for LibGDX's Screen interface. Read more about Screen's here and see the CelTutorialScreen source within a full project context here.
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.
The first is contained in the captureDepth() function.
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.
The depthBatch is a ModelBatch that uses a CelDepthShaderProvider, which provides a 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.
I'm guessing the GLSL files are where you're running into issues. 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:
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. 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.
After the first pass, the packed depth map has been captured by the 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.
The changes from the default fragment shader are in lines 140-150 where the specular value is discretized before being added to 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:
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.
Next in render() the prepTextureRegion() function is called. 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.
The final pass is performed in drawOutlines() and makes use of a SpriteBatch since we're drawing a 2d texture instead of geometry. The call to spriteBatch.setShader(lineshader) sets the SpriteBatch to use an instance of CelLineShaderProgram, another class that extends ShaderProgram. It sets a u_size uniform and uses cel.line.vertex.glsl and cel.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.
The fragment shader uses an updated method of unpacking the depth values based on code from the getShadowness() function here as recommended by 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. The shadow systems might even be a good base to create a multi-pass rendering system for cel shading. 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.
Hope this helps you or anyone else looking for info on multipass cel shading.
Related
I'm currently stuck in front of a problem.
I work on Android with API 19 with Eclipse IDE using Java.
I've done an Activity which contains some Layouts. And i've put in one of these layout a custom class which inherits from GLSurfaceView in order to provide a 3D rendering for the user.
But actually i have a problem on a specific device, the "Epson moverio" glasses which works with Android 4.04.
When the softkeyboard pops out, it shows me the GLSurfaceView and also a black square which have the same dimensions as GLSurfaceView and this black square is contiguous with the GLSurfaceView. I don't understand why i have this bug, because it works well on classic devices like samsung tablets and i have another version of the view on which i draw 2D shapes using canvas, and i don't get the black square problem, it seems that kind of problem happens only with openGL rendering context (i'm using openGL 2.0 ES).
it seems that this bug occurs only when the softkeyboard pops on the screen.
I tried to put an invalidate() and a forceLayout function call in onSurfaceChanged and onSizeChanged but it doesn't work.
I point out that the bug only occurs on the "Epson moverio" glasses and i don't understand why.
Is it a good way to build my own class which inherits from GLSurfaceView, and put it into the Activity's layout in order to display 3D or is there another better way to do that kind of feature ?
Here's some snippet of code of my GLSurfaceView class implementation
#Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
long time = SystemClock.uptimeMillis() % 10000L;
float angleInDegrees = (360.0f / 10000.0f) * ((int) time);
if (_trolleyContentInfoList != null && _trolleyCaracteristics != null) {
_matrixTools.loadIndentity(MatrixType.MODEL_MATRIX);
drawTrolleyStructure(angleInDegrees);
drawTrolleyShelves(angleInDegrees);
//drawTrolleyWheels(angleInDegrees);
}
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
GLES20.glViewport(0, 0, width, height);
float aspect = (float)width / height;
float fovy = 60.0f;
float near = 0.1f;
float far = 10.0f;
_width = width;
_height = height;
_matrixTools.loadIndentity(MatrixType.PROJECTION_MATRIX);
_matrixTools.perspective(MatrixType.PROJECTION_MATRIX, fovy, aspect, near, far);
this.forceLayout();
}
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
this.forceLayout();
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0.8f, 0.8f, 0.8f, 0.0f);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
_matrixTools.loadIndentity(MatrixType.VIEW_MATRIX);
_matrixTools.lookAt(MatrixType.VIEW_MATRIX, 0.0f, 0.0f, -2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
_matrixTools.loadIndentity(MatrixType.MODEL_MATRIX);
_cubeDrawer = new CubeDrawer();
_cubeDrawer.initialize();
}
And, finally, here's how my custom class inherits from GLSurfaceView
public class Trolley3DView extends GLSurfaceView implements GLSurfaceView.Renderer, ITrolleyPreviewView{
}
Edit : Okay after a test i've seen that the black square dissapear after typing something into a TextView in the Activity. Must i understand that i should invalidate the Activity in order to force it to refresh when the softkeyboard pops out ?
Okay, i finnaly found the reason of the bug. You have to set the color of the View on which you display openGL content, and the black square will magically disapear.
If I run this code as it is, I see a line from the bottom left corner of the screen to the top right corner of the screen. However, afaik I set glOrthox and glViewport, so why it's using the default projection is a mystery to me... what's wrong? the lower left of the screen should be 0,0, and 1,1 should be basically 1 pixel over and up into the screen.. not the whole screen.
package com.opengl.hello;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
public class Renderer2d implements Renderer {
Context mContext;
public static ByteBuffer newByteBuffer(int size)
{ return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); }
public static FloatBuffer newFloatBuffer(int size)
{ return newByteBuffer(size * 4).asFloatBuffer(); }
public static ByteBuffer newByteBuffer(byte[] buf)
{ ByteBuffer out=newByteBuffer(buf.length); out.put(buf).position(0); return out; }
public static FloatBuffer newFloatBuffer(float[] buf)
{ FloatBuffer out=newFloatBuffer(buf.length); out.put(buf).position(0); return out; }
public Renderer2d(Context context)
{
mContext=context;
}
int mWidth;
int mHeight;
#Override
public void onDrawFrame(GL10 gl) {
gl.glViewport(0, 0, mWidth, mHeight);
// Reset projection
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
// Set up our ortho view
gl.glOrthox(0, mWidth, 0, mHeight, 0, 0);
// Reset model view
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Build buffers sufficient to draw a single
ByteBuffer indicesBuffer=newByteBuffer(new byte[] { 0, 1 });
// PROBLEM: I expect this to draw a small line, from the bottom left to 1,1 (a single pixel over and up into the image)..
// as well as sticking off-screen down and to the left. Instead, this covers the entire screen, corner to corner!
FloatBuffer vertexBuffer=newFloatBuffer(new float[]{ -1.f, -1.f, 1.f, 1.f });
// Enable vertex arrays, as we're about to use them
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// Each point in the vertex buffer has two dimensions.
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
// Draw one point
gl.glDrawElements(GL10.GL_LINES, 2, GL10.GL_UNSIGNED_BYTE, indicesBuffer);
// No longer need vertex arrays
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
}
/**
* If the surface changes, reset the view.
*/
#Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
mWidth=width;
mHeight=height;
// Moved code to onDrawFrame just to group everything together
}
/**
* The Surface is created/init()
*/
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Yellow Background
gl.glClearColor(1.0f, 1.0f, 0.0f, 0.0f);
// Disable dithering for better performance
gl.glDisable(GL10.GL_DITHER);
// enable texture mapping
gl.glEnable(GL10.GL_TEXTURE_2D);
//enable transparency blending
gl.glEnable(GL10.GL_BLEND);
// use opaque texturing
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_SRC_COLOR);
// Disable the depth buffer, we're drawing in 2D.
gl.glDisable(GL10.GL_DEPTH_TEST);
// Smooth shading
gl.glShadeModel(GL10.GL_SMOOTH);
// Really Nice Perspective Calculations
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
}
gl.glPointSize(150);
I suspect that 150 is far greater than the upper ends of GL_ALIASED_POINT_SIZE_RANGE and GL_SMOOTH_POINT_SIZE_RANGE. Try something smaller like 1 or 2.
I have been looking at using JOGL to create some things and have been looking through what documentation I can find.
Brief tutorials they all mention how using the JOGL version of canvas can have performance issues and instead you should use NEWT. However each and every tutorial / FAQ then goes on to use the canvas! Or simply specify a few tiny snippets of methods to create a window using NEWT but which (on my machine at least) I cannot get to correctly run.
Does anyone have a good source of examples of how to correctly implement creating and rendering to a window in JOGL using the NEWT method? I'm not even sure how it functions compared to the Canvas so an explanation of the differences between the two and a typical layout of methods to create / manage / render to a window would be ideal.
Just a little lost and cannot find anything useful. Hope someone's come across something before!
This tutorial helped me a lot, see chapter 3.9 - Yet Another Tutorial on JOGL.
Also documentation is useful. Take a look at the attached example, please.
JOGL2NewtDemo.java
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLProfile;
import com.jogamp.newt.event.WindowAdapter;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.FPSAnimator;
/**
* A program that draws with JOGL in a NEWT GLWindow.
*
*/
public class JOGL2NewtDemo {
private static String TITLE = "JOGL 2 with NEWT"; // window's title
private static final int WINDOW_WIDTH = 640; // width of the drawable
private static final int WINDOW_HEIGHT = 480; // height of the drawable
private static final int FPS = 60; // animator's target frames per second
static {
GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations
}
/**
* The entry main() method.
*/
public static void main(String[] args) {
// Get the default OpenGL profile, reflecting the best for your running platform
GLProfile glp = GLProfile.getDefault();
// Specifies a set of OpenGL capabilities, based on your profile.
GLCapabilities caps = new GLCapabilities(glp);
// Create the OpenGL rendering canvas
GLWindow window = GLWindow.create(caps);
// Create a animator that drives canvas' display() at the specified FPS.
final FPSAnimator animator = new FPSAnimator(window, FPS, true);
window.addWindowListener(new WindowAdapter() {
#Override
public void windowDestroyNotify(WindowEvent arg0) {
// Use a dedicate thread to run the stop() to ensure that the
// animator stops before program exits.
new Thread() {
#Override
public void run() {
if (animator.isStarted())
animator.stop(); // stop the animator loop
System.exit(0);
}
}.start();
}
});
window.addGLEventListener(new JOGL2Renderer());
window.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
window.setTitle(TITLE);
window.setVisible(true);
animator.start(); // start the animator loop
}
}
JOGL2Renderer.java
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
/**
* Class handles the OpenGL events to render graphics.
*
*/
public class JOGL2Renderer implements GLEventListener {
private double theta = 0.0f; // rotational angle
/**
* Called back by the drawable to render OpenGL graphics
*/
#Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2(); // get the OpenGL graphics context
gl.glClear(GL.GL_COLOR_BUFFER_BIT); // clear background
gl.glLoadIdentity(); // reset the model-view matrix
// Rendering code - draw a triangle
float sine = (float)Math.sin(theta);
float cosine = (float)Math.cos(theta);
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor3f(1, 0, 0);
gl.glVertex2d(-cosine, -cosine);
gl.glColor3f(0, 1, 0);
gl.glVertex2d(0, cosine);
gl.glColor3f(0, 0, 1);
gl.glVertex2d(sine, -sine);
gl.glEnd();
update();
}
/**
* Update the rotation angle after each frame refresh
*/
private void update() {
theta += 0.01;
}
/*... Other methods leave blank ...*/
}
take a look at JOGL's junit tests, they cover large parts of the NEWT API.
I have been using JOGL for a few days now and this is now becoming a major road block. I can not get shapes to draw in the correct z-order, instead, they are drawn in the order they are given to OpenGL.
I have spent the last few hours researching this and the general resolutions (and my reactions) seem to be the following:
Make sure your frustum is correct
I have double checked the frustum, it seems correct
I have switched to gluLookAt instead of a custom built frustum
I have switched to glOrthof just to make sure it is not perspective that is the problem.
I have not even set ANY view at all, instead working in the -1, 1 range that seems to be default
Make sure the following calls are in the init:
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
Make sure you are clearing the depth buffer on each redraw
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
I have provided the very basic sample of a program below where this problem is occurring. If you have JOGL and run it, you will see the red triangle at Z-position -0.5f ALWAYS on top while the triangles rotate around each other. If you swap the two triangle vertex calls, the green triangle will then always be on top.
This has been an immense headache for me so any insight will be helpful, either from JOGL or OpenGL in general, but I can't seem to see what is wrong.
Also note that for brevity I removed the proper code to destroy the window.
import java.awt.Frame;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
import com.jogamp.opengl.util.Animator;
public class JOGLTest implements GLEventListener
{
static GLU glu = new GLU();
static GLCanvas canvas = new GLCanvas();
static Frame frame = new Frame("JOGL test");
static Animator animator = new Animator(canvas);
float rot = 0.0f;
public void display(GLAutoDrawable glDrawable)
{
final GL2 gl = glDrawable.getGL().getGL2();
rot++;
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glRotatef(rot, 0.0f, 1.0f, 0.0f);
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor3f(0.0f, 1.0f, 0.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, 0.0f);
gl.glColor3f(1.0f, 0.0f, 0.0f);
gl.glVertex3f(-1.0f, 1.0f, -0.5f);
gl.glVertex3f(1.0f, 1.0f, -0.5f);
gl.glVertex3f(0.0f, 0.0f, -0.5f);
gl.glEnd();
}
public void dispose(GLAutoDrawable arg0)
{
}
public void init(GLAutoDrawable glDrawable)
{
GL2 gl = glDrawable.getGL().getGL2();
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glClearDepth(1.0f);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
}
public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3,
int arg4)
{
}
public static void main(String[] args)
{
canvas.addGLEventListener(new JOGLTest());
frame.add(canvas);
frame.setSize(640, 480);
frame.setVisible(true);
animator.start();
canvas.requestFocus();
}
}
I have solved the problem over much deliberation.
I created a GLCapabilities object and manually set the number of bits for a depth buffer while creating a GLCanvas. The code is as follows:
GLProfile glp = GLProfile.getDefault();
GLCapabilities caps = new GLCapabilities(glp);
caps.setDepthBits(16);
canvas = new GLCanvas(caps);
Just stumbled across your question... are you using an Intel graphics card and an 'older' (pre-24th November) version of JOGL, by any chance? If so, the following may be helpful (ignore the mention of win7 64-bit; the problem described is more general and was solved in that instance in subsequent JOGL releases):
http://jogamp.762907.n3.nabble.com/Depth-buffer-not-working-on-Win7-64b-td1737435.html
Got the same problem, but unlike #david I just set the depthSize in GLData:
GLData data = new GLData ();
data.depthSize = 24;
#jogl #linux
I have an application that receives information from a database, and is used to visualize 2D bitmaps onto a GLSurfaceView. The information received will determine the x-position of the bitmap, and which bitmap image to use (there are 4 different bitmaps in my res folder to choose from).
Below are the three classes that are being used. The Activity sets the Shapes objects that need to be drawn by passing an ArrayList to the GLLayer class. This ArrayList is passed to the instance of ShapeStorage class via another setList method. This class is responsible for drawing when they are received.
The problem that I'm having is the following. Suppose I receive one object (let's say that it's a square at it's located at x=1). Some time goes by, and I receive another shape (this time, it's a triangle, and it's located at x=-1). However, when this new shape appears on the screen, the old bitmap's appearance changes to a triangle, and the new one becomes a square. In other words, the objects themselves are at the correct position that they are supposed to be at, but the bitmap being associated with them has changed. Does anyone know what the possible cause of this can be? I'm still a newbie to OpenGL-ES, and while this code looks very convoluted, it just involves setting a bunch of various properties for the View. Please help me, StackOverflow! You're my only hope.
public class GLLayer extends GLSurfaceView implements Renderer {
int onDrawFrameCounter=1;
int[] cameraTexture;
byte[] glCameraFrame=new byte[256*256]; //size of a texture must be a power of 2
private Context context;
FloatBuffer cubeBuff;
FloatBuffer texBuff;
ShapeStorage shapes;
ArrayList<Shapes> shapereceptionbuffer;
public GLLayer(Context c) {
super(c);
this.context=c;
//Initiate our stars class with the number of stars
shapes = new ShapeStorage();
shapereceptionbuffer=new ArrayList<Shapes>();
this.setEGLConfigChooser(5, 6, 5, 8, 16, 0);
this.setRenderer(this); //set the following class as a GLSurfaceView renderer
this.getHolder().setFormat(PixelFormat.TRANSPARENT); //makes the GLSurfaceView translucent
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
try {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glShadeModel(GL10.GL_SMOOTH); //Enable Smooth Shading
gl.glEnable(GL10.GL_TEXTURE_2D); //Enable Texture Mapping
gl.glEnable(GL10.GL_BLEND); //Enable blending
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //Black Background
gl.glClearDepthf(1.0f); //Depth Buffer Setup
gl.glDisable(GL10.GL_DEPTH_TEST); //Disable depth test
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE); //Set The Blending Function For Translucency
shapes.setTextures(gl,context);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("Created",e.getMessage());
}
}//end of surfacecreated
public void setList(ArrayList<Shapes> receivedList){
synchronized(this.shapereceptionbuffer){
shapereceptionbuffer=receivedList;
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
try {
if(height == 0) { //Prevent A Divide By Zero By
height = 1; //Making Height Equal One
}
gl.glViewport(0, 0, width, height);//specifies transformation from normalized device coordinates to window coordinates
float ratio = (float) width / height;
gl.glMatrixMode(GL11.GL_PROJECTION); //Select The Projection Matrix
gl.glLoadIdentity();//Reset The Projection Matrix
GLU.gluPerspective(gl, 45.0f, ratio, 0.1f, 100.0f);
gl.glMatrixMode(GL11.GL_MODELVIEW);//Select The Modelview Matrix
gl.glLoadIdentity();//Reset The Modelview Matrix
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("Changed",e.getMessage());
}
//GLU.gluLookAt(gl, 0, 0, 4.2f, 0, 0, 0, 0, 1, 0);//eye-point location, center of the scene and an UP vector
}//end of surfacechanged
public void onDrawFrame(GL10 gl) {
try {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); //Clear Screen And Depth Buffer
Log.d("Buffer Size", String.valueOf(shapereceptionbuffer.size()));
synchronized(this.shapereceptionbuffer){
shapes.setShapes(shapereceptionbuffer);
shapes.draw(gl, this.context);
}
} catch (Exception e) {
Log.d("Draw",e.getMessage());
}
}//end of ondrawframe
}
This class is responsible for drawing each of the shapes that are received from the external database.
/**
* This class contains, loads, initiates textures and draws our Shapes
*/
public class ShapeStorage {
private ArrayList<Shapes> shapestoragebuffer;
private Random rand = new Random(); // Initiate Random for random values of
// stars
/** Our texture pointer */
private int[] textures = new int[4];
/**
* Constructor for our holder
*/
public ShapeStorage() {
shapestoragebuffer = new ArrayList<Shapes>();
}
public void setShapes(ArrayList<Shapes> receivedlist) {
shapestoragebuffer = receivedList;
}
public void setupTextures(GL10 gl, Context context) {
// Get the texture from the Android resource directory
InputStream is = null;
gl.glGenTextures(4, textures, 0);
for (int i = 2; i < 6; i++) {
switch (i) {
case 2:
is = context.getResources().openRawResource(R.drawable.square);
break;
case 3:
is = context.getResources().openRawResource(R.drawable.circle);
break;
case 4:
is = context.getResources().openRawResource(R.drawable.hexagon);
break;
case 5:
is = context.getResources()
.openRawResource(R.drawable.triangle);
break;
}
Bitmap bitmap = null;
try {
// BitmapFactory is an Android graphics utility for images
bitmap = BitmapFactory.decodeStream(is);
} finally {
// Always clear and close
try {
is.close();
is = null;
} catch (IOException e) {
}
}
// Generate the texture pointer
// Create Linear Filtered Texture and bind it to texture
GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
gl.glBindTexture(GL11.GL_TEXTURE_2D, textures[i - 2]);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER,
GL11.GL_LINEAR);
gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR);
// Clean up
bitmap.recycle();
}
}
/**
* The drawing function.
*
* #param gl
* - The GL Context
* #param twinkle
* - Twinkle on or off
*/
public void draw(GL10 gl, Context context) {
// Bind the icon texture for all Shapes
for (int loop = 0; loop < shapestoragebuffer.size(); loop++) {
// Recover the current star into an object
Shapes shape = shapestoragebuffer.get(loop);
gl.glLoadIdentity(); // Reset The Current Modelview Matrix
// gl.glRotatef(180.0f, -1.0f, 0.0f, 0.0f);
float x = shape.get_Offset_from_center();
gl.glTranslatef(x, 0.0f, -40.0f);
// Draw
switch (victim.getType()) {
// green
case 2:
shape.draw(gl, textures[0]);
break;
// red
case 3:
shape.draw(gl, textures[1]);
break;
// yellow
case 4:
shape.draw(gl, textures[2]);
break;
case 5:
shape.draw(gl, textures[3]);
break;
}
}
}
}
Here is the class that defines each of the objects that are being drawn to the GLSurfaceView; each of the shapes that are being drawn.
public class Shapes {
private int _Offset_from_center;
private int type;
Context c;
/** The buffer holding the vertices */
private FloatBuffer vertexBuffer;
/** The buffer holding the texture coordinates */
private FloatBuffer textureBuffer;
/** The initial vertex definition */
private float vertices[] = {
-1.0f, -1.0f, 0.0f, //Bottom Left
1.0f, -1.0f, 0.0f, //Bottom Right
-1.0f, 1.0f, 0.0f, //Top Left
1.0f, 1.0f, 0.0f //Top Right
};
/** The initial texture coordinates (u, v) */
private float texture[] = {
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
public Shapes() {
//
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
//
byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.position(0);
}
public int get_Offset_from_center() {
return _Offset_from_center;
}
public void set_Offset_from_center(int _Offset_from_center) {
this._Offset_from_center = _Offset_from_center;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
/**
* The object own drawing function.
* Called from the renderer to redraw this instance
* with possible changes in values.
*
* #param gl - The GL Context
*/
public void draw(GL10 gl,int texture) {
gl.glBindTexture(GL11.GL_TEXTURE_2D, texture);
//Enable the vertex, texture and normal state
gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
//Point to our buffers
gl.glVertexPointer(3, GL11.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, textureBuffer);
//Draw the vertices as triangle strip
gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
//Disable the client state before leaving
gl.glDisableClientState(GL11.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
}
}
You're doing something very strange with the textures. Why do you upload the texture image data for every shape on every frame, and why do you do it after you render the shape?
Here's how the usual flow of texture use in OpenGL works:
At app initialization time:
Generate a texture ID using glGenTextures.
Use glBindTexture to make that ID the current texture.
Set texture parameters.
Upload image data with texImage2D or similar.
Then, every time you need to render stuff with the texture:
Bind the texture with glBindTexture with the same ID you used above.
Render things, which will use the texture.
What I would recommend here is this:
When you activity starts up (called indirectly from onCreate or maybe onResume depending on how Android OpenGL works):
Make textures a 5-element array and pass 5 to glGenTextures
Loop through, and for each of your five resources, bind one of the four above, and upload your image with texImage2D just like you have.
Then, when you actually need to draw a shape:
Pass in an int for the texture, not an int[]; choose the right one based on the shape you want.
Call glBindTexture in your draw function, first, with that value.
Do not make any calls to texImage2D in your rendering pass.
Call glDrawArrays to draw the shape you chose with glBindTexture.
Note also that all your shapes can share the same vertex and texture buffers, since their contents are the same; that's just an efficiency thing though.