From: akin@tuolumne.engr.sgi.com (Allen Akin) Newsgroups: comp.graphics.api.opengl,comp.sys.sgi.graphics Subject: Re: Texture distortion problem Date: Fri, 11 Jul 97 14:13:03 1997 Organization: Silicon Graphics, Inc., Mountain View, CA In article <868594016.1232@dejanews.com>, wrote: | I'm trying to map an oblique photo texture onto a rectangle, that is, | the rectangular region in the photo has some perspective foreshortening. | When I map the corners of the distorted texture rectangle to the | rectangular polygon I get this angular artifact along the diagonal. | My understanding is that this has to do with a linear interpolation | optimization in the rendering pipeline. (Is that right?) Mostly. You've specified texture coordinates that vary nonlinearly across the surface of the rectangle. When the renderer decomposes the rectangle into two triangles, and then interpolates the texture coordinates linearly inside each of the triangles, you'll see a seam at the edge between the two triangles. This has nothing to do with perspective-correct interpolation of texture coordinates, which is why changing the perspective correction hint has no effect. It's more a problem of approximating a nonlinear model with a linear one. It would be a lot easier to explain this if I could draw you a picture on a whiteboard, but maybe a schematic presentation will do the trick. Imagine your rectangle projected onto the viewing plane, with texture coordinates attached to each vertex. I'll pick texture coordinates that shorten up one side of the square in texture space, just as you did in your test case: (x,y;s,t) = (0,1; 0.25,1) +-----------+ (1,1; 0.75,1) | | | | | | | | | | (0,0; 0,0) +-----------+ (1,0; 1,0) For the texture to be mapped onto the rectangle without distortion artifacts, the mapping must be linear; that is, each of ds/dx, ds/dy, dt/dx, and dt/dy must not vary across the rectangle. However, you can see that ds/dx is 1.0 at the bottom edge of the rectangle, and ds/dx is 0.5 at the top edge of the rectangle, so the mapping isn't linear. This causes artifacts when the rectangle is triangulated: (x,y;s,t) = (0,1; 0.25,1) +-----------+ (1,1; 0.75,1) | + | | + | | + | | + | | + | (0,0; 0,0) +-----------+ (1,0; 1,0) The lower-left triangle has ds/dx = 1.0 and ds/dy = 0.25. The upper-right triangle has ds/dx = 0.5 and ds/dy = -0.25. If you imagine rendering pixels horizontally across the rectangle, picking up appropriate texels as you go, you can see that you'll take a sudden ``turn'' in texture space just as you cross the edge between triangles, because ds/dx and ds/dy change abruptly. This is what creates the seam you're seeing. Note that none of this discussion involves perspective correction; that's a completely independent issue. This is all a matter of trying to model a nonlinear change in texture coordinates using two triangles, within which the texture coordinates vary linearly. This is very much like trying to model a nonplanar quadrilateral using two triangles; as stated, it's not a well-constrained problem, and simple approximations are subject to many kinds of artifacts. By the way, I like to transform this sort of problem into a purely geometric one, by thinking of quadrilaterals in (x,y,s) and (x,y,t) spaces. If you observe that one of those quadrilaterals is nonplanar, then you know right away you'll run into rendering artifacts. The same technique also gives you insight into the behavior of Gouraud shading on quadrilaterals with arbitrary colors at the vertices. | ... I can't make the artifact go away | when using explicit texture coordinates. Suggestions? There are two things you can do. One is to subdivide your quadrilateral into much smaller pieces, essentially creating a piecewise-planar approximation of your (x,y,s) surface. This is quite straightforward, though it might be slow if you need a large number of pieces to obtain adequate quality. Since you're using OpenGL (rather than other APIs which shall remain nameless :-)), you have a second option: you could take advantage of projective texturing. Here's a note on the subject from Mark Segal: From: segal (Mark Segal) Subject: Re: Textures on Quads? Date: 15 May 1996 18:55:56 GMT | The effect I'm hoping to achieve is essentially an image warp | where the top of the texture is scaled down, but all the | vertical lines stay straight (sort of emitting radially from | the top). Is there a way to achive this without having lots | and lots of geometry? Thanx for any help. You can do this with 'projective' textures (it only works with a quadrilateral, though). I won't give you all the details, but here's the idea: There's a (2D) projective map that carries a square (your texture) onto the quadrilateral. If you can figure out what this map is, you can load the texture matrix with it and OpenGL will figure out the coordinates for you. Alternatively, you could apply the map yourself and supply the texture 'q' coordinate (using glTexCoord4). What you have to do is look at a diagram like this / / | / | | | | | * \ | \ | \ This is supposed to look like what you'd see if you put your eye at the star and looked at a square in perspective in 2D (think about what a wireframe cube looks like in 3D in perspective). What you have to do is figure out the near, far, and other parameters to get the projection matrix that converts your square into your quadrilateral. (Ignore the y coordinate; you're working in 2D so all you care about is x and z.) Once you figure out this transformation you're done. If you don't want to figure out the viewing parameters, you can do this directly: look at (s') ( a b c )( s ) (t') = ( d e f )( t ) (q') ( g h i )( q ) Applied to (s,t,q) = (0,0,1), (1,0,1), (0,1,1), and (1,1,1) (these are the coordinates of the corners of your texture). You want (s'/q', t'/q') for each of these four (s,t,q) values to be the corners of your quadrilateral. This will give you 8 equations in the 9 unknowns a-i. You get one more equation because you can scale the matrix by anything you want and get the same result (equivalently, you're free to set any entry of the matrix that doesn't turn out to be zero to anything you want). Once you have the matrix, set the OpenGL texture matrix to it (you have to fill in a row and column of zeros for the third coordinate, which isn't being used) and supply (s,t) at the quadrilateral vertices. Or, leave the texture matrix at the identity and supply s', t', and q'. Hope this helps. Mark Allen