سؤال

What is the simplest way of applying a normal map to a textured four vertex polygon in SDL? Do you have to create a shader or is there a simpler way? I have here my code that creates a textured rotating cube, with a rotating satellite light.

What additions to my code do I need to do if I want to use the generated texture (tex_Norm = generateTexture()) as a normal map also?

#include <windows.h>
#include <SDL.h>

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GL/glext.h>

#include <math.h>

void            initAll();
void            setupBox();
void            mainloop();
unsigned int    generateTexture();
void            handle_inputs();
void            updateScreen();
void            clean_up();

int             scrWidth, scrHeight, flags;
bool            bQuit = false;
float           angle = 0.0f;

GLuint          tex_Box, tex_Norm;

struct sVert
{
    float x;
    float y;
    float z;
};

class cPolygon
{
public:
    int v[4];

    void fillverts(int v1, int v2, int v3, int v4) {
        v[0] = v1;
        v[1] = v2;
        v[2] = v3;
        v[3] = v4;
    }
} p[6];

sVert pv[8];

int main(int argc, char *argv[])
{
    initAll();
    mainloop();
    clean_up();
    return 0;
}

void initAll()
{
    scrWidth = 800;
    scrHeight = 600;

    SDL_InitSubSystem(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    flags = SDL_OPENGL | SDL_ANYFORMAT ;

    SDL_SetVideoMode(scrWidth, scrHeight, 16, flags);
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    gluPerspective( 45.0f, (GLfloat)scrWidth/(GLfloat)scrHeight, 1.0f, 500.0f );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );

    glEnable (GL_DEPTH_TEST);
    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);

    SDL_WM_SetCaption( "Normal Mapping", NULL );

    tex_Box = generateTexture();
    tex_Norm = generateTexture();

    setupBox();
}

void setupBox()
{

    for (int z=0;z<2;z++)
    for (int y=0;y<2;y++)
    for (int x=0;x<2;x++)
    {
        pv[x+y*2+z*4].x = -1.0+x;
        pv[x+y*2+z*4].y = -1.0+y;
        pv[x+y*2+z*4].z = -1.0+z;
    }

    // Box object
    p[0].fillverts (0, 1, 3, 2);    // above
    p[1].fillverts (4, 5, 1, 0);    // behind
    p[2].fillverts (6, 7, 3, 2);    // in front
    p[3].fillverts (5, 7, 3, 1);    // right
    p[4].fillverts (0, 2, 6, 4);    // left
    p[5].fillverts (7, 6, 4, 5);    // below
}

unsigned int generateTexture()
{
    BYTE    data[128*128*3];
    unsigned int id;

    for (int x=0;x<128;x++)
        for (int y=0;y<128;y++)
        {
            data[y*128*3+x*3+0] = x;        // Red
            data[y*128*3+x*3+1] = y;        // Green
            data[y*128*3+x*3+2] = 128-(abs(64-x)+abs(64-y));    // Blue
        }

    glGenTextures(1, &id); 
    glBindTexture(GL_TEXTURE_2D, id); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 

    return id;
}

void mainloop()
{
    while(bQuit == false)
    {
        handle_inputs();
        updateScreen();
        angle += 1.5f;
        Sleep(50);
    }
}

void handle_inputs()
{
    SDL_PumpEvents();
    Uint8 * keystate = SDL_GetKeyState(NULL);
    if(keystate[SDLK_ESCAPE]) bQuit = true;
}

void updateScreen()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    glPushMatrix();

    glTranslatef(0.0f, 0.0f, -3.0f);        
    glRotatef(-angle*2.0, 1.0f, 1.0f, 1.0f);

    // Setup a light source
    float lpos[] = {0.0, 0.0, 2.0, 1.0};    // position
    float ldif[] = {1.0, 1.0, 1.0, 1.0};    // diffuse 
    float lamb[] = {0.3, 0.3, 0.2, 1.0};    // ambient 

    glLightfv(GL_LIGHT0, GL_POSITION, lpos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, ldif);
    glLightfv(GL_LIGHT0, GL_AMBIENT, lamb);

    glPopMatrix();
    glPushMatrix();

    glTranslatef(0.5f, 0.5f, -3.0f);        
    glRotatef(angle, 1.0f, 1.0f, 1.0f); 

    // Draw box object
    glBindTexture(GL_TEXTURE_2D, tex_Box);
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);

    for(int pi=0; pi<6; pi++)
        for(int vi=0; vi<4; vi++)
        {
            switch(vi) {
            case 0: glTexCoord2d(0.0,2.0); break;
            case 1: glTexCoord2d(0.0,0.0); break;
            case 2: glTexCoord2d(2.0,0.0); break;
            case 3: glTexCoord2d(2.0,2.0); break; };

            glVertex3f( pv[ p[pi].v[vi] ].x, 
                        pv[ p[pi].v[vi] ].y, 
                        pv[ p[pi].v[vi] ].z );

        };

    glEnd();
    glDisable(GL_TEXTURE_2D);
    glPopMatrix();

    glFinish();
    SDL_GL_SwapBuffers();
}

void clean_up()
{
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
    glDeleteTextures(1, &tex_Box);
    glDeleteTextures(1, &tex_Norm);
    SDL_Quit();
}
هل كانت مفيدة؟

المحلول

Sorry, but if you want to use modern OpenGL features, you need modern OpenGL, which means shaders. :( That also means all your lighting needs to be done manually in the shader, so your program is going to get quite a bit more complicated, but that's the price you pay for cool images.

Explaining in detail how to do normal mapping wouldn't fit here, but there are very nice tutorials out there, for example http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/.

Good luck!

نصائح أخرى

After some hours adventure into the world of GLSL and some help from the answer in this question: SDL OpenGL in C++, Texture Shader missing the texture

... I finally made a Normal map out of my generated texture. Here is the result:

#include <windows.h>
#include <SDL.h>

#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdio.h>

#include <math.h>
#include <string>

using namespace std;
#define LOG_SIZE 10000

void            initAll();
void            setupBox();
void            mainloop();
unsigned int    generateTexture();
void            handle_inputs();
void            updateScreen();
void            clean_up();
void            dbpf(int, const char *, ...);

int             scrWidth, scrHeight, flags;
bool            bQuit = false;
float           angle = 0.0f;

va_list         m;
int             db_threashold = 0;
GLint           status;
GLchar          elog[LOG_SIZE];
GLint           rLength = 0;

GLuint          tex_Box, tex_Norm;

std::string     vertex_source, fragment_source;
GLuint          shader_program, vertex_shader, fragment_shader;

GLuint          vao, vbo;

const char      *source;
int             length;

struct sVert
{
    float x;
    float y;
    float z;
};

class cPolygon
{
public:
    int v[4];

    void fillverts(int v1, int v2, int v3, int v4) {
        v[0] = v1;
        v[1] = v2;
        v[2] = v3;
        v[3] = v4;
    }
} p[6];

sVert pv[8];

int main(int argc, char *argv[])
{
    initAll();
    mainloop();
    clean_up();
    return 0;
}

void initAll()
{
    scrWidth = 800;
    scrHeight = 600;

    vertex_source =
        "#version 330\n"
        "in vec3 Position;\n"
        "in vec2 TexCoord;\n"
        "out vec3 oColor;\n"
        "out vec2 oTexcoord;\n"
        "void main() {\n"
        "    oTexcoord = TexCoord;\n"
        "    gl_Position = gl_ModelViewProjectionMatrix*vec4(Position, 1.0);\n"
        "}\n";

    fragment_source =
        "#version 330\n"
        "in vec2 oTexcoord;\n"
        "out vec4 oColor;\n"
        "uniform sampler2D tex;\n"
        "uniform sampler2D tex_norm;\n"
        "void main() {\n"
        "   vec4 lightpos = normalize(-gl_ModelViewProjectionMatrix*vec4(1.0, -1.0, -1.5, 1.0));\n"
        "   vec3 tmpNorm = normalize(texture2D(tex_norm, oTexcoord).rgb * 2.0 - 1.0);\n"
        "   float difuse = max(dot(tmpNorm, lightpos.xyz), 0.0);\n"
        "   vec3 tmpcolor = difuse * texture2D(tex, oTexcoord).rgb;\n"
        "   oColor = vec4(tmpcolor, 1.0);\n"
        "}\n";

    SDL_InitSubSystem(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    flags = SDL_OPENGL | SDL_ANYFORMAT ;

    SDL_SetVideoMode(scrWidth, scrHeight, 16, flags);

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    gluPerspective( 45.0f, (GLfloat)scrWidth/(GLfloat)scrHeight, 1.0f, 500.0f );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );

    glEnable (GL_DEPTH_TEST);
    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);

    SDL_WM_SetCaption( "Normal map", NULL );

    glewInit();

    // vertex shader
    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    source = vertex_source.c_str();
    length = vertex_source.size();
    glShaderSource(vertex_shader, 1, &source, &length); 
    glCompileShader(vertex_shader);

    glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &status);
    glGetShaderInfoLog(vertex_shader, LOG_SIZE, &rLength, elog);
    dbpf(10, "Compile vertex log: \n %s \n", elog);

    // fragment shader
    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    source = fragment_source.c_str();
    length = fragment_source.size();
    glShaderSource(fragment_shader, 1, &source, &length);
    glCompileShader(fragment_shader);

    glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &status);
    glGetShaderInfoLog(fragment_shader, LOG_SIZE, &rLength, elog);
    dbpf(10, "Compile fragment log: \n %s \n", elog);

    // create program
    shader_program = glCreateProgram();

    // attach shaders
    glAttachShader(shader_program, vertex_shader);
    glAttachShader(shader_program, fragment_shader);

    // link the program and check for errors
    glLinkProgram(shader_program);
    glGetProgramiv(shader_program, GL_LINK_STATUS, &status);
    glGetProgramInfoLog(shader_program, LOG_SIZE, &rLength, elog);
    dbpf(10, "Link log: \n %s \n", elog);

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    setupBox();

    GLfloat vd[6*5*6];

    for(int pi=0; pi<6; pi++)
    {
        vd[pi*30+ 0] = pv[ p[pi].v[0] ].x;
        vd[pi*30+ 1] = pv[ p[pi].v[0] ].y;
        vd[pi*30+ 2] = pv[ p[pi].v[0] ].z;

        vd[pi*30+ 3] = 0.0;
        vd[pi*30+ 4] = 1.0;

        vd[pi*30+ 5] = pv[ p[pi].v[1] ].x;
        vd[pi*30+ 6] = pv[ p[pi].v[1] ].y;
        vd[pi*30+ 7] = pv[ p[pi].v[1] ].z;

        vd[pi*30+ 8] = 0.0;
        vd[pi*30+ 9] = 0.0;

        vd[pi*30+10] = pv[ p[pi].v[2] ].x;
        vd[pi*30+11] = pv[ p[pi].v[2] ].y;
        vd[pi*30+12] = pv[ p[pi].v[2] ].z;

        vd[pi*30+13] = 1.0;
        vd[pi*30+14] = 0.0;

        vd[pi*30+15] = pv[ p[pi].v[0] ].x;
        vd[pi*30+16] = pv[ p[pi].v[0] ].y;
        vd[pi*30+17] = pv[ p[pi].v[0] ].z;

        vd[pi*30+18] = 0.0;
        vd[pi*30+19] = 1.0;

        vd[pi*30+20] = pv[ p[pi].v[2] ].x;
        vd[pi*30+21] = pv[ p[pi].v[2] ].y;
        vd[pi*30+22] = pv[ p[pi].v[2] ].z;

        vd[pi*30+23] = 1.0;
        vd[pi*30+24] = 0.0;

        vd[pi*30+25] = pv[ p[pi].v[3] ].x;
        vd[pi*30+26] = pv[ p[pi].v[3] ].y;
        vd[pi*30+27] = pv[ p[pi].v[3] ].z;

        vd[pi*30+28] = 1.0;
        vd[pi*30+29] = 1.0;
    }

    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*6*5*6, vd, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (char*)0 + 0*sizeof(GLfloat));

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(GLfloat), (char*)0 + 3*sizeof(GLfloat));

    tex_Box = generateTexture();
    tex_Norm = generateTexture();
}

void setupBox()
{

    for (int z=0;z<2;z++)
    for (int y=0;y<2;y++)
    for (int x=0;x<2;x++)
    {
        pv[x+y*2+z*4].x = -1.0+x;
        pv[x+y*2+z*4].y = -1.0+y;
        pv[x+y*2+z*4].z = -1.0+z;
    }

    p[0].fillverts (0, 1, 3, 2);    // above
    p[1].fillverts (4, 5, 1, 0);    // behind
    p[2].fillverts (6, 7, 3, 2);    // in front
    p[3].fillverts (5, 7, 3, 1);    // right
    p[4].fillverts (0, 2, 6, 4);    // left
    p[5].fillverts (7, 6, 4, 5);    // below
}

unsigned int generateTexture()
{
    BYTE    data[128*128*3];
    unsigned int id;

    for (int x=0;x<128;x++)
        for (int y=0;y<128;y++)
        {
            data[y*128*3+x*3+0] = x;        // Red
            data[y*128*3+x*3+1] = y;        // Green
            data[y*128*3+x*3+2] = 128-(abs(64-x)+abs(64-y));    // Blue
        }

    glGenTextures(1, &id); 
    glBindTexture(GL_TEXTURE_2D, id); 
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); 
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);

    return id;
}

void mainloop()
{
    while(bQuit == false)
    {
        handle_inputs();
        updateScreen();
        angle += 1.5f;
        Sleep(50);
    }
}

void handle_inputs()
{
    SDL_PumpEvents();
    Uint8 * keystate = SDL_GetKeyState(NULL);
    if(keystate[SDLK_ESCAPE]) bQuit = true;
}

void updateScreen()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    gluLookAt (2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    // use the shader program
    glUseProgram(shader_program);

    // bind the vao
    glBindVertexArray(vao);

    // rotation
    glRotatef(angle, 1.0, 0.0, 0.0); //rotate on the x axis
    glRotatef(angle, 0.0, 1.0, 0.0); //rotate on the y axis
    glRotatef(angle, 0.0, 0.0, 1.0); //rotate on the z axis

    // bind texture
    glActiveTexture(GL_TEXTURE0);
    int loc = glGetUniformLocation(shader_program, "tex");
    glUniform1i(loc, 0); 
    glBindTexture(GL_TEXTURE_2D, tex_Box);

    // bind normal texture
    glActiveTexture(GL_TEXTURE1);
    loc = glGetUniformLocation(shader_program, "tex_norm");
    glUniform1i(loc, 1); 
    glBindTexture(GL_TEXTURE_2D, tex_Norm);

    // draw
    glDrawArrays(GL_TRIANGLES, 0, 6*6);

    // cleanup
    glActiveTexture(GL_TEXTURE1); 
    glBindTexture(GL_TEXTURE_2D, 0); 
    glDisable(GL_TEXTURE_2D);

    glActiveTexture(GL_TEXTURE0); 
    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    glUseProgram(0);
    SDL_GL_SwapBuffers();
}

void clean_up()
{
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);

    glDetachShader(shader_program, vertex_shader);
    glDetachShader(shader_program, fragment_shader);
    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);
    glDeleteProgram(shader_program);

    SDL_QuitSubSystem(SDL_INIT_VIDEO);
    glDeleteTextures(1, &tex_Box);
    glDeleteTextures(1, &tex_Norm);
    SDL_Quit();
}

void dbpf(int t, const char * msg, ...)
{
    va_start(m, msg);
    if (t >= db_threashold) vfprintf(stderr, msg, m);
    va_end(m);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top