WhyYouNoWork
WhyYouNoWork

Reputation: 293

Opengl: how do I affect the lighting on a textured plane?

I have a class I have written to draw a plane and a put a texture on it. I have also made a class for lighting. It appears that my light will affect solid objects, but it doesn't change the lighting on my plane. My plane always appears to have the same lighting no matter what. Any idea why it isn't working?

This is what it looks like when I run the project (the cone shows where the light is coming from): https://i.sstatic.net/g1xOo.png

This is my code:

class Light
{
public:
     //light constructor

    void Draw();

private:
    GLenum m_LightSource;

    float m_Ambient[4];//ambient is light all around
    float m_Specular[4];//gleem that hits an object
    float m_Diffuse[4];//lights oart of an object
    float m_SpotlightWidth;
    float m_Position[4];
    float m_Attenuation;

    float m_MaterialSpecular[4];
    float m_MaterialShine[1];
};

void Light::Draw()
{
        glPushMatrix();
        glTranslatef(m_Position[0], m_Position[1], m_Position[2]); // Move the spotlight.

        // Light properties.
        glLightfv(m_LightSource, GL_AMBIENT, m_Ambient);
        glLightfv(m_LightSource, GL_DIFFUSE, m_Diffuse);
        glLightfv(m_LightSource, GL_SPECULAR, m_Specular);

        glEnable(m_LightSource);// Enable particular light source.

        // Material properties shared by all the spheres.
        glMaterialfv(GL_FRONT, GL_SPECULAR, m_MaterialSpecular);
        glMaterialfv(GL_FRONT, GL_SHININESS, m_MaterialShine);

        if(m_DrawCone)
        {
            // Draw the spotlight cone in wireframe after disabling lighting
            glPushMatrix();
            glDisable(GL_LIGHTING);
            glRotatef(-90.0, 1.0, 0.0, 0.0);
            glColor3f(1.0, 1.0, 1.0);
            glutWireCone(3.0 * tan( m_SpotlightWidth/180.0 * PI ), 3.0, 20, 20);
            glEnable(GL_LIGHTING);
            glPopMatrix();
        }

        float lightPos[] = { 0.0, 3.0, 0.0, 1.0 }; // Spotlight position.
        float spotDirection[] = {0.0, -1.0, 0.0}; // Spotlight direction.

        // Spotlight properties including position.
        glLightfv(m_LightSource, GL_POSITION, lightPos);  
        glLightf(m_LightSource, GL_SPOT_CUTOFF, m_SpotlightWidth);
        glLightfv(m_LightSource, GL_SPOT_DIRECTION, spotDirection);    
        glLightf(m_LightSource, GL_SPOT_EXPONENT, m_Attenuation);


        glPopMatrix();
}


class Plane
{
public:
    Plane();
    Plane(float x, float y, float z, float width, float height, float depth,  string textureName);

    void Draw();

private:
    float m_CenterX, m_CenterY, m_CenterZ, m_Width, m_Height, m_Depth;
    unsigned int m_Texture[1];
    unsigned char m_Colour[3];
    string m_TextureName;
};

void Plane::Draw()
{
    if(m_HasTexture)
    {
    // Turn on OpenGL texturing.
   glEnable(GL_TEXTURE_2D);

     // Activate a texture.
   glBindTexture(GL_TEXTURE_2D, m_Texture[0]); 

    // Map the texture onto a square polygon.
   glBegin(GL_POLYGON);
   glTexCoord2f(0.0, 1.0); glVertex3f(m_CenterX - m_Width, m_CenterY - m_Height, 0.0);
   glTexCoord2f(1.0, 1.0); glVertex3f(m_CenterX + m_Width, m_CenterY - m_Height, 0.0);
   glTexCoord2f(1.0, 0.0); glVertex3f(m_CenterX + m_Width, m_CenterY + m_Height, 0.0);
   glTexCoord2f(0.0, 0.0); glVertex3f(m_CenterX - m_Width, m_CenterY + m_Height, 0.0);
   glEnd();

   // Turn off OpenGL texturing.
   glDisable(GL_TEXTURE_2D);
}

void Plane::LoadTexture(string textureName)         
{
    // Create texture index array.
    glGenTextures(1, m_Texture); 

   // Local storage for bmp image data.
   PNGFile *image[1];

   // Load the texture.
   image[0] = GetPNGData(textureName);

   // Bind image to texture index[0]. 
   glBindTexture(GL_TEXTURE_2D, m_Texture[0]); 
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

   //used to make the image look blocky
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image[0]->sizeX, image[0]->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, image[0]->pixels.data() );
    delete image[0];
}


