views:

700

answers:

3

I'm having trouble getting a texture to map onto geometry properly with OpenGL. In fact I seem to have even broken the colour interpolation that used to work fine. I've created a test case in C99 that uses SDL, GLee and SOIL.

#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <SDL/SDL.h>
#include <GL/GLee.h>
#include <SOIL/SOIL.h>

static const char *vertex_source = " \
uniform mat4 projection; \
uniform mat4 view_model; \
 \
attribute vec2 vertex; \
attribute vec2 texcoord; \
attribute vec4 colour; \
 \
varying vec2 _texcoord; \
 \
void main() \
{ \
    gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 0, 1); \
    _texcoord = texcoord; \
    gl_FrontColor = colour; \
} ";

static const char *fragment_source = " \
uniform sampler2D sampler0; \
 \
varying vec2 _texcoord; \
 \
void main() \
{ \
    gl_FragColor = texture2D(sampler0, _texcoord) * 0.01 + gl_Color; \
} ";

typedef struct
{
    GLfloat position[2];
    GLfloat texcoord[2];
    GLubyte colour[4];
} Vertex;

static Vertex verts[] = {
        {
            .position = { 1, 1 },
            .texcoord = { 1, 1 },
            .colour = { 255, 0, 0, 255 },
        },
        {
            .position = { -1, 1 },
            .texcoord = { 0, 1 },
            .colour = { 0, 255, 0, 255 },
        },
        {
            .position = { -1, -1 },
            .texcoord = { 0, 0 },
            .colour = { 0, 0, 255, 255 },
        },
        {
            .position = { 1, -1 },
            .texcoord = { 1, 0 },
            .colour = { 255, 255, 0, 255 },
        },
    };

static GLuint vertex, fragment, program, vbo, texture;
static GLint sampler_loc, vertex_loc, texcoord_loc, colour_loc;

static void init()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_SetVideoMode(800, 800, 0, SDL_OPENGL);

    glClearColor(1, 0, 0, 0);

    glMatrixMode(GL_PROJECTION);
    glOrtho(-1, 1, -1, 1, -1, 1);

    /* Shaders */
    vertex = glCreateShader(GL_VERTEX_SHADER);
    assert(vertex != 0);
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    assert(fragment != 0);

    GLint length = strlen(vertex_source);
    glShaderSource(vertex, 1, &vertex_source, &length);
    length = strlen(fragment_source);
    glShaderSource(fragment, 1, &fragment_source, &length);

    glCompileShader(vertex);
    glCompileShader(fragment);

    program = glCreateProgram();

    glAttachShader(program, vertex);
    glAttachShader(program, fragment);

    glLinkProgram(program);

    sampler_loc = glGetUniformLocation(program, "sampler0");
    vertex_loc = glGetAttribLocation(program, "vertex");
    texcoord_loc = glGetAttribLocation(program, "texcoord");
    colour_loc = glGetAttribLocation(program, "colour");

    /* VBO */
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    /* Texture */
    texture = SOIL_load_OGL_texture("test.png", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y);
    assert(texture != 0);
}

static void draw()
{
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(program);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(sampler_loc, 0);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glScalef(.5, .5, .5);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glEnableVertexAttribArray(0);

    glVertexAttribPointer(vertex_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), offsetof(Vertex, position));
    glVertexAttribPointer(texcoord_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), offsetof(Vertex, texcoord));
    glVertexAttribPointer(colour_loc, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), offsetof(Vertex, colour));

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindTexture(GL_TEXTURE_2D, 0);
    glPopMatrix();

    glUseProgram(0);
}

static void shutdown()
{
    SDL_Quit();
}

int main()
{
    init();
    atexit(shutdown);

    while(true)
    {
        static SDL_Event event;
        while(SDL_PollEvent(&event)) 
        {
            switch(event.type) 
            {
                case SDL_QUIT:
                    exit(0);
                    break;

                default:
                    break;
            }
        }

        draw();

        SDL_GL_SwapBuffers();

        if(glGetError() != GL_NO_ERROR)
        {
            printf("Error\n");
            exit(1);
        }
    }

    return 0;
}

The only thing that renders is a plain blue square on top of the glClearColor.

Any help much appreciated.

Thanks for answers, I've attached fixed code for completeness.

#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <SDL/SDL.h>
#include <GL/GLee.h>
#include <SOIL/SOIL.h>

static const char *vertex_source = " \
uniform mat4 projection; \
uniform mat4 view_model; \
 \
attribute vec2 vertex; \
attribute vec2 texcoord; \
attribute vec4 colour; \
 \
varying vec2 _texcoord; \
 \
void main() \
{ \
    gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 0, 1); \
    _texcoord = texcoord; \
    gl_FrontColor = colour; \
} ";

static const char *fragment_source = " \
uniform sampler2D sampler0; \
 \
varying vec2 _texcoord; \
 \
void main() \
{ \
    gl_FragColor = texture2D(sampler0, _texcoord) + gl_Color; \
} ";

typedef struct
{
    GLfloat position[2];
    GLfloat texcoord[2];
    GLubyte colour[4];
} Vertex;

static Vertex verts[] = {
        {
            .position = { 1, 1 },
            .texcoord = { 1, 1 },
            .colour = { 255, 0, 0, 255 },
        },
        {
            .position = { -1, 1 },
            .texcoord = { 0, 1 },
            .colour = { 0, 255, 0, 255 },
        },
        {
            .position = { -1, -1 },
            .texcoord = { 0, 0 },
            .colour = { 0, 0, 255, 255 },
        },
        {
            .position = { 1, -1 },
            .texcoord = { 1, 0 },
            .colour = { 255, 255, 0, 255 },
        },
    };

static GLuint vertex, fragment, program, vbo, texture;
static GLint sampler_loc, vertex_loc, texcoord_loc, colour_loc;

static void init()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_SetVideoMode(800, 800, 0, SDL_OPENGL);

    glClearColor(1, 0, 0, 0);

    glMatrixMode(GL_PROJECTION);
    glOrtho(-1, 1, -1, 1, -1, 1);

    /* Shaders */
    vertex = glCreateShader(GL_VERTEX_SHADER);
    assert(vertex != 0);
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    assert(fragment != 0);

    GLint length = strlen(vertex_source);
    glShaderSource(vertex, 1, &vertex_source, &length);
    length = strlen(fragment_source);
    glShaderSource(fragment, 1, &fragment_source, &length);

    glCompileShader(vertex);
    glCompileShader(fragment);

    program = glCreateProgram();

    glAttachShader(program, vertex);
    glAttachShader(program, fragment);

    glLinkProgram(program);

    sampler_loc = glGetUniformLocation(program, "sampler0");
    vertex_loc = glGetAttribLocation(program, "vertex");
    texcoord_loc = glGetAttribLocation(program, "texcoord");
    colour_loc = glGetAttribLocation(program, "colour");

    glUseProgram(program);
    glUniform1i(sampler_loc, 0);
    glUseProgram(0);

    /* VBO */
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts[0], GL_STATIC_DRAW);
    glVertexAttribPointer(vertex_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), offsetof(Vertex, position));
    glVertexAttribPointer(texcoord_loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), offsetof(Vertex, texcoord));
    glVertexAttribPointer(colour_loc, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Vertex), offsetof(Vertex, colour));

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    /* Texture */
    texture = SOIL_load_OGL_texture("test.png", SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y);
    assert(texture != 0);
}

static void draw()
{
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(program);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glScalef(.5, .5, .5);

    glEnableVertexAttribArray(vertex_loc);
    glEnableVertexAttribArray(texcoord_loc);
    glEnableVertexAttribArray(colour_loc);

    glDrawArrays(GL_QUADS, 0, 4);

    glDisableVertexAttribArray(vertex_loc);
    glDisableVertexAttribArray(texcoord_loc);
    glDisableVertexAttribArray(colour_loc);

    glBindTexture(GL_TEXTURE_2D, 0);
    glPopMatrix();

    glUseProgram(0);
}

static void shutdown()
{
    SDL_Quit();
}

int main()
{
    init();
    atexit(shutdown);

    while(true)
    {
        static SDL_Event event;
        while(SDL_PollEvent(&event)) 
        {
            switch(event.type) 
            {
                case SDL_QUIT:
                    exit(0);
                    break;

                default:
                    break;
            }
        }

        draw();

        SDL_GL_SwapBuffers();

        if(glGetError() != GL_NO_ERROR)
        {
            printf("Error\n");
            exit(1);
        }
    }

    return 0;
}
+1  A: 

Your shader includes the subexpression

texture2D(sampler0, _texcoord) * 0.01

Which would make your texture essentially invisible on most displays, wouldn't it?

spurserh
Ah yes, but that's not the issue. That's left from me playing around to see where the colours were coming from.
jsimmons
+1  A: 

You're not enabling the vertex attrib arrays correctly.

  • you enable 0, which may not even be something you use (well, in practice, it's probably vertex_loc, but you should not rely on it)
  • you ignore the other 2 arrays

Try the following:

glEnableVertexAttribArray(vertex_loc);
glEnableVertexAttribArray(texcoord_loc);
glEnableVertexAttribArray(colour_loc);

Edit to add: I might as well point out other details:

  • I'd set the sampler location only once. setting it tends to force extra work in the driver, and since you'll only set it to the same texture unit every time, you might as well do it at initilization.

  • The place you call glBindBuffer(GL_ARRAY_BUFFER, 0) is not wrong, but I'd put it just after the VertexAttribPointer calls. The currently bound buffer is really just an extra argument to those calls... And it does not affect the glDrawArrays call itself.

Bahbar
Yay I fixed it. Thanks a lot. (I updated the first post with my new code)
jsimmons
A: 

Afaik you need to use glClientActiveTexture() before binding a texture for a VBO.

karx11erx