Sign in to follow this  
yhbae

OpenGL Question on how lighting works in OpenGL

Recommended Posts

yhbae    100
Hi everyone,

I've been learning how to code OpenGL (ES to be exact, for Android platform) for the past couple of weeks. I've bumped into a problem and I am unable to progress any further at the moment. I'd appreciate if someone can suggest what I'm doing wrong here.

I've drawn couple of walls here. The goal was to draw walls with spot lights so that I can see the "ring" of lights. Although I had a tunnel working with just 2 triangles per wall, I realized there just isn't enough vertices to create a realistic spot light, so I went further from here.

This is what I've done so far:

1. I've created a basic wall that contains 5x5 squares which in turn are made out of 2 triangles. Since this is going to be a tunnel, I've assigned normal vectors at each vertices to be (1,0,0) for the "left" wall.

2. I've used translations and rotations to create more walls.

3. I've placed the light source on top of the bottom surface, facing (-1, 0, 0). I am expecting a circular light pattern on the left wall and no other walls.


Here's the link to the render:

http://www.flickr.com/photos/yhb_photos/5173472812/


Explanation of what you are seeing in the picture:

1. All vertex points positions are ok - its working as intended.

2. For some reason, all walls further than 2f from the origin shows white. Only the left wall connected to the ceiling is rendered correctly.

3. Floor shows some weird patterns and even a hole in the middle. The light does not face in this direction at all. In fact, if I bring the lower left wall into the distance 1f, it looks identical to this floor.

Any idea what's going on here?

Thank you.

Share this post


Link to post
Share on other sites
yhbae    100
There's a lot of code already so I'll do my best to post only the relevant pieces. :)


// Initialization

private float[] lightAmbient = { 0.1f, 0.1f, 0.1f, 1f };
private float[] lightDiffuse = { 1f, 1f, 1f, 1f };
private float[] lightSpecular = { 1f, 1f, 1f, 1f };
private float[] lightPosition = { 0f, 0f, -7f, 1f };
private float[] lightDirection = { -1f, 0f, 0f };

private FloatBuffer lightAmbientBuffer;
private FloatBuffer lightDiffuseBuffer;
private FloatBuffer lightSpecularBuffer;
private FloatBuffer lightPositionBuffer;
private FloatBuffer lightDirectionBuffer;

byteBuf = ByteBuffer.allocateDirect(lightAmbient.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
lightAmbientBuffer = byteBuf.asFloatBuffer();
lightAmbientBuffer.put(lightAmbient);
lightAmbientBuffer.position(0);

byteBuf = ByteBuffer.allocateDirect(lightDiffuse.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
lightDiffuseBuffer = byteBuf.asFloatBuffer();
lightDiffuseBuffer.put(lightDiffuse);
lightDiffuseBuffer.position(0);

ByteBuf = ByteBuffer.allocateDirect(lightSpecular.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
lightSpecularBuffer = byteBuf.asFloatBuffer();
lightSpecularBuffer.put(lightSpecular);
lightSpecularBuffer.position(0);

byteBuf = ByteBuffer.allocateDirect(lightPosition.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
lightPositionBuffer = byteBuf.asFloatBuffer();
lightPositionBuffer.put(lightPosition);
lightPositionBuffer.position(0);

byteBuf = ByteBuffer.allocateDirect(lightDirection.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
lightDirectionBuffer = byteBuf.asFloatBuffer();
lightDirectionBuffer.put(lightDirection);
lightDirectionBuffer.position(0);

gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();

GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f);

gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();

gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbientBuffer);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuseBuffer);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightSpecularBuffer);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPositionBuffer);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPOT_DIRECTION, lightDirectionBuffer);

gl.glLightf(GL10.GL_LIGHT0, GL10.GL_SPOT_CUTOFF, 5f);
gl.glLightf(GL10.GL_LIGHT0, GL10.GL_SPOT_EXPONENT, 40f);

gl.glEnable(GL10.GL_LIGHT0);

gl.glEnable(GL10.GL_TEXTURE_2D); // Enable Texture Mapping
gl.glShadeModel(GL10.GL_SMOOTH); // Enable Smooth Shading
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
gl.glClearDepthf(1f); // Depth Buffer Setup
gl.glEnable(GL10.GL_DEPTH_TEST); // Enables Depth Testing
gl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing To Do

gl.glEnable(GL10.GL_NORMALIZE);

gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);


// "OnDraw"

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();

gl.glEnable(GL10.GL_LIGHTING);

gl.glTranslatef(-1f, 0.0f, -5.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);

gl.glLoadIdentity();
gl.glTranslatef(0.0f, 2.0f, -5.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);

