tags:

views:

3136

answers:

3

How do I apply a repeating texture that always maintains its original scale (1 pixel in the texture = 1 pixel on screen), regardless of the vertex data it is applied with.

I realize this is not the most usual task, but is it possible to easily set opengl to do this, or do I need to apply some kind of mask to vertex data that respects its original appearance?

edit: in my specific case, I'm trying to draw 2D ellipses of different sizes, with the same pixel pattern. The ellipses are made of a triangle fan, and I'm having a hard time to draw a repeating texture of any kind on it. I was hoping there was some opengl configuration combination to do this easily. Also, now I realize it's important to mention that I'm using opengles, for the iphone, so GLU is not available.

+1  A: 

I'm not really sure, but try something like this:

Take your model matrix, perspective matrix, and stuff like that. Mash them together in the proper order by multiplying them. Take that matrix's inverse. Multiply it by your texture matrix (which is probably the identity matrix). Set that as your texture matrix.

strager
+5  A: 

Create the 3D object in question, without displaying it.

You can get the bounds of the object as pixel locations by using gluProject (to get the pixels that represent the object's edges. You can then use gluUnProject to map the intervening pixels to the object's coordinates.

Then, you start your draw over, and map a custom (on-the-fly) texture over the same object and display it.

Not sure why you'd want to do this, but that should be a good starting point.

Edit:

What I mean by custom, is if the bounds of your object (in one dimension,) are -3.0 to 1.0, and the first pixel row is from -3.0 to -2.0, your texture map is going to indicate that 25% of your custom texture maps over that spot, and you create it all with the color of the pixel you want to show there.

After thinking that through, I realized you could just draw a texture over the top of the projected screen coordinates (using the 2D drawing facilities.)

I think that gets the gist of your idea across. I don't think it would work well in an interactive 3D demo, if the 'object' comes closer and moves away, if the texture doesn't seem to scale up and down. But you didn't say what you were actually doing.

Edit 2:

OpenGL 2D Projection:


CAUTION
Careful with the function names, e.g., opengles 1.1 has glOrthox and glOrthof. Make sure you check what is available in your gl.h header file.

const XSize = 640, YSize = 480
glMatrixMode (GL_PROJECTION)
glLoadIdentity ()
glOrtho (0, XSize, YSize, 0, 0, 1)
glMatrixMode (GL_MODELVIEW)
glDisable(GL_DEPTH_TEST)
glClear(GL_COLOR_BUFFER_BIT)

// Now draw with 2i or 2f vertices instead of the normal vertex3f functions.
// And for ES, of course set up your data structures and call drawarrays ofr drawelements.

SwapBuffers()

This will allow you to draw 2D shapes in OpenGL (much more simply than using 3D projections.) To mix the two, e.g., draw in 3D then in 2D, follow the second link.

Here's an excellent tutorial on 2D drawing:
http://basic4gl.wikispaces.com/2D+Drawing+in+OpenGL

Here's the basics on mixing the two:
http://www.gamedev.net/community/forums/topic.asp?topic_id=96440

I hope that is what you want. I get a sneaking suspicion from your post that you're having trouble mapping your texture across triangle points to make it show up 'straight'. You might want to review basic texture mapping on NeHe:
http://www.gamedev.net/community/forums/topic.asp?topic_id=96440
E.g., gltexcoord2f specifies the point (0.0-1.0) within the texture in terms of the percentage of width and height of the texture that maps to the next drawn vertex. With triangle fans, you can have some mathematical conniptions to figure out what % of width and height of the overall object you are specifying with the vertex.

Take, for example, a sphere with a texture map (a mercator projection of the earth,) is best mapped by calculating the lines of latitude as a basis for your underlying triangle fan vertex values, as it eases calculation of the texture coordinates. Making your polygons approximate simple geometric shapes allows you to use trigonometry to more easily calculate texture coordinates.

I hope this is helpful.

Hehere, I'll quit going on with desktop examples you have to modifiy. Here's an OpenGLES example that does proper 3D texture mapping. You can use what I said above, and this example, to do 2D texture mapping.
http://www.zeuscmd.com/tutorials/opengles/17-TextureMapping.php

Chris
hey chris, thanks for the input. I'm not quite grasping the first method, but I can't use GLU anyway (see my edit). Could you describe the second method you mentioned, drawing the texture using 2D drawing facilities?
Steph Thirion
Thanks Chris... these are terrific resources. For now I'm abandoning the visual effect I was going for, as according to the answers I got, it turned out to be too complex. I'll look into it when I have more time. I'm still curious though if it's possible to achieve this using some kind of mask.
Steph Thirion
You're welcome, I think no on the mask, if I understand correctly.
Chris
+1  A: 

I think the following code snippet is what you're talking about. However, without the hack in reshape() it shimmers pretty badly with GL_NEAREST and non-even viewport sizes. Any insight would be appreciated.

It uses texture coordinate generation though, so I'm not sure what to tell you on the OpenGL ES 1.1 front. A PowerVR rep hinted at a solution, but wasn't very explicit.

#include <GL/glut.h>
#include <cstdlib>
#include <cmath>

static GLuint texName;

void init(void)
{
glClearColor(0,0,0,0);

// create random texture
const int texWidth = 8;
const int texHeight = 8;
GLubyte tex[texHeight][texWidth][4];
for(int i = 0; i < texHeight; i++)
    {
    for(int j = 0; j < texWidth; j++) 
        {
        tex[i][j][0] = (GLubyte) rand()%255;
        tex[i][j][1] = (GLubyte) rand()%255;
        tex[i][j][2] = (GLubyte) rand()%255;
        tex[i][j][3] = (GLubyte) 255;
        }
    }
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex);

// planes for texture coordinate generation
GLfloat xp[] = {1,0,0,0};
GLfloat yp[] = {0,1,0,0};
GLfloat zp[] = {0,0,1,0};
GLfloat wp[] = {0,0,0,1};
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGeni(GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
glTexGenfv(GL_S, GL_EYE_PLANE, xp);
glTexGenfv(GL_T, GL_EYE_PLANE, yp);
glTexGenfv(GL_R, GL_EYE_PLANE, zp);
glTexGenfv(GL_Q, GL_EYE_PLANE, wp);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);

glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}

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

// projection
glMatrixMode(GL_PROJECTION); glLoadIdentity();
int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport);
gluPerspective(60.0, (GLdouble)viewport[2]/(GLdouble)viewport[3], 1.0, 100.0 );

// texture matrix trickery
int tw,th;
glMatrixMode(GL_TEXTURE); glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, texName);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tw);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th);
glScaled( (viewport[2]/2)/(GLdouble)tw, (viewport[3]/2)/(GLdouble)th, 0 );
GLdouble proj[16];
glGetDoublev(GL_PROJECTION_MATRIX, proj); // grab projection matrix
glMultMatrixd(proj);

// view transform
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
glTranslatef(0,0,-2.5);

// render textured teapot
glPushMatrix();
const float ANGLE_SPEED = 60; // degrees/sec
float angle = ANGLE_SPEED * (glutGet(GLUT_ELAPSED_TIME) / 1000.0f);
glRotatef(angle*0.5f, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(angle*0.7f, 0, 0, 1);
glScalef(-1,-1,-1); // teapot is wound backwards (GL_CW), so flip it
glutSolidTeapot(1);
glPopMatrix();

glutSwapBuffers();
}

void reshape(int w, int h)
{
// make width/height evenly divisible by 2
w -= (w%2);
h -= (h%2);
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
}

void keyboard (unsigned char key, int x, int y)
{
switch (key) 
    { 
    case 27: exit(0); break;
    default: break; 
    }
}

void idle() { glutPostRedisplay(); }

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(640, 480);
glutInitWindowPosition(100, 100);
glutCreateWindow (argv[0]);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(idle);

init();
glutMainLoop();
return 0;
}

EDIT: Got the texture matrix only method figured out (should be OpenGL ES 1.1-able):

#include <GL/glut.h>
#include <cstdlib>
#include <cmath>

void glutTexturedCube(GLdouble size)
{
    GLfloat texc[] = {
        1,1,    0,1,    0,0,    1,0,
        0,1,    0,0,    1,0,    1,1,
        1,0,    1,1,    0,1,    0,0,
        1,1,    0,1,    0,0,    1,0,
        0,0,    1,0,    1,1,    0,1,
        0,0,    1,0,    1,1,    0,1,
    };

    GLfloat norm[] = {
        0,0,1,      0,0,1,      0,0,1,      0,0,1,
        1,0,0,      1,0,0,      1,0,0,      1,0,0,
        0,1,0,      0,1,0,      0,1,0,      0,1,0,
        -1,0,0,     -1,0,0,     -1,0,0,     -1,0,0,
        0,-1,0,     0,-1,0,     0,-1,0,     0,-1,0,
        0,0,-1,     0,0,-1,     0,0,-1,     0,0,-1,
    };

    GLfloat vert[] = {
        1,1,1,      -1,1,1,     -1,-1,1,    1,-1,1,
        1,1,1,      1,-1,1,     1,-1,-1,    1,1,-1,
        1,1,1,      1,1,-1,     -1,1,-1,    -1,1,1,
        -1,1,1,     -1,1,-1,    -1,-1,-1,   -1,-1,1,
        -1,-1,-1,   1,-1,-1,    1,-1,1,     -1,-1,1,
        1,-1,-1,    -1,-1,-1,   -1,1,-1,    1,1,-1,
    };

    GLuint idxs[] = { 
        0, 1, 2, 3,     
        4, 5, 6, 7,     
        8, 9, 10, 11,
        12, 13, 14, 15,
        16, 17, 18, 19,
        20, 21, 22, 23,
    };

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    // feed vertices in as texture coordinates
    glTexCoordPointer(3, GL_FLOAT, 0, vert);
    glNormalPointer(GL_FLOAT, 0, norm);
    glVertexPointer(3, GL_FLOAT, 0, vert);

    glPushMatrix();
    glColor4f(1, 1, 1, 1);
    glScaled(size, size, size);
    glDrawElements(GL_QUADS, sizeof(idxs)/sizeof(idxs[0]), GL_UNSIGNED_INT, idxs);
    glPopMatrix();

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

static GLuint texName;

void init(void)
{
glClearColor(0,0,0,0);

// create random texture
const int texWidth = 8;
const int texHeight = 8;
GLubyte tex[texHeight][texWidth][4];
for(int i = 0; i < texHeight; i++)
    {
    for(int j = 0; j < texWidth; j++) 
        {
        tex[i][j][0] = (GLubyte) rand()%255;
        tex[i][j][1] = (GLubyte) rand()%255;
        tex[i][j][2] = (GLubyte) rand()%255;
        tex[i][j][3] = (GLubyte) 255;
        }
    }
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex);

glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable(GL_TEXTURE_2D);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}

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

// projection
glMatrixMode(GL_PROJECTION); glLoadIdentity();
int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport);
gluPerspective(60.0, (GLdouble)viewport[2]/(GLdouble)viewport[3], 1.0, 100.0 );

// view transform
glMatrixMode(GL_MODELVIEW); glLoadIdentity();
glTranslatef(0,0,-3);

// render textured teapot
glPushMatrix();
const float ANGLE_SPEED = 10; // degrees/sec
float angle = ANGLE_SPEED * (glutGet(GLUT_ELAPSED_TIME) / 1000.0f);
glRotatef(angle*0.5f, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glRotatef(angle*0.7f, 0, 0, 1);

// texture matrix trickery
int tw,th;
glBindTexture(GL_TEXTURE_2D, texName);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tw);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &th);
GLint mmode = 0;
glGetIntegerv(GL_MATRIX_MODE, &mmode);
glMatrixMode(GL_TEXTURE); glLoadIdentity();
glScaled( (viewport[2]/2)/(GLdouble)tw, (viewport[3]/2)/(GLdouble)th, 0 );
GLdouble mat[16];
glGetDoublev(GL_PROJECTION_MATRIX, mat);
glMultMatrixd(mat);
glGetDoublev(GL_MODELVIEW_MATRIX, mat);
glMultMatrixd(mat);
glMatrixMode(mmode);

glutTexturedCube(1);
glPopMatrix();

glutSwapBuffers();
}

void reshape(int w, int h)
{
// make width/height evenly divisible by 2
w -= (w%2);
h -= (h%2);
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
}

void keyboard (unsigned char key, int x, int y)
{
switch (key) 
    { 
    case 27: exit(0); break;
    default: break; 
    }
}

void idle() { glutPostRedisplay(); }

int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(640, 480);
glutInitWindowPosition(100, 100);
glutCreateWindow (argv[0]);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutIdleFunc(idle);

init();
glutMainLoop();
return 0;
}
genpfault
wow, that's a big snippet, thanks. What does it do exactly? I can't test it right now because the glut library is not on the iphone SDK.
Steph Thirion
The first just draws a glutTeapot with glTexGen() and friends, the second a cube using only the texture matrix. The core logic should be glut-independent. I built them using FreeGLUT on Windows XP, but if you get GLUT/FreeGLUT going on OSX they should work fine.
genpfault