What's wrong with this simple OpenGL/JOGL stencil test? - opengl

I'm learning how to use a stencil buffer, but so far have been unsuccessful at getting a even a simple example to work. In fact, despite trying various combinations of parameters for glStencilOp and glStencilFunc I have not been able to see any evidence that the stencil buffer is working at all. I'm starting to suspect my graphics driver (Mac Pro, Mac OS X 10.8.5) or JOGL (2.0.2) doesn't support it... or I'm missing something really basic.
Here's what I'm seeing:
I'm expecting to see the red diamond clipped by the green diamond. What am I doing wrong?
public class Test {
public static void main(String[] args) {
GLProfile glprofile = GLProfile.getDefault();
final GLCapabilities glcapabilities = new GLCapabilities(glprofile);
final GLCanvas glcanvas = new GLCanvas(glcapabilities);
final GLU glu = new GLU();
glcanvas.addGLEventListener(new GLEventListener() {
#Override
public void reshape(GLAutoDrawable glautodrawable, int x, int y, int width, int height) {}
#Override
public void init(GLAutoDrawable glautodrawable) {
GL2 gl = glautodrawable.getGL().getGL2();
glcapabilities.setStencilBits(8);
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45, 1, 1, 10000);
glu.gluLookAt(0, 0, 100, 0, 0, 0, 0, 1, 0);
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
gl.glLoadIdentity();
}
#Override
public void dispose(GLAutoDrawable glautodrawable) {}
#Override
public void display(GLAutoDrawable glautodrawable) {
GL2 gl = glautodrawable.getGL().getGL2();
gl.glEnable(GL.GL_STENCIL_TEST);
gl.glClearStencil(0x0);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
gl.glStencilFunc(GL.GL_ALWAYS, 1, 1);
gl.glStencilOp(GL.GL_REPLACE, GL.GL_REPLACE, GL.GL_REPLACE);
gl.glStencilMask(0xFF);
//gl.glColorMask(false, false, false, false);
//gl.glDepthMask(false);
gl.glColor3f(0, 1, 0);
gl.glBegin(GL2.GL_QUADS);
gl.glVertex2f(-25.0f, 0.0f);
gl.glVertex2f(0.0f, 15.0f);
gl.glVertex2f(25.0f, 0.0f);
gl.glVertex2f(0.0f, -15.0f);
gl.glEnd();
gl.glStencilMask(0);
gl.glStencilFunc(GL2.GL_EQUAL, 1, 1);
gl.glStencilOp(GL2.GL_KEEP, GL2.GL_KEEP, GL2.GL_KEEP);
//gl.glColorMask(true, true, true, true);
//gl.glDepthMask(true);
gl.glColor3f(1, 0, 0);
gl.glBegin(GL2.GL_QUADS);
gl.glVertex2f(-20.0f, 0.0f);
gl.glVertex2f(0.0f, 20.0f);
gl.glVertex2f(20.0f, 0.0f);
gl.glVertex2f(0.0f, -20.0f);
gl.glEnd();
}
});
final JFrame jframe = new JFrame("One Triangle Swing GLCanvas");
jframe.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent windowevent) {
jframe.dispose();
System.exit(0);
}
});
jframe.getContentPane().add(glcanvas, BorderLayout.CENTER);
jframe.setSize(640, 480);
jframe.setVisible(true);
}
}

