OpenGL Questions

All questions regarding Windows programming, post here. API,COM, ActiveX, DirectX, OpenGL, MFC and so on...

Moderators: Darobat, RecursiveS, Dante Shamest, Bugdude, Wizard

OpenGL Questions

Postby tim » Thu Dec 24, 2009 3:37 am

Greetings everyone.

I joined the C++ Home forum. I'm also a member of Java Forums. I have two questions regarding OpenGL, so I'll combine them here. The first is about lighting in a scene and the second is about simple textures. Please note that my programs were created using GLUT in Visual Studio, hence the "#include "stdafx.h"" include.

First Question
I have created a program that lets the user navigate a maze built on the plane from a three dimensional perspective. From a birds-eye-view, the walls have appropriate diffuse and specular lighting effects. However, a first person view results in distorted diffuse and/or specular lighting. I will give the program code next. I do not expect the reader to debug or fix the program.
I think the problem is that OpenGL colors each vertex according to it's normal vector, then the color is interpolated between the vertices. So, from the birds-eye-view, the distortion is minimal and from a first person view, the distortion is greater. I figured that creating a mesh would solve this problem, but this would be computationally expensive.
Code: Select all
// include standard API
#include "stdafx.h"
#include <cmath>
#include <ctime>
#include <iostream>
// include OpenGL related API
#include "glut.h"

//uses
using namespace std;

// constants
const float SCREEN_HEIGHT = 600;
const int   GRID_SIZE     = 15;                 // the grid size
const int   MAZE_SIZE     = GRID_SIZE * 2 + 1;  // an odd number for the matrix size
const float CELL_SIZE     = 60;                 // the cell size
const float WALL_WIDTH    = 5;                  // the wall width
const float WALL_HEIGHT   = 50;
const float MAZE_FLOOR    = 50;
const int CELL_TYPE_OPEN = 1;
const int CELL_TYPE_CLOSED = 2;
const float BLACK[]   = {0, 0, 0, 1};
const float RED[]     = {1, 0, 0, 1};
const float GREEN[]   = {0, 1, 0, 1};
const float BLUE[]    = {0, 0, 1, 1};
const float GLASS[]   = {1, 1, 1, 0.3};
const float CYAN[]    = {0, 1, 1, 1};
const float YELLOW[]  = {1, 1, 0, 1};
const float MAGENTA[] = {1, 0, 1, 1};
const float WHITE[]   = {1, 1, 1, 1};
const float PI = 3.1415926;

// global variables
    // display
    float aspectRatio = 0;
    // maze
    int **maze;                       // the matrix
    int   mazeSize;                   // the odd matrix size
    float mazeBoundary            = 0;
    float displacement            = 0;
    // viewer
    int   viewSourceRow           = 0;
    int   viewSourceColumn        = 0;
    int   viewDestinationRow      = 0;
    int   viewDestinationColumn   = 0;
    float viewProgress            = 0;
    int   viewLook                = 0;
    float viewSourceLook          = 0;
    float viewDestinationLook     = 0;
    bool  viewMotion              = false;
    float viewX                   = 0;
    float viewY                   = 0;
    // parameters
    float SCALE = 7;
    float TILT = 10;
    bool DRAW_VIEWER = false;
    bool DRAW_AXIS = false;
// functions
void display();
void animate(int value);
void sleep(int time);
void keyboard(unsigned char key, int x, int y);
void drawBox(float x1, float y1, float z1, float x2, float y2, float z2);
float *getCrossProduct(float *v1, float *v2);
void traceFlatQuad(float *v1, float *v2, float *v3, float *v4);
void drawMaze();
void createMaze(int size);
void drawViewer();
bool isInMaze(int row, int column);