gl.glLoadIdentity();
gl.glRotatef(-90f, 0f, 0f, 1f);
gl.glTranslatef(-2f, 0f, -5.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);

gl.glLoadIdentity();
gl.glRotatef(90f, 0f, 0f, 1f);
gl.glTranslatef(0.0f, 0.0f, -5.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);
gl.glTranslatef(0.0f, 0.0f, -2.0f);
mMeshWallLeft.draw(gl);


// mMeshWallLeft is an instance of a Class MeshWallLeft as follows:


public MeshWallLeft(int ny, int nz) {

ByteBuffer byteBuf;

verticesLeft = new float[ny*nz*12];

int count = 0;
for (float y = -1; y < 1f-0.01; y += 2f/ny) {
for (float z = 1; z > -1f+0.01; z -= 2f/nz) {

// vertex 1
verticesLeft[count++] = -1f; // 0
verticesLeft[count++] = y; // 1
verticesLeft[count++] = z - 2f/nz; // 2

// vertex 2
verticesLeft[count++] = -1f; // 3
verticesLeft[count++] = y; // 4
verticesLeft[count++] = z; // 5

// vertex 3
verticesLeft[count++] = -1f; // 6
verticesLeft[count++] = y + 2f/ny; // 7
verticesLeft[count++] = z - 2f/nz; // 8

// vertex 4
verticesLeft[count++] = -1f; // 9
verticesLeft[count++] = y + 2f/ny; // 10
verticesLeft[count++] = z; // 11
}
}

normalLeft = new float[ny*nz*12];

count = 0;
for (float y = -1; y < 1f-0.01; y += 2f/ny) {
for (float z = 1; z > -1f+0.01; z -= 2f/nz) {
normalLeft[count++] = 1f; // 0
normalLeft[count++] = 0f; // 1
normalLeft[count++] = 0f; // 2
normalLeft[count++] = 1f; // 3
normalLeft[count++] = 0f; // 4
normalLeft[count++] = 0f; // 5
normalLeft[count++] = 1f; // 6
normalLeft[count++] = 0f; // 7
normalLeft[count++] = 0f; // 8
normalLeft[count++] = 1f; // 9
normalLeft[count++] = 0f; // 10
normalLeft[count++] = 0f; // 11
}
}


indicesLeft = new byte[ny*nz*6];

count = 0;
int count2 = 0;
for (float y = -1; y < 1f-0.01; y += 2f/ny) {
for (float z = 1; z > -1f+0.01; z -= 2f/nz) {
indicesLeft[count++] = (byte) (1 + count2); // 0
indicesLeft[count++] = (byte) (0 + count2); // 1
indicesLeft[count++] = (byte) (3 + count2); // 2
indicesLeft[count++] = (byte) (3 + count2); // 3
indicesLeft[count++] = (byte) (0 + count2); // 4
indicesLeft[count++] = (byte) (2 + count2); // 5
count2 += 4;
}
}


byteBuf = ByteBuffer.allocateDirect(verticesLeft.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBufferLeft = byteBuf.asFloatBuffer();
vertexBufferLeft.put(verticesLeft);
vertexBufferLeft.position(0);

byteBuf = ByteBuffer.allocateDirect(normalLeft.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
normalBufferLeft = byteBuf.asFloatBuffer();
normalBufferLeft.put(normalLeft);
normalBufferLeft.position(0);

indexBufferLeft = ByteBuffer.allocateDirect(indicesLeft.length);
indexBufferLeft.put(indicesLeft);
indexBufferLeft.position(0);
}

And here's the "Draw" method for the above class:

private void drawLeft(GL10 gl) {
//Bind our only previously generated texture in this case
gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesLeft[0]);

//Point to our buffers
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);

//Set the face rotation
gl.glFrontFace(GL10.GL_CCW);

//Enable the vertex and texture state
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBufferLeft);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBufferLeft);
gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBufferLeft);

//Draw the vertices as triangles, based on the Index Buffer information
gl.glDrawElements(GL10.GL_TRIANGLES, indicesLeft.length, GL10.GL_UNSIGNED_BYTE, indexBufferLeft);

//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}


I hope I didn't miss anything....

Share this post


Link to post
Share on other sites
karwosts    840
I've gone through it a little bit and I haven't seen anything that would really indicate some of the strange artifacts that you see in your image. Can you call glGetError() during the rendering loop and assert if it is nonzero? That's always the first thing to check anytime you have unexpected behavior.

Secondly, your environment would really benefit from either finer tessellation or per-pixel lighting (shaders required).

You're never going to see a nice cone of light with such large quads, you'll just have ugly gradients across the surface which will not look anything like reality. Read chapter 2 of this page: 2. Poor Tessellation Hurts Lighting to understand what I mean.

Share this post