Zero298 has the right idea, though fails to explain why what you tried in your code does not work. This becomes more apparent when you understand how framebuffer pixel formats work in OpenGL; I will touch on this a little bit below, but first just to re-hash the proper solution:
public static void main(String[] args) {
GLProfile glprofile = GLProfile.getDefault ();
GLCapabilities glcapabilities = new GLCapabilities (glprofile);
// You must do this _BEFORE_ creating a render context
glcapabilities.setStencilBits (8);
final GLCanvas glcanvas = new GLCanvas (glcapabilities);
final GLU glu = new GLU ();
The important thing is that you do this before creating your render context ("canvas"). The stencil buffer is not something you can enable or disable whenever you need it -- you first have to select a pixel format that reserves storage for it. Since pixel formats are fixed from the time you create your render context onward, you need to do this before new GLCanvas (...).
You can actually use an FBO to do stencil operations in a render context that does not have a stencil buffer, but this is much more advanced than you should be considering at the moment. Something to consider if you ever want to do MSAA though, FBOs are a much nicer way of changing pixel formats at run-time than creating and destroying your render context ("canvas").

You need a call to glStencilMask() it's what controls what gets written or not. Set it to do or don't write, draw a stencil (in your case, the diamond), set the glStencilMask() again, and then draw what you want to get clipped.
This has a good sample: Stencil Buffer explanation
EDIT:
OK, I think I found the problem. You need to set your capabilities up at the top of the program.
final GLCapabilities glcapabilities = new GLCapabilities(glprofile);
glcapabilities.setStencilBits(8);
final GLCanvas glcanvas = new GLCanvas(glcapabilities);
The important part being:
glcapabilities.setStencilBits(8);
Thanks to: enabling stencil in jogl

Related

Can't turn light off in libgdx

Use the code below, if I turn the light off, the blue box will be black.
But there seems no effect on the entity, it is still colorful. What's wrong with the code? Please help thanks.
package com.louxiu.game;
/**
* Created by louxiu on 2/22/16.
*/
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.*;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.loader.ObjLoader;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.Vector3;
public class TestApp implements ApplicationListener {
private PerspectiveCamera camera;
private ModelBatch modelBatch;
private Model box;
private ModelInstance boxInstance;
public Model entityModel;
public ModelInstance entityInstance;
private Environment environment;
#Override
public void create() {
// Create camera sized to screens width/height with Field of View of 75 degrees
camera = new PerspectiveCamera(
75,
Gdx.graphics.getWidth(),
Gdx.graphics.getHeight());
// Move the camera 3 units back along the z-axis and look at the origin
camera.position.set(0f, 0f, 3f);
camera.lookAt(0f, 0f, 0f);
// Near and Far (plane) repesent the minimum and maximum ranges of the camera in, um, units
camera.near = 0.1f;
camera.far = 300.0f;
// A ModelBatch is like a SpriteBatch, just for models. Use it to batch up geometry for OpenGL
modelBatch = new ModelBatch();
// A ModelBuilder can be used to build meshes by hand
ModelBuilder modelBuilder = new ModelBuilder();
// It also has the handy ability to make certain premade shapes, like a Cube
// We pass in a ColorAttribute, making our cubes diffuse ( aka, color ) red.
// And let openGL know we are interested in the Position and Normal channels
box = modelBuilder.createBox(2f, 2f, 2f,
new Material(ColorAttribute.createDiffuse(Color.BLUE)),
Usage.Position | Usage.Normal
);
// A entityModel holds all of the information about an, um, entityModel, such as vertex data and texture info
// However, you need an entityInstance to actually render it. The entityInstance contains all the
// positioning information ( and more ). Remember Model==heavy ModelInstance==Light
boxInstance = new ModelInstance(box, 0, 0, 0);
String entity = "creeper/creeper";
ObjLoader loader = new ObjLoader();
entityModel = loader.loadModel(Gdx.files.internal(entity + ".obj"), new ObjLoader.ObjLoaderParameters(true));
entityInstance = new ModelInstance(entityModel, 0, 0, 0);
Texture texture = new Texture(Gdx.files.internal(entity + ".png"));
entityInstance.materials.get(0).set(TextureAttribute.createDiffuse(texture));
// Finally we want some light, or we wont see our color. The environment gets passed in during
// the rendering process. Create one, then create an Ambient ( non-positioned, non-directional ) light.
environment = new Environment();
// environment.add(new DirectionalLight().set(1f, 1f, 1f, -1f, -0.8f, -0.2f));
// environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.8f, 0.8f, 0.8f, 1.0f));
}
#Override
public void dispose() {
modelBatch.dispose();
box.dispose();
}
#Override
public void render() {
// You've seen all this before, just be sure to clear the GL_DEPTH_BUFFER_BIT when working in 3D
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT | GL30.GL_DEPTH_BUFFER_BIT);
// For some flavor, lets spin our camera around the Y axis by 1 degree each time render is called
camera.rotateAround(Vector3.Zero, new Vector3(0, 1, 0), 1f);
// When you change the camera details, you need to call update();
// Also note, you need to call update() at least once.
camera.update();
// Like spriteBatch, just with models! pass in the box Instance and the environment
modelBatch.begin(camera);
modelBatch.render(boxInstance, environment);
// modelBatch.render(entityInstance, environment);
modelBatch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
You wavefront model probably does not have normals, which is required for lighting to work.
Check the log, there should be an error message showing you that you should not use ObjLoader altogether. Instead use G3dModelLoader or even better: use AssetManager and the g3dj or g3db file format.
Export your model from your modeling application into e.g. the FBX file format and convert it using fbx-conv. Do not convert your .obj file into a .g3dx file using fbx-conv, that won't work.
Btw, although not related you might want to take into consideration that:
your camera far/near ratio is very high, you usually should never use a near value below 1.
Unlike what your comment says, ModelBatch is not used to batch geometry and not that comparable to SpriteBatch.
ObjLoader has a loadModel method which accepts a boolean, so you don't have to create ObjLoaderParameters for that (although, as said, you shouldnt be using ObjLoader altogether).
You are creating a Texture without properly disposing it when no longer needed. This will cause a potential resource leak.
Creating a new Vector3 every frame is going to pressure the GC and will cause hick-ups. Simply use Vector3.Y instead of new Vector3(0, 1, 0) to fix that.

OpenGL/JOGL throwing GL_INVALID_OPERATION

I am coding a level editor for a game I am developing. I use JOGL and I seem to have a problem. I am used to LWJGL openGL calls and adjusting to core opengl is a little confusing since lwjgl seem to have simplified a lot of stuff.
So my problem is that I created a model that holds vao ID/name and vertex count and a model loader that creates the model and a renderer. The renderer is not a batched at the moment. I will work on it later. The problem is that opengl throws a GL_INVALID_OPERATION error. Not sure what is causing it. Everything else including the basic triangle I drew to test the environment works, so there seems to be a problem somewhere in my loader or renderer.
Here's the code:
Model:
public class JoglModel {
private int vaoID;
private int vertexCount;
public JoglModel(int vertexCount, int vaoID) {
this.vertexCount = vertexCount;
this.vaoID = vaoID;
}
public int getVertexCount() {
return vertexCount;
}
public int getVaoID() {
return vaoID;
}
}
Loader:
public class ModelLoader {
private GL2 gl;
private List<int[]> vaos = new ArrayList<int[]>();
private List<int[]> vbos = new ArrayList<int[]>();
public ModelLoader(GL2 gl){
this.gl = gl;
}
public JoglModel loadToVao(float[] positions){
int vaoID = createVAO();
storeDataInAttributeList(0,positions);
unbind();
return new JoglModel(vaoID,positions.length/3);
}
private int createVAO(){
int[] vaoID = new int[1];
gl.glGenVertexArrays(vaoID.length, vaoID, 0);
vaos.add(vaoID);
gl.glBindVertexArray(vaoID[0]);
return vaoID[0];
}
private void storeDataInAttributeList(int attributeNumber,float[] data){
int[] vboID = new int[1];
gl.glGenBuffers(vboID.length,vboID,0);
vbos.add(vboID);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER,vboID[0]);
FloatBuffer floatBuffer = createFloatBuffer(data);
gl.glBufferData(gl.GL_ARRAY_BUFFER,floatBuffer.remaining(),floatBuffer,gl.GL_STATIC_DRAW);
gl.glVertexAttribPointer(attributeNumber,3,gl.GL_FLOAT,false,0,0);
gl.glBindBuffer(gl.GL_ARRAY_BUFFER,0);
}
private FloatBuffer createFloatBuffer(float[] data){
FloatBuffer floatBuffer = FloatBuffer.allocate(data.length);
floatBuffer.put(data);
floatBuffer.flip();
return floatBuffer;
}
private void unbind(){}
public void clear(){
for(int[] vao : vaos){
gl.glDeleteVertexArrays(vao.length,vao,0);
}
for(int[] vbo: vbos){
gl.glDeleteBuffers(vbo.length,vbo,0);
}
vaos.clear();
vbos.clear();
}
}
Renderer:
public class JoglRenderer {
private GL2 gl;
public JoglRenderer(GL2 gl){
this.gl = gl;
}
public void begin(){
gl.glClearColor(1f,0f,0f,1f);
gl.glClear(gl.GL_CLEAR_BUFFER);
}
public void render(JoglModel joglModel){
gl.glBindVertexArray(joglModel.getVaoID());
gl.glEnableVertexAttribArray(0);
gl.glDrawArrays(gl.GL_TRIANGLES,0,joglModel.getVertexCount());
gl.glDisableVertexAttribArray(0);
gl.glBindVertexArray(0);
/*
gl.glBegin(gl.GL_TRIANGLES);
gl.glColor3f(1, 0, 0);
gl.glVertex2f(-1, -1);
gl.glColor3f(0, 1, 0);
gl.glVertex2f(0, 1);
gl.glColor3f(0, 0, 1);
gl.glVertex2f(1, -1);
gl.glEnd();
*/
}
public void checkError() {
String errorString = "";
int error = gl.glGetError();
if (error != GL.GL_NO_ERROR) {
switch (error) {
case GL.GL_INVALID_ENUM:
errorString = "GL_INVALID_ENUM";
break;
case GL.GL_INVALID_VALUE:
errorString = "GL_INVALID_VALUE";
break;
case GL.GL_INVALID_OPERATION:
errorString = "GL_INVALID_OPERATION";
break;
case GL.GL_INVALID_FRAMEBUFFER_OPERATION:
errorString = "GL_INVALID_FRAMEBUFFER_OPERATION";
break;
case GL.GL_OUT_OF_MEMORY:
errorString = "GL_OUT_OF_MEMORY";
break;
default:
errorString = "UNKNOWN";
break;
}
}
System.out.println(errorString);
}
}
the commented out triangle part works just fine. There also seems to be an error in clear screen method but that's not my concern right now. Can any one point out where the problem could be?
Thanks
(EDIT)
So i figured out the opengl error. I was accidentally passing the vaoID as the vertex count and vice versa . so i fixed that the error is gone. But nothing is being rendered. Any ideas?
I write here few considerations, since comments are too short for that:
loadToVao could lead you wrong, you don't load anything to a vao, the vao is useful to remember which vertices attributes arrays are enabled, their layout/format and which vbo they refer to, so that you don't have to call them every frame. It can also store the bound element array. So glEnableVertexAttribArray and glDisableVertexAttribArray shouldn't go in the render() function
the renderer should always be there as default, so I'd suggest to have a main and there initialize your renderer (the GLEventListener)
I'd not bind the vao in the createVAO
Do not store the GL element. Keep it transient (pass as argument everytime) or get from GLContext. The first option may increase complexity (since every gl call need to have the GL object from the class implementing GLEventListener) but simplifies debugging (because you do know exactly in which order the gl calls get executed).
if you need just one vao, avoid creating a List for that, same for the vbo.
I suggest you to use static final int variables to hold the vertices attribute indices. It improves readability and avoid potential bugs.
Unless you do not need direct buffers, Use GLBuffers to allocate (direct) buffers.
What is that gl.GL_FLOAT? I never saw that. Use Float.BYTES or GLBuffers.SIZEOF_FLOAT instead.
As #BDL already said, look at glClear and call checkError like here, passing every time a different string so that you can easily find out which is the problematic call if something throw an error.
Jogl does have a GL_COLOR_BUFFER_BIT, just write it and call for auto completition, your IDE should suggest you the right location or automatically insert the right import if you set it up properly
What it looks also missing (maybe you didn't report it) is glVertexAttribPointer
If still it does not work, come back to the basic test triangle, be sure it works and then start building up from there. Move it outside the renderer in its own class, rich it with more geometry, use indexed drawing, ecc. Each step control it works, if it doesn't, then the error lies in your last modifications.

JOGL GL_SELECT picking fails

I am using the GL_SELECT method to achieve mouse selection in OpenGL using JOGL Java Library.
I know the method is deprecated and such, but it is a simple school assignment and this should do it.
However I am having some trouble: even though something is rendered in GL_SELECT mode, glRenderMode(GL_RENDER) returns zero hits. The problem is deterministic, but I don't see a kind of pattern; for example, if I have a sphere in the center, it works if I click on its upper part, but not on its lower part. For a cube, it only won't work on one specific face. For a rectangle it works alright.
I have tested commenting out the glRenderMode(GL_SELECT) to check if something was indeed being rendered and yes, I could see the shape, but even so glRenderMode(GL_RENDER) gave me zero.
EDIT: I have also tested removing the call to gluPickMatrix() and the glRenderMode(GL_SELECT), which gave me exactly the same as the normal (non-picking) render, so the projection and model view matrixes are set up correctly I think.
So, I don't think I am rendering incorrectly in select mode. What can be going on?
EDIT: maybe this could be a hardware problem, as the method is deprecated. Is that possible?
Thanks in advance.
// Get required information
point.y = getHeight() - point.y;
gl.glGetIntegerv(GL2.GL_VIEWPORT, view, 0);
// Setup OpenGL for selection
gl.glSelectBuffer(64, buffer);
gl.glRenderMode(GL2.GL_SELECT);
gl.glInitNames();
// Setup projection matrix
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPushMatrix();
gl.glLoadIdentity();
Util.glu.gluPickMatrix( point.x, point.y, 5.0, 5.0, view, 0 );
Util.glu.gluPerspective(camera.getFieldOfView(), getWidth() * 1.0 / getHeight(),
camera.getCloseDistance(), camera.getFarDistance() );
// Setup model view matrix for rendering
gl.glMatrixMode(GL2.GL_MODELVIEW);
camera.setView(gl); // Set to model view and use glLookAt
gl.glClear( GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT );
// Render objects
for(int i = 0; i < shapeList.size(); i++) {
gl.glPushName(i);
// Execute transformations for translation/rotation/scale and render shape
shapeList.get(i).display(gl, false);
gl.glPopName();
}
// Process hits
hits = gl.glRenderMode(GL2.GL_RENDER);
System.out.println("Hits = " + hits);
// ... Process hits here ...
// Reset matrixes
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glPopMatrix();
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
camera.setView function:
public void setView( GL2 gl ) {
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
Util.glu.gluLookAt( eye[Axis.X], eye[Axis.Y], eye[Axis.Z],
target[Axis.X], target[Axis.Y], target[Axis.Z],
up[Axis.X], up[Axis.Y], up[Axis.Z] );
}

Ambiguous results with Frame Buffers in libgdx

I am getting the following weird results with the FrameBuffer class in libgdx.
Here is the code that is producing this result:
// This is the rendering code
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stage.act();
stage.draw();
fbo.begin();
batch.begin();
batch.draw(heart, 0, 0);
batch.end();
fbo.end();
test = new Image(fbo.getColorBufferTexture());
test.setPosition(256, 256);
stage.addActor(test);
}
//This is the initialization code
#Override
public void show() {
stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
atlas = Assets.getAtlas();
batch = new SpriteBatch();
background = new Image(atlas.findRegion("background"));
background.setFillParent(true);
heart = atlas.findRegion("fluttering");
fbo = new FrameBuffer(Pixmap.Format.RGBA8888, heart.getRegionWidth(), heart.getRegionHeight(), false);
stage.addActor(background);
Image temp = new Image(new TextureRegion(heart));
stage.addActor(temp);
}
Why is it that I am getting the heart that I drew on the frame buffer to get flipped and be smaller than the original one though the frame buffer width and height are the same as that of the image (71 x 72).
Your SpriteBatch is using the wrong projection matrix. Since you are rendering to a custom sized FrameBuffer you will have to manually set one.
projectionMatrix = new Matrix4();
projectionMatrix.setToOrtho2D(0, 0, heart.getRegionWidth(), heart.getRegionHeight());
batch.setProjectionMatrix(projectionMatrix);
To solve this, the frame buffer has to have a width and height equal to that of stage, like this:
fbo = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);