void main(int argumentCount, char **arguments)
{
    // set random seed
    srand(time(0));

    // initialize glut
    glutInit(&argumentCount, arguments);

    // get aspect ratio
    float
        screenWidth  = glutGet(GLUT_SCREEN_WIDTH),
        screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
    aspectRatio = screenWidth / screenHeight;

    // create window
    glutInitWindowPosition(100, 100);
    glutInitWindowSize((int) (aspectRatio * SCREEN_HEIGHT), (int) SCREEN_HEIGHT);
    glutCreateWindow("Maze");

    // setup display
    glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glEnable(GL_NORMALIZE);
    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glClearColor(0, 0, 0, 1);

    // setup projections
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluPerspective(80, aspectRatio, 100, 100000);
    gluLookAt(0, 0, 200, 0, 0, 100, 0, 1, 0);

    // assign functions
    glutDisplayFunc(display);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(0, animate, 0);

    // create content
    createMaze(MAZE_SIZE);

    // start main loop
    glutMainLoop();
}


int random(int min, int max) {
    return min + rand() % (max - min + 1);
}

void display() {
    // clear buffers
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_DEPTH_BUFFER_BIT);

    // display scene
    glEnable(GL_LIGHTING);
        // transform scene
        glPushMatrix();
        glRotatef(TILT, 1, 0, 0);
        // rotate entire scene
        glRotatef(viewLook, 0, 1, 0);
        // scale entire scene
        glScalef(SCALE, SCALE, SCALE);
        // translate entire scene
        glTranslatef(-(viewX - displacement + (CELL_SIZE + WALL_WIDTH) / 2.0), 0, -(viewY - displacement + (CELL_SIZE + WALL_WIDTH) / 2.0));
        glPushMatrix();

            // draw the maze
            glColor4fv(WHITE);
            mazeBoundary = (mazeSize / 2) * CELL_SIZE + (mazeSize / 2 + 1) * WALL_WIDTH,
            displacement = (float) (mazeBoundary) / 2.0;
            glPushMatrix();
                glTranslatef(-displacement, -MAZE_FLOOR / 2, -displacement);               

                // draw axis
                if (DRAW_AXIS) {
                    glDisable(GL_LIGHTING);
                    const int axisOffset = 70;
                    glBegin(GL_LINE_STRIP);
                        glColor4fv(RED);
                        glVertex3f(-axisOffset, 0, mazeBoundary + axisOffset);
                        glColor4fv(GREEN);
                        glVertex3f(-axisOffset, 0, -axisOffset);
                        glColor4fv(BLUE);
                        glVertex3f(mazeBoundary + axisOffset, 0, -axisOffset);
                    glEnd();
                    glEnable(GL_LIGHTING);
                }

                // add white light
                float pos[]      = {displacement, WALL_HEIGHT * 3, displacement, 1};
                float ambient[]  = {0.1, 0.1, 0.1, 1};
                float diffuse[]  = {0.5, 0.5, 0.5, 1};
                float specular[] = {0.2, 0.2, 0.2, 1};
                glLightfv(GL_LIGHT0, GL_POSITION, pos);
                glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
                glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
                glLightfv(GL_LIGHT0, GL_SPECULAR, specular);   
                glEnable(GL_LIGHT0);

                // draw maze
                drawMaze();

                // draw viewer
                drawViewer();

            glPopMatrix();
        glPopMatrix();
    glPopMatrix();

    glDisable(GL_LIGHTING);

    // flush buffers
    glFlush();
}

float *getCrossProduct(float *v1, float *v2) {
    float *result = new float[3];
    result[0] = v1[1] * v2[2] - v1[2] * v2[1],
    result[1] = v1[2] * v2[0] - v1[0] * v2[2],
    result[2] = v1[0] * v2[1] - v2[0] * v1[1];   
    return result;
}

float *interpolate(float *v1, float *v2, float ratio) {
    float *result = new float[3];
    result[0] = v1[0] * (1 - ratio) + ratio * v2[0];
    result[1] = v1[1] * (1 - ratio) + ratio * v2[1];
    result[2] = v1[2] * (1 - ratio) + ratio * v2[2];
    return result;
}