Link to post
Share on other sites
yhbae    100
Quote:
Original post by karwosts
I've gone through it a little bit and I haven't seen anything that would really indicate some of the strange artifacts that you see in your image. Can you call glGetError() during the rendering loop and assert if it is nonzero? That's always the first thing to check anytime you have unexpected behavior.


Within the "draw" method, I added the glGetError() all over the places and checked for a non-zero value. I got nothing, so I guess all gl functions went through ok.

Quote:

Secondly, your environment would really benefit from either finer tessellation or per-pixel lighting (shaders required).


Understood. I did code the class in such a way that I can specify the number of squares in my mesh wall. Unfortunately, I also realized that # of indices are specified in a byte variable? So am I limited to only 255 indices per draw call?

And about the use of shaders - I have no idea how to go about doing this yet. Is it available in OpenGL ES like the one I use for the Android phone environment?

Quote:

You're never going to see a nice cone of light with such large quads, you'll just have ugly gradients across the surface which will not look anything like reality. Read chapter 2 of this page: 2. Poor Tessellation Hurts Lighting to understand what I mean.


Thanks for the link, very useful. :)

So in summary, I still have no idea where it went wrong. :(

Share this post


Link to post
Share on other sites
yhbae    100
Just looking through the code again - is it fundamentally correct to write a code that draws a wall at a fixed position and use the same code + translate/rotate to draw other walls?

Thank you.

Share this post


Link to post
Share on other sites
karwosts    840
Quote:

I also realized that # of indices are specified in a byte variable? So am I limited to only 255 indices per draw call?

I'm not sure where you saw this, but it isn't true. You can use shorts or ints to specify your index variables, so you can have 2^32 verts in a single call if you want to.


Quote:

Just looking through the code again - is it fundamentally correct to write a code that draws a wall at a fixed position and use the same code + translate/rotate to draw other walls?


There's nothing fundamentally wrong with this approach. Typically you might make your meshes in a modeling program and then import the vertices, but you can draw them procedurally if you want.


Quote:

And about the use of shaders - I have no idea how to go about doing this yet. Is it available in OpenGL ES like the one I use for the Android phone environment?

Yes they are available and they are what you should eventually be using, but they have a bit of a learning curve to them so if you're satisfied where you are now you can keep on without them for now. But eventually you'll want to start looking into them. I recommend this site for help:
lighthouse3d GLSL tutorial

Quote:

So in summary, I still have no idea where it went wrong. :(

I think you should try some finer tessellated walls, it may help you spot what is going on. Even if you do get it to work it will still look terrible on such large polygons, so you'll want to do it eventually anyway. It may help you see what's going on with your lights when you have more vertices to process.

Share this post


Link to post
Share on other sites
yhbae    100
Quote:

I'm not sure where you saw this, but it isn't true. You can use shorts or ints to specify your index variables, so you can have 2^32 verts in a single call if you want to.


You are right. :) The example I used happened to use the byte buffer so I naturally thought that was the only option. As soon as I flipped it over to the short buffer, it would let me have larger mesh. Thank you.

Quote:

There's nothing fundamentally wrong with this approach. Typically you might make your meshes in a modeling program and then import the vertices, but you can draw them procedurally if you want.


Good to hear that the approach is ok. I can stop worrying about this concern now. :)

Quote:

Yes they are available and they are what you should eventually be using, but they have a bit of a learning curve to them so if you're satisfied where you are now you can keep on without them for now. But eventually you'll want to start looking into them. I recommend this site for help:
lighthouse3d GLSL tutorial


Will definitely look into this link once I get the current part ironed out. Thanks.

Quote:

I think you should try some finer tessellated walls, it may help you spot what is going on. Even if you do get it to work it will still look terrible on such large polygons, so you'll want to do it eventually anyway. It may help you see what's going on with your lights when you have more vertices to process.


For now, I'll try the finer tessellated walls and see what happens. Thanks!

Share this post


Link to post
Share on other sites
yhbae    100
Ok, here's the new result.

http://www.flickr.com/photos/yhb_photos/5178129104/

I've increased the tessellation to 100x100 per wall.

Then I noticed a part of a circle on the ceiling, so I pushed the light location by another -1 on the z-axis.

Now I can clearly see a circle on the ceiling! Remember, my light direction vector is (-1, 0, 0)!

This is driving me crazy. :)

