Reputation: 948
Background
I am attempting to make a CAD viewer to which I can bring in an STL file and simply rotate, pan, etc. This shouldn't be anything that advanced like a game.
I was having difficulty with lighting which can be seen in a previous post: Previous Post
Problem
Basically I have light on now, and I have my pop and push matrix call outs in the right place. All I am trying to do now is to optimize my lighting to be like a CAD environment. Correct me if I am wrong but I believe the best lighting for viewing a part is always having the light at the camera pointing in the direction of the object. If this is not the optimal way to do lighting, by all means feel free to suggest a better way.
I think this mostly comes down to math. I don't know if there are any built in functions to simply get where the camera is, or if you have to keep track of it.
Here is what I have for my display function:
Private Sub display()
Gl.glEnable(Gl.GL_DEPTH_TEST)
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT)
Gl.glClear(Gl.GL_DEPTH_BUFFER_BIT)
Gl.glPushMatrix()
Gl.glRotatef(rotX, 1.0F, 0.0F, 0.0F)
' Rotate on x
Gl.glRotatef(rotY, 0.0F, 1.0F, 0.0F)
' Rotate on y
Gl.glRotatef(rotZ, 0.0F, 0.0F, 1.0F)
' Rotate on z
Gl.glTranslatef(X, Y, Z)
'Draw x,y,z axis
Gl.glBegin(Gl.GL_LINES)
Gl.glColor4f(0.0F, 1.0F, 0.0F, 1.0F)
' Green for x axis
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glVertex3f(5000.0F, 0.0F, 0.0F)
Gl.glColor3f(1.0F, 0.0F, 0.0F)
' Red for y axis
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glVertex3f(0.0F, 5000.0F, 0.0F)
Gl.glColor3f(0.0F, 0.0F, 1.0F)
' Blue for z axis
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glVertex3f(0.0F, 0.0F, 15.0F)
Gl.glEnd()
' Dotted lines for the negative sides of x,y,z
Gl.glEnable(Gl.GL_LINE_STIPPLE)
' Enable line stipple to
' use a dotted pattern for
' the lines
Gl.glLineStipple(1, &H101)
' Dotted stipple pattern for
' the lines
Gl.glBegin(Gl.GL_LINES)
Gl.glColor3f(0.0F, 1.0F, 0.0F)
' Green for x axis
Gl.glVertex3f(-5000.0F, 0.0F, 0.0F)
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glColor3f(1.0F, 0.0F, 0.0F)
' Red for y axis
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glVertex3f(0.0F, -5000.0F, 0.0F)
Gl.glColor3f(0.0F, 0.0F, 1.0F)
' Blue for z axis
Gl.glVertex3f(0.0F, 0.0F, 0.0F)
Gl.glVertex3f(0.0F, 0.0F, -5000.0F)
Gl.glEnd()
Gl.glDisable(Gl.GL_LINE_STIPPLE)
' Disable the line stipple
Gl.glColor3f(1.0F, 1.0F, 1.0F)
Glut.glutSolidTeapot(5)
Gl.glEnable(Gl.GL_LIGHTING)
Label1.Text = "X=" & lighting_x & ", Y=" & lighting_y & " Z=" & lighting_z
Light1Position = {lighting_x, lighting_y, lighting_z, 0.0F}
' Enable Default Light
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, Light1Ambient)
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, Light1Diffuse)
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, Light1Position)
Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPECULAR, Light1Specular)
Gl.glEnable(Gl.GL_LIGHT1)
Gl.glPopMatrix()
draw_extras()
Gl.glEnable(Gl.GL_COLOR_MATERIAL)
Gl.glColorMaterial(Gl.GL_FRONT, Gl.GL_AMBIENT_AND_DIFFUSE)
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SPECULAR, MaterialSpecular)
Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SHININESS, SurfaceShininess)
Gl.glEnable(Gl.GL_COLOR_MATERIAL)
Glut.glutSwapBuffers()
End Sub
Basically I have lighting_x, lighting_y, and lighting_z as variables to move my light around. I have a variables X, Y, and Z that establish the panning (Gl.glTranslatef(X, Y, Z)). The variables rotx, roty, and rotz set the rotation in the main function using Gl.glRotatef(rotX, 1.0F, 0.0F, 0.0F).
These variables are being applied to the whole scene which makes sense in a CAD environment so that my axis moves as well.
On top of this, I am using the gluLookAt function to create a "zoom to fit" function. From what I understand, the functions in my display function move and rotate my environment but the gluLookAt is moving my camera. Keeping the relation between these must be what is needed for my lighting calculation:
Private Sub zoom_fit()
rotX = 0
rotY = 0
rotZ = 0
rotLx = 0
rotLy = 0
rotLz = 0
Dim i As Integer = 1
Dim maxz As Decimal = -10000
Do Until i >= ListView1.Items.Count
If Convert.ToDecimal(ListView1.Items.Item(i).SubItems(2).Text) > maxz Then
maxz = Convert.ToDecimal(ListView1.Items.Item(i).SubItems(2).Text)
End If
If Convert.ToDecimal(ListView1.Items.Item(i + 1).SubItems(2).Text) > maxz Then
maxz = Convert.ToDecimal(ListView1.Items.Item(i).SubItems(2).Text)
End If
If Convert.ToDecimal(ListView1.Items.Item(i + 2).SubItems(2).Text) > maxz Then
maxz = Convert.ToDecimal(ListView1.Items.Item(i).SubItems(2).Text)
End If
i = i + 4
Loop
rotLz = Convert.ToSingle((maxz - avgz) * 10)
Gl.glMatrixMode(Gl.GL_MODELVIEW)
Gl.glLoadIdentity()
Glu.gluLookAt(rotLx, rotLy, 15.0 + rotLz, 0.0, 0.0, 0.0, _
0.0, 1.0, 0.0)
Glut.glutPostRedisplay()
End Sub
I don't know where the environment puts the camera relative to the origin when it initially runs but I would think that this too would need to be taken into account.
What I can see so far:
The light side of the teapot:
Dark side of the moon (teapot):
All code is written in VB.NET but C# answers would work too.
Upvotes: 0
Views: 1550
Reputation: 54572
The code looks a bit rough, and I will not comment on every detail that you may have to fix. But here are some important aspects that will hopefully get you up and running:
All state needs to be set before the draw calls. In other words, each draw call uses the exact state that was in place at the time of the call. For example, you set some material properties at the end of the display()
function, after all the draw calls have already been made. Those state changes will not affect anything until the next draw call.
You need to be careful about the current transformation when you specify light positions. From the spec:
The current model-view matrix is applied to the position parameter indicated with Light for a particular light source when that position is specified.
So if you want a light source that is stationary relative to the camera, you need to make the glLightfv(.., GL_POSITION, ..)
call while no model transformations are on the matrix stack.
I don't think putting the light exactly at the camera position is very common. For CAD type systems, what I have seen most is a light source positioned at the top left. Or in other words, the light source is placed with a negative x-offset and a positive y-offset relative to the camera position. For better results, it can be beneficial to use more than one light source. For example, placing a (possibly weaker) light source in the opposite direction behind the objects can look really good. But you'll have to play with it to see what works best for you.
Also, even though you probably know this already, but I'll have to add the (almost) obligatory: Most of the OpenGL functionality you are using is obsolete and deprecated. If you are learning, I would strongly recommend to learn "modern OpenGL", meaning a version that is not using the fixed pipeline anymore, and is based on programmable shaders.
Upvotes: 2