void traceQuad(float *v1, float *v2, float *v3, float *v4) {
    // get the normal vector
    float p1[] = {v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]};
    float p2[] = {v3[0] - v2[0], v3[1] - v2[1], v3[2] - v2[2]};
    float *n = getCrossProduct(p1, p2);
    // trace the quad in a plane
    glNormal3fv(n);
    glVertex3fv(v1);
    glNormal3fv(n);
    glVertex3fv(v2);
    glNormal3fv(n);
    glVertex3fv(v3);
    glNormal3fv(n);
    glVertex3fv(v4);
    delete[] n;
}

bool even(int value) {
    return value % 2 == 0;
}

bool odd(int value) {
    return !even(value);
}

void setColor(const float *color) {
    glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
    glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE);
    glMaterialf(GL_FRONT, GL_SHININESS, 0.9);
}

void drawMaze() {   
    float x = 0, y = 0;
    for (int row = 0; row < mazeSize; row++){
        float yWidth = even(row)    ? WALL_WIDTH : CELL_SIZE;
        for (int column = 0; column < mazeSize; column++){
            float xWidth = even(column) ? WALL_WIDTH : CELL_SIZE;
            int cell = maze[row][column];
            // draw floor
            setColor(WHITE);
            glBegin(GL_QUADS);
                float p1[] = {x         , 0, y};
                float p2[] = {x + xWidth, 0, y};
                float p3[] = {x + xWidth, 0, y + yWidth};
                float p4[] = {x         , 0, y + yWidth};
                traceQuad(p1, p2, p3, p4);
            glEnd();               
            // draw corners
            setColor(MAGENTA);
            if (even(row) && even(column))
                drawBox(x, 0, y, x + WALL_WIDTH, WALL_HEIGHT, y + WALL_WIDTH);
            // draw walls
            setColor(MAGENTA);
            if (maze[row][column] == CELL_TYPE_CLOSED) {
                if (odd(row) && even(column) || even(row) && odd(column))
                    drawBox(x, 0, y, x + xWidth, WALL_HEIGHT, y + yWidth);
            }
            x += xWidth;
        }
        x = 0;
        y += yWidth;
    }
}

void drawViewer() {
    float step = CELL_SIZE + WALL_WIDTH;
    float
        x1 = (float) viewSourceColumn * step,
        y1 = (float) viewSourceRow * step,
        x2 = (float) viewDestinationColumn * step,
        y2 = (float) viewDestinationRow * step;
    viewX = x1 * (1.0 - viewProgress) + x2 * viewProgress + WALL_WIDTH,
    viewY = y1 * (1.0 - viewProgress) + y2 * viewProgress + WALL_WIDTH;
    viewLook = viewSourceLook * (1.0 - viewProgress) + viewDestinationLook * viewProgress;
    setColor(GLASS);
    if (DRAW_VIEWER)
        drawBox(viewX + 1, 1, viewY + 1, viewX + CELL_SIZE - 1, WALL_HEIGHT, viewY + CELL_SIZE - 1);
}

void drawBox(float x1, float y1, float z1, float x2, float y2, float z2) {
    float **p = new float*[8];
    for (int i = 0; i < 8; i++)
        p[i] = new float[3];

    p[0][0] = x1;  p[0][1] = y1;  p[0][2] = z1;
    p[1][0] = x2;  p[1][1] = y1;  p[1][2] = z1;
    p[2][0] = x2;  p[2][1] = y2;  p[2][2] = z1;
    p[3][0] = x1;  p[3][1] = y2;  p[3][2] = z1;

    p[4][0] = x1;  p[4][1] = y1;  p[4][2] = z2;
    p[5][0] = x2;  p[5][1] = y1;  p[5][2] = z2;
    p[6][0] = x2;  p[6][1] = y2;  p[6][2] = z2;
    p[7][0] = x1;  p[7][1] = y2;  p[7][2] = z2;

    glBegin(GL_QUADS);
        traceQuad(p[0], p[1], p[2], p[3]);
        traceQuad(p[0], p[1], p[2], p[3]);
        traceQuad(p[7], p[6], p[5], p[4]);
        traceQuad(p[4], p[5], p[1], p[0]);
        traceQuad(p[0], p[3], p[7], p[4]);
        traceQuad(p[1], p[5], p[6], p[2]);
        traceQuad(p[2], p[6], p[7], p[3]);
    glEnd();

    for (int i = 0; i < 8; i++)
        delete[] p[i];
    delete[] p;

}

void turnViewer(float angle) {
    if (!viewMotion) {
        viewProgress = 0;
        viewDestinationLook = viewSourceLook + angle;
        viewMotion = true;
    }
}

void moveViewer(int xDirection, int yDirection) {
    if (!viewMotion) {
        if (isInMaze(viewSourceRow * 2 + 1 + yDirection, viewSourceColumn * 2 + 1 + xDirection))
            if (maze[viewSourceRow * 2 + 1 + yDirection][viewSourceColumn * 2 + 1 + xDirection] == CELL_TYPE_OPEN) {
                viewProgress = 0;
                viewDestinationRow = viewSourceRow + yDirection;
                viewDestinationColumn = viewSourceColumn + xDirection;
                viewMotion = true;
            }
    }
}

void keyboard(unsigned char key, int a, int b){
    int
        x = (int) cosf((viewLook - 90) / 180.0 * PI),
        y = (int) sinf((viewLook - 90) / 180.0 * PI);
    switch (key) {
        case 27:
            exit(0);
            break;
        case 'f':
            glutFullScreen();
            break;
        case 'F':
            glutPositionWindow(100, 100);
            glutReshapeWindow((int) (aspectRatio * SCREEN_HEIGHT), (int) SCREEN_HEIGHT);
            break;
        case 'v':
            SCALE = 0.5;
            TILT = 80;
            DRAW_VIEWER = true;
            DRAW_AXIS = false;
            break;
        case 'V':
            SCALE = 7;
            TILT = 10;
            DRAW_VIEWER = false;
            DRAW_AXIS = false;
            break;
        case 'a':
            turnViewer(-90);
            break;
        case 'd':
            turnViewer(90);
            break;
        case 's':
            moveViewer(-x, -y);
            break;
        case 'w':
            moveViewer(x, y);
            break;
    }
}

void animate(int value) {
    // move viewer
    if (viewMotion) {
        if (viewProgress > 1) {
            viewProgress = 0;
            viewMotion = false;
            viewSourceRow = viewDestinationRow;
            viewSourceColumn = viewDestinationColumn;
            viewSourceLook = viewDestinationLook;
        } else {
            viewProgress += 0.05;
        }
    }

    // disply and recure at 10 ms
    display();
    glutTimerFunc(10, animate, 0);
}

void sleep(int time) {
    // disply and recure at 10 ms
    display();
    glutTimerFunc(time, sleep, 0);
}

bool isInMaze(int row, int column) {
    return row >= 0 && column >= 0 && row < mazeSize && column < MAZE_SIZE;
}

void chamber(int row1, int column1, int row2, int column2) {
    if (column2 - column1 > 1 && row2 - row1 > 1) {
        // create walls
        int
            horizontal = random(row1 + 1, row2 - 1),
            vertical = random(column1 + 1, column2 - 1);
        int
            horizontalSkipA = random(vertical, column2 - 1),
            horizontalSkipB = random(column1, vertical - 1);
        for (int x = column1; x < column2; x++) {
            if (x != horizontalSkipA && x != horizontalSkipB)
                maze[horizontal * 2][x * 2 + 1] = CELL_TYPE_CLOSED;
        }
        int
            verticalSkipA = random(horizontal, row2 - 1);
        for (int y = row1; y < row2; y++) {
            if (y != verticalSkipA)
                maze[y * 2 + 1][vertical * 2] = CELL_TYPE_CLOSED;
        }
        // recure
        chamber(row1, column1, horizontal, vertical);
        chamber(horizontal, vertical, row2, column2);
        chamber(row1, vertical, horizontal, column2);
        chamber(horizontal, column1, row2, vertical);
    }
}

void createMaze(int size) {
    // initialize the matrix
    maze = new int*[size];
    mazeSize = size;
    for (int row = 0; row < size; row++){
        maze[row] = new int[size];
        for (int column = 0; column < size; column++){
            maze[row][column] = CELL_TYPE_OPEN;
        }
    }

    // add a maze boundary
    for (int i = 0; i < mazeSize; i++) {
        maze[0][i] = CELL_TYPE_CLOSED;
        maze[i][0] = CELL_TYPE_CLOSED;
        maze[mazeSize - 1][i] = CELL_TYPE_CLOSED;
        maze[i][mazeSize - 1] = CELL_TYPE_CLOSED;
    }

    // start chamber recurion
    chamber(0, 0, GRID_SIZE, GRID_SIZE);
}


Second Question
I have attempted to apply a simple texture to a quad. My goal is to create a texture with four colors containing four pixels in a simple unsigned byte array. The texture should be red, green, blue and white in a clockwise direction. So far, my array is formatted incorrectly and I've tried searching for the solution. So to keep it simple. I'd like the entire quad to be colored red using this four-pixel texture. Here is the program up to date. My main concern is the format of the unsigned byte array.
Code: Select all
// include standard API
#include "stdafx.h"
#include <cmath>
#include <iostream>
// include OpenGL API
#include "glut.h"

// constants
const float GREY[] = {0.2, 0.2, 0.2, 1};
const float WHITE[] = {1, 1, 1, 1};
const float RED[]   = {1, 0, 0, 1};

// functions
void display();
void animate(int value);
GLubyte *createBuffer(int width, int height, GLubyte red, GLubyte green, GLubyte blue);

// variables
float xAxisRotation = 20;
float yAxisRotation = 0;

// implementations
void main(int argumentCount, char **arguments)
{
    // initialize
    glutInit(&argumentCount, arguments);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(500, 500);
    glutCreateWindow("Textures");
    // setup display
    glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE | GLUT_DEPTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_DEPTH_TEST);
    glClearColor(1, 0, 1, 1);
    glEnable(GL_NORMALIZE);
    // setup projections
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluPerspective(90, 1, 100, 300);
    gluLookAt(0, 0, 200, 0, 0, 100, 0, 1, 0);
    // assign functions
    glutDisplayFunc(display);
    glutTimerFunc(0, animate, 0);
    // start loop
    glutMainLoop();
}

void display() {
    // clear buffers
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_DEPTH_BUFFER_BIT);

    // add a light
    float position[] = {100, 100, 100, 1};
    float ambient[]  = {0.1, 0.1, 0.1, 1};
    float diffuse[]  = {0.5, 0.5, 0.5, 1};
    float specular[] = {0.2, 0.2, 0.2, 1};
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
    glEnable(GL_LIGHT0);

    // transform and draw
    GLubyte *texture = createBuffer(2, 2, 255, 0, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    glPushMatrix();
        glScalef(1.5, 1.5, 1.5);
        glRotatef(yAxisRotation, 0, 1, 0);
        glRotatef(xAxisRotation, 1, 0, 0);
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, WHITE);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, GREY);
        glNormal3f(0, 0, 1);
        glBegin(GL_QUADS);
            glTexCoord2f(0, 0);      glVertex3f(-50, -50, 0);
            glTexCoord2f(1, 0);      glVertex3f( 50, -50, 0);
            glTexCoord2f(1, 1);      glVertex3f( 50,  50, 0);
            glTexCoord2f(0, 1);      glVertex3f(-50,  50, 0);
        glEnd();
    glPopMatrix();
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_LIGHTING);

    // flush buffers
    glFlush();
}

void animate(int value) {
    // animate
    yAxisRotation += 0.1;
    // recur
    display();
    glutTimerFunc(10, animate, 0);
}

GLubyte *createBuffer(int width, int height, GLubyte red, GLubyte green, GLubyte blue) {
    GLubyte *result = new GLubyte[3 * width * height];
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            result[y + x * height + 0] = red;
            result[y + x * height + 1] = green;
            result[y + x * height + 2] = blue;
        }
    }
    return result;
}


And that's that. :)
Eyes dwelling in the past are blind to what lies in the future.
User avatar
tim
 
Posts: 1
Joined: Thu Dec 24, 2009 2:55 am

Return to Windows Programming

Who is online

Users browsing this forum: No registered users and 1 guest