void drawScene(void)
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glLoadIdentity();


  //glulookat

   // Turn on OpenGL lighting.
   glEnable(GL_LIGHTING);

   //Light.Draw();

   // Draw 10 x 10 array of multi-colored spheres.
   int i,j;
   for (i = 0; i < 9; i++)
      for (j = 0; j < 9; j++)
      {
         glPushMatrix();
         glTranslatef(-4.0+i, 0.0, -4.0+j);

         // Ambient and diffuse colors of the spheres specified to alternate.
         if ((i+j)%3 == 0) glColor4f(1.0, 0.0, 0.0, 1.0);
         else if ((i+j)%3 == 1) glColor4f(0.0, 1.0, 0.0, 1.0);
         else glColor4f(0.0, 0.0, 1.0, 1.0);

         glutSolidSphere (0.5, 20.0, 16.0);
         glPopMatrix(); 
      }

   plane.Draw();

   glutSwapBuffers();
}

void setup(void) 
{
    glClearColor(0.0, 0.0, 0.0, 0.0); 

   plane = Plane(0,0,-10,5,5,0, "launch.png");

   glShadeModel (GL_SMOOTH);
   // stops GL_QUAD from showing faces by priority
    glEnable(GL_DEPTH_TEST);


   // Specify how texture values combine with current surface color values.
   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 

   glEnable(GL_BLEND); // Enable blending.
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


   // Enable color material mode:
   // The ambient and diffuse color of the front faces will track the color set by glColor().
   glEnable(GL_COLOR_MATERIAL); 
   glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
}

Upvotes: 1

Views: 6875

Answers (1)

Reto Koradi
Reto Koradi

Reputation: 54642

There are at least two problems that prevent you from getting good lighting. The first one should be easy to solve. The second one goes deeper.

Texture combination setting

The following call determines how the result of the lighting calculation is combined with the color read from the texture:

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

As the name indicates, GL_REPLACE means that the color resulting from lighting is replaced by the texture color. So by definition, you will get no lighting if you use this setting with texturing enabled.

The most common setting to combine the color from lighting with the color read from the texture is GL_MODULATE:

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

This means that the two colors (from lighting and from texture) are multiplied.

Per-vertex lighting

The legacy fixed function pipeline in OpenGL uses per-vertex lighting. This means that the lighting equations are only evaluated once per vertex, and the result is the interpolated across the entire polygon.

Per-vertex lighting works poorly for very large primitives, like in the case where you draw the entire plane as a single, large polygon. Particularly specular highlights are normally lost this way, and even the diffuse term suffers if the primitives are two large.

To fix this, there are two main options:

  • Sub-divide the plane into smaller polygons. This introduces more vertices where lighting will be evaluated, which reduces the problems resulting from interpolation.
  • Use per-fragment lighting. I believe people may have figured out ways to do this with the fixed pipeline, but I don't think it will be easy. You're really entering a domain where the programmable pipeline is greatly superior.

While moving to the programmable pipeline is a bigger step for you right now if you have not dealt with it before, I would still consider it. The fixed pipeline is not even available anymore in modern versions of OpenGL (Core Profile, as well as ES 2.x and later), so you will want to learn about shader programming sooner or later anyway.

Upvotes: 5

Related Questions