OpenGL texture colors are wrong

I've made a simple program that cretes an Ortho perspective, and puts a texture containing a png on a quad
However, I can't figure out why some of the colors are displayed all jumbled.
The png looks like this (the white rectangle in the middle is transparent):
The quad in my OpenGL program looks like this:
Below is the code for initializing OpenGL as well as what goes on in the method called by the OpenGL thread.
I'm using JOGL.
public void init(GLAutoDrawable gLDrawable) {
gl.glGenTextures(1, textureId, 0);
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]);
gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST);
gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT);
gl.glTexParameterf(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT);
BufferedImage image = null;
try {
image = ImageIO.read(new File("d:\\temp\\projects\\openglTest1\\texTest.png"));
} catch (IOException e1) {e1.printStackTrace();}
DataBufferByte dataBufferByte = (DataBufferByte) image.getRaster().getDataBuffer();
Buffer imageBuffer = ByteBuffer.wrap(dataBufferByte.getData());
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_RGBA, GL.GL_UNSIGNED_BYTE, imageBuffer);
gl.glEnable(GL2.GL_TEXTURE_2D);
gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnable(GL2.GL_BLEND_SRC);
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);
gl.glHint(GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
}
//this is called by the OpenGL Thread
public void display(GLAutoDrawable gLDrawable) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
gl.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
gl.glEnableClientState(GLPointerFunc.GL_TEXTURE_COORD_ARRAY);
gl.glFrontFace(GL2.GL_CCW);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, textureBuffer);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_BYTE, indexBuffer);
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
}
This is puzzling to me because, while I'm not an OpenGL expert I tried to understand what all the above OpenGL commands do before using them. In fact, I've dont the same thing on Android, and everything is displayed ok, but when doing it in Java with JOGL I get this result described here. The only thing I'm doing different is the way I load the png image. On Adroid there's a helper method like this:
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapStatic, 0);
while with JOGL I'm doing my own loading via:
try {
image = ImageIO.read(new File("d:\\temp\\projects\\openglTest1\\texTest.png"));
} catch (IOException e1) {e1.printStackTrace();}
DataBufferByte dataBufferByte = (DataBufferByte) image.getRaster().getDataBuffer();
Buffer imageBuffer = ByteBuffer.wrap(dataBufferByte.getData());
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_RGBA, GL.GL_UNSIGNED_BYTE, imageBuffer);
as detailed above.
==UPDATE==
As per jcadam's comment, I've tried setting the format of the pixel data to GL_BGRA like so:
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL2.GL_RGBA, image.getWidth(), image.getHeight(), 0, GL2.GL_BGRA, GL.GL_UNSIGNED_BYTE, imageBuffer);
The colors are still jumbled, but it's a different jumble this time:
How can I find out what particular format my png image is in?
== UPDATE 2 - solution implementation ==
Ok, first, I want to thank jcadam, rotoglup and Tim for pointing me in the right direction.
In short, the issue was that the way in which Java is ordering the pixels when decoding an image is not always the good order for passing to OpenGL. More precisely, if you do not have an Alpha Channel in your image, then it's ok but if you do have an alpha channel the order is bad and some colors will be jumbled.
Now, I started off by making my own manual implementation which works ok for 32bit PNGs and 24 bit JPEGs:
public void texImage2D(File imageLocation,GL gl) {
BufferedImage initialImage = null;
try {
initialImage = ImageIO.read(imageLocation);
} catch (IOException e1) {
throw new RuntimeException(e1.getMessage(), e1);
}
int imgHeight = initialImage.getHeight(null);
int imgWidth = initialImage.getWidth(null);
ColorModel cm = initialImage.getColorModel();
boolean hasAlpha = cm.hasAlpha();
Buffer buffer = null;
int openGlInternalFormat = -1;
int openGlImageFormat = -1;
if(!hasAlpha) {
DataBufferByte dataBufferByte = (DataBufferByte) initialImage.getRaster().getDataBuffer();
buffer = ByteBuffer.wrap(dataBufferByte.getData());
openGlInternalFormat = GL2.GL_RGB;
openGlImageFormat = GL2.GL_BGR;
} else {
openGlInternalFormat = GL2.GL_RGBA;
openGlImageFormat = GL2.GL_RGBA;
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, imgWidth, imgHeight, 4, null);
ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] { 8, 8, 8, 8 },
true, false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
BufferedImage bufImg = new BufferedImage(colorModel,
raster, false,
null);
Graphics2D g = bufImg.createGraphics();
g.drawImage(initialImage, null, null);
DataBufferByte imgBuf = (DataBufferByte) raster.getDataBuffer();
byte[] bytes = imgBuf.getData();
buffer = ByteBuffer.wrap(bytes);
g.dispose();
}
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, openGlInternalFormat, imgWidth, imgHeight, 0, openGlImageFormat, GL.GL_UNSIGNED_BYTE, buffer);
}
however I later found out that JOGL has its own helper tools for this, and this is in fact what I ended up using:
//this code should be called in init(), to load the texture:
InputStream stream = new FileInputStream("d:\\temp\\projects\\openglTest1\\texTest.png");
TextureData data = TextureIO.newTextureData(gl.getGLProfile(),stream, false, "png");
Texture myTexture = TextureIO.newTexture(data);
//this code should be called in the draw/display method, before the vertices drawing call
myTexture.enable(gl);
myTexture.bind(gl);
It looks like ABGR to me. If you just look at the colors:
png red (A1,B0,G0,R1) looks like
opengl red (R1,G0,B0,A1)
png bluegreen (A1, B1, G1, R0) looks like
opengl white (R1, G1, B1, A0)
png blue (A1, B1, G0, R0) looks like
opengl yellow (R1, G1, B0, A0)
png clear (A0, B?, G?, R?) could be
ogl bluegreen (R0, B?, G?, A?)
If opengl transparency is disabled then the alpha channel wouldn't matter.
Hmm... It looks like a pixel format problem. You could get more specific and try GL_RGBA8, GL_RGBA16, etc. Is this an 8-bit PNG rather than 24 or 32? Is there not an alpha channel (in which case use GL_RGB rather than GL_RGBA)?
Just out of a quick search (I don't have any actual experience with Java ImageIO), it seems that Java has a native ARGB byte ordering, you may take a look at this source code for inspiration.