Question: If I apply a rotation, does this change the normal vectors assigned to the wall? (I expect this is the case). Also does rotation affect the light direction vector? (I don't expect this is the case).

Thank you.

[Edited by - yhbae on November 15, 2010 3:17:55 AM]

Share this post


Link to post
Share on other sites
karwosts    840
Well I've looked up and down your code and at your image but I'm completely baffled by your result, I have no idea how you are getting that image from that code.

Do you have more than one light in the scene? The light doesn't look like a spotlight at all, it's actually brighter outside the spotlight cone than inside it, so I'm not really sure how that's possible. I think there must be something wrong elsewhere in your application that you haven't shown or your data is corrupted or something, because I just can't explain that from the code I'm seeing. Sorry I know this must be frustrating, but believe me that it is not usually this difficult!

Quote:

Question: If I apply a rotation, does this change the normal vectors assigned to the wall? (I expect this is the case). Also does rotation affect the light direction vector? (I don't expect this is the case).


Yes, the normal vector is also transformed by the rotation. For the light direction, it is affected by rotations before you specify the direction, not after.

So this: ( I know the syntax is illegal, but it illustrates the point)

gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPOT_DIRECTION, [-1,0,0]);
glRotate(180,0,1,0);

defines a light direction in the negative x axis, while this:


glRotate(180,0,1,0);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPOT_DIRECTION, [-1,0,0]);

defines a light direction down the positive x axis.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Similar Content

    • By povilaslt2
      Hello. I'm Programmer who is in search of 2D game project who preferably uses OpenGL and C++. You can see my projects in GitHub. Project genre doesn't matter (except MMO's :D).
    • By ZeldaFan555
      Hello, My name is Matt. I am a programmer. I mostly use Java, but can use C++ and various other languages. I'm looking for someone to partner up with for random projects, preferably using OpenGL, though I'd be open to just about anything. If you're interested you can contact me on Skype or on here, thank you!
      Skype: Mangodoor408
    • By tyhender
      Hello, my name is Mark. I'm hobby programmer. 
      So recently,I thought that it's good idea to find people to create a full 3D engine. I'm looking for people experienced in scripting 3D shaders and implementing physics into engine(game)(we are going to use the React physics engine). 
      And,ye,no money =D I'm just looking for hobbyists that will be proud of their work. If engine(or game) will have financial succes,well,then maybe =D
      Sorry for late replies.
      I mostly give more information when people PM me,but this post is REALLY short,even for me =D
      So here's few more points:
      Engine will use openGL and SDL for graphics. It will use React3D physics library for physics simulation. Engine(most probably,atleast for the first part) won't have graphical fron-end,it will be a framework . I think final engine should be enough to set up an FPS in a couple of minutes. A bit about my self:
      I've been programming for 7 years total. I learned very slowly it as "secondary interesting thing" for like 3 years, but then began to script more seriously.  My primary language is C++,which we are going to use for the engine. Yes,I did 3D graphics with physics simulation before. No, my portfolio isn't very impressive. I'm working on that No,I wasn't employed officially. If anybody need to know more PM me. 
       
    • By Zaphyk
      I am developing my engine using the OpenGL 3.3 compatibility profile. It runs as expected on my NVIDIA card and on my Intel Card however when I tried it on an AMD setup it ran 3 times worse than on the other setups. Could this be a AMD driver thing or is this probably a problem with my OGL code? Could a different code standard create such bad performance?
    • By Kjell Andersson
      I'm trying to get some legacy OpenGL code to run with a shader pipeline,
      The legacy code uses glVertexPointer(), glColorPointer(), glNormalPointer() and glTexCoordPointer() to supply the vertex information.
      I know that it should be using setVertexAttribPointer() etc to clearly define the layout but that is not an option right now since the legacy code can't be modified to that extent.
      I've got a version 330 vertex shader to somewhat work:
      #version 330 uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ModelViewMatrix; layout(location = 0) in vec4 Vertex; layout(location = 2) in vec4 Normal; // Velocity layout(location = 3) in vec3 TexCoord; // TODO: is this the right layout location? out VertexData { vec4 color; vec3 velocity; float size; } VertexOut; void main(void) { vec4 p0 = Vertex; vec4 p1 = Vertex + vec4(Normal.x, Normal.y, Normal.z, 0.0f); vec3 velocity = (osg_ModelViewProjectionMatrix * p1 - osg_ModelViewProjectionMatrix * p0).xyz; VertexOut.velocity = velocity; VertexOut.size = TexCoord.y; gl_Position = osg_ModelViewMatrix * Vertex; } What works is the Vertex and Normal information that the legacy C++ OpenGL code seem to provide in layout location 0 and 2. This is fine.
      What I'm not getting to work is the TexCoord information that is supplied by a glTexCoordPointer() call in C++.
      Question:
      What layout location is the old standard pipeline using for glTexCoordPointer()? Or is this undefined?
       
      Side note: I'm trying to get an OpenSceneGraph 3.4.0 particle system to use custom vertex, geometry and fragment shaders for rendering the particles.
  • Popular Now