Pootle's picture

Lighting in GLControl

At the moment I am using Tao and the SimpleOpenGlControl for my 3D application, but as I need it to work both in Linux and Windows environments I am thinking about converting to OpenTK. I have set up a simple test project, which just renders a 3D cube. Using OpenTK and the GLControl, the cube isn't lit properly (all faces have the same colouring) when I draw it using quads. However when I draw a cube using glutSolidCube, the lighting is fine. Both ways work perfectly when I use Tao and the SimpleOpenGlControl (I just did a straight conversion to OpenTK, no extra code). I need to render other (non-cubeish) objects using quads in my proper 3D application, so can't use glutSolidCube there.

Does anybody know what I am doing wrong? Here's my code (drawing is done in display(), change drawQuads to draw using quads or glut):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Tao.FreeGlut;
using OpenTK.OpenGL;
//using Tao.OpenGl;
 
namespace LightingForm
{
    public partial class Form1 : Form
    {
        private bool loaded = false;
 
        public Form1()
        {
            InitializeComponent();
        }   
 
        // initialisation
        private void init()
        {
            // initialise Glut
            Glut.glutInit();
 
            float[] mat_specular = { 1.0f, 1.0f, 1.0f, 1.0f };
            float[] mat_shininess = { 50.0f };
            float[] light_position = { 1.0f, 1.0f, 1.0f, 0.0f };
            float[] light_ambient = { 0.5f, 0.5f, 0.5f, 1.0f };
 
            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.ShadeModel(OpenTK.OpenGL.Enums.ShadingModel.Smooth);
 
            GL.Materialv(OpenTK.OpenGL.Enums.MaterialFace.Front, OpenTK.OpenGL.Enums.MaterialParameter.Specular, mat_specular);
            GL.Materialv(OpenTK.OpenGL.Enums.MaterialFace.Front, OpenTK.OpenGL.Enums.MaterialParameter.Shininess, mat_shininess);
            GL.Lightv(OpenTK.OpenGL.Enums.LightName.Light0, OpenTK.OpenGL.Enums.LightParameter.Position, light_position);
            GL.Lightv(OpenTK.OpenGL.Enums.LightName.Light0, OpenTK.OpenGL.Enums.LightParameter.Ambient, light_ambient);
            GL.Lightv(OpenTK.OpenGL.Enums.LightName.Light0, OpenTK.OpenGL.Enums.LightParameter.Diffuse, mat_specular);
 
            GL.Enable(OpenTK.OpenGL.Enums.EnableCap.Lighting);
            GL.Enable(OpenTK.OpenGL.Enums.EnableCap.Light0);
            GL.Enable(OpenTK.OpenGL.Enums.EnableCap.DepthTest);
            GL.Enable(OpenTK.OpenGL.Enums.EnableCap.ColorMaterial);
            GL.Enable(OpenTK.OpenGL.Enums.EnableCap.CullFace);
        }
 
        // draws the scene
        private void display()
        {
            GL.Clear(OpenTK.OpenGL.Enums.ClearBufferMask.ColorBufferBit | OpenTK.OpenGL.Enums.ClearBufferMask.DepthBufferBit);
 
            GL.PushMatrix();
            {
                GL.Rotate(10, 1, 0, 0);
                GL.Rotate(10, 0, 1, 0);
 
                GL.Color3(0.0, 0.5, 1.0);
 
 
                // *** SET THIS TO FALSE TO CHANGE TO GLUTSOLIDCUBE ***
                bool drawQuads = true;
 
                if (drawQuads)
                {
                    GL.Begin(OpenTK.OpenGL.Enums.BeginMode.Quads);
                    {
                        // front face
                        GL.Normal3(0, 0, 1);
                        GL.Vertex3(-0.5, -0.5, 0.5);
                        GL.Vertex3(0.5, -0.5, 0.5);
                        GL.Vertex3(0.5, 0.5, 0.5);
                        GL.Vertex3(-0.5, 0.5, 0.5);
 
                        // back face
                        GL.Normal3(0, 0, -1);
                        GL.Vertex3(-0.5, -0.5, -0.5);
                        GL.Vertex3(-0.5, 0.5, -0.5);
                        GL.Vertex3(0.5, 0.5, -0.5);
                        GL.Vertex3(0.5, -0.5, -0.5);
 
                        // top face
                        GL.Normal3(0, 1, 0);
                        GL.Vertex3(-0.5, 0.5, -0.5);
                        GL.Vertex3(-0.5, 0.5, 0.5);
                        GL.Vertex3(0.5, 0.5, 0.5);
                        GL.Vertex3(0.5, 0.5, -0.5);
 
                        // bottom face
                        GL.Normal3(0, -1, 0);
                        GL.Vertex3(-0.5, -0.5, -0.5);
                        GL.Vertex3(0.5, -0.5, -0.5);
                        GL.Vertex3(0.5, -0.5, 0.5);
                        GL.Vertex3(-0.5, -0.5, 0.5);
 
                        // right face
                        GL.Normal3(1, 0, 0);
                        GL.Vertex3(0.5, -0.5, -0.5);
                        GL.Vertex3(0.5, 0.5, -0.5);
                        GL.Vertex3(0.5, 0.5, 0.5);
                        GL.Vertex3(0.5, -0.5, 0.5);
 
                        // left face
                        GL.Normal3(-1, 0, 0);
                        GL.Vertex3(-0.5, -0.5, -0.5);
                        GL.Vertex3(-0.5, -0.5, 0.5);
                        GL.Vertex3(-0.5, 0.5, 0.5);
                        GL.Vertex3(-0.5, 0.5, -0.5);
                    }
                    GL.End();
                }
                else
                {
                    Glut.glutSolidCube(1.0);
                }
            }
            GL.PopMatrix();
 
            GL.Flush();
        }
 
        // resizes the viewport
        private void reshape(int w, int h)
        {
            GL.Viewport(0, 0, w, h);
            GL.MatrixMode(OpenTK.OpenGL.Enums.MatrixMode.Projection);
            GL.LoadIdentity();
            if (w <= h)
            {
                GL.Ortho(-1.5, 1.5, -1.5 * (double)h / (double)w, 1.5 * (double)h / (double)w, -10.0, 10.0);
            }
            else
            {
                GL.Ortho(-1.5 * (double)w / (double)h, 1.5 * (double)w / (double)h, -1.5, 1.5, -10.0, 10.0);
            }
            GL.MatrixMode(OpenTK.OpenGL.Enums.MatrixMode.Modelview);
            GL.LoadIdentity();
        }
 
        // GLControl resize event handler
        private void glControl1_Resize(object sender, EventArgs e)
        {
            if (!loaded) return;
            reshape(glControl1.Width, glControl1.Height);
        }
 
        // GLControl paint event handler
        private void glControl1_Paint(object sender, PaintEventArgs e)
        {
            if (!loaded) return;
            display();
            glControl1.SwapBuffers();
        }
 
        // Main form load event handler
        private void Form1_Load(object sender, EventArgs e)
        {
            loaded = true;
            init();
            reshape(glControl1.Width, glControl1.Height);
        }
    }
}

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

What happens if you change this:

GL.Normal3(1, 0, 0);    // calls glNormal3i

to this?

GL.Normal3(1.0, 0.0, 1.0);   // calls glNormal3d

Also, keep in mind that you can use Tao.OpenGl with OpenTK.GLControl (makes sense if you have a lot of existing code). OpenTK.OpenGL may be a bit cleaner than Tao.OpenGl, but functionally they are identical.

Pootle's picture

Thanks ever so much for spotting that! Yes, using doubles in the call to GL.Normal3() means that the cube is lit properly.
Thanks also for the tip about using Tao.OpenGl with the OpenTK.GLControl, I had thought about that but I wanted to keep the number of dlls to a minimum.

the Fiddler's picture

You are welcome :)

Regarding the amount of dlls, you can always merge everything into the exe with ILMerge on Windows or monomerge on Linux/MacOS. This will also reduce size, sometimes dramatically.

For example, a simple "hello world" OpenTK application can be typically compressed to 10-20KB or so. There's little point in distributing the whole 1.5MB OpenTK.dll in this case.

objarni's picture

How much have you tried ILMerge? Has there been any issues with it yet?

It's quite a feat, but I wouldn't buy into it if it cases any harm - OpenTK's 1.5 mb is simply quite small 2008 anyway :)

the Fiddler's picture

I've tried monolinker and I'm planning to test monomerge (if I manage find how to build it). The first tools copies all dependencies of your exe and (optionally) strips them (IIRC, OpenTK.dll fell to 20KB or so for Examples.exe). The latter can merge everything into a single exe.

I haven't used ILMerge myself yet, but it's quite widespread and there's a lot of material available. I'm going to use it in the OpenTK demo though.

And while 1.5MB does not sound much, there are still dialup users out there. Besides, if you are using dependencies like Tao.OpenGl (1.2MB or so), it adds up to quite a lot. No point in distributing 3MB you can go down to 300KB (someone has to pay for the bandwidth, too).

objarni's picture

Problem is, .NET/mono 1.2.6 itself is not that widespread. That's like 23 mb dotnetfx.exe for windows, don't know the numbers for linux. What's the numbers for mac?

the Fiddler's picture

That's not my experience at all. Almost all computers have .Net 2.0/Mono 1.2.x installed these days. Only vanilla WinXP SP2 computers don't have it, and SP3 will solve that anyway.

If this is a problem, on Linux you can create a native executable that bundles mono, with mkbundle (don't know if this works on windows). But I really don't think this is a problem.

Pootle's picture

The user base for my app is going to be very small so I don't think I need to worry about the availability of .net 2.0/Mono 1.2.x, they are all big enough and clever enough to work out how to update their installations accordingly. I think I might look into extracting the GLControl from the OpenTK project and building it directly into my project though, then I won't need to worry about ILMerge, and I will save time converting to OpenTK from Tao.OpenGL.

By the way, is the fact that Normal3() works differently for integer and double arguments a bug?

the Fiddler's picture

No, this is the expected behavior: http://www.ugrad.cs.ubc.ca/~cs314/opengl/glNormal.html

GL.Normal3 always converts its parameters to floats, by dividing the value you specify by MAX_INT (or MAX_SHORT, or MAX_BYTE etc). You should always use the float or double versions to avoid this conversion.

Extracting GLControl won't be easy. It depends on other classes (GraphicsContext, GraphicsMode), which in turn depend on platform specific methods. It might be easier to update Tao.SimpleOpenGlControl itself to run on Linux, using Tao.Platform.X11 from SVN.

Or if you don't care about size, just include OpenTK.dll and stop worrying about it :)

objarni's picture

Fiddler: Is it OK to include precisely the .cs files of OpenTK I actually use in eg. Dogfight? That would be another way of "bundling" to one exe, besides ILMerge?

.NET availability: SP3 will be a nice upgrade for the world, then :)
Ubuntu: which Mono will it be? (hoping 1.9, guessing 1.2.6 but that's enough for OpenTK, right?)
Mac: how is the situation here?