Hi all,
I'm doing volume rendering with 3D textures. To rotate my data I
manipulate openGL's texture matrix. For example:
glMatrixMode ( GL_TEXTURE );
glLoadIdentity();
// shift origin to center of texture
glTranslatef ( 0.5, 0.5, 0.5 );
// apply rotation
glRotatef(20, 0.0, 0.0, 1.0);
// shift back
glTranslatef ( -0.5, -0.5, -0.5 );
However, recently I discovered that one axis always seems to be
oriented in the wrong direction (depending on how I map texel coords
to quad vertices). Currently it's the z axis, meaning that glRotatef
(20, 0.0, 0.0, 1.0) will rotate my volume in clockwise direction
instead of counterclockwise.
No matter how I alter mapping of texture coords, there is always one
axis that is affected by this problem. Here is what my mapping looks
like:
glMatrixMode ( GL_MODELVIEW );
glLoadIdentity();
// view transformation
gluLookAt (0.0, 0.0, 1000.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
float tex_z;
int num_quads = 128;
glBegin ( GL_QUADS );
for ( int z = num_quads - 1; z >= 0; z -- )
{
tex_z = ((double) z) / ((double) num_quads);
glTexCoord3f ( 0.0, 0.0, tex_z );
glVertex3f ( -100, -100, z );
glTexCoord3f ( 1.0, 0.0, tex_z );
glVertex3f ( 100, -100, z );
glTexCoord3f ( 1.0, 1.0, tex_z );
glVertex3f ( 100, 100, z );
glTexCoord3f ( 0.0, 1.0, tex_z );
glVertex3f ( -100, 100, z );
}
glEnd();
glDisable ( GL_TEXTURE_3D );
glutSwapBuffers();
}
I already tested what's it's like for 2D textures. However I couldn't
find this effect there. I hope you guys can help me, because my work
strongly depends on a correct representation of data in terms of the
transformation matrix.
Cheers,
Sebastian
|
|
0
|
|
|
|
Reply
|
sebastianthelen
|
12/17/2008 3:49:58 AM |
|
sebastianthelen@googlemail.com wrote:
> Hi all,
>
> I'm doing volume rendering with 3D textures. To rotate my data
> I manipulate openGL's texture matrix. For example:
>
> glMatrixMode ( GL_TEXTURE );
> glLoadIdentity();
> // shift origin to center of texture
> glTranslatef ( 0.5, 0.5, 0.5 );
> // apply rotation
> glRotatef(20, 0.0, 0.0, 1.0);
> // shift back
> glTranslatef ( -0.5, -0.5, -0.5 );
>
> However, recently I discovered that one axis always seems to be
> oriented in the wrong direction (depending on how I map texel
> coords to quad vertices). Currently it's the z axis, meaning
> that glRotatef (20, 0.0, 0.0, 1.0) will rotate my volume in
> clockwise direction instead of counterclockwise.
Are you sure it's really clockwise? A screenshot/movie would be
nice (if you got a Unix system you can use the code I append at
the end of the post, to create a movie file, which you can
upload to YouTube).
Most people get confused by the right handedness of OpenGL,
whereas in school they oftenly learn left handed coordinate
systems. Now get one axis wrong and you end up, misinterpreting
rotations. Small hint: Just draw a small coordinate system
indicator into the scene: Just three lines originating at 0,0,0
going to
* 1,0,0 in red
* 0,1,0 in green
* 0,0,1 in blue
BTW: The way you address the texture coordinates, you're not
going to hit the texels exactly.
Here's the basic setup, with which you can dump a video directly,
though it's quite rudimentary, and requires the viewport being
the same size as the target video and not changed during between
frames.
/* videodump.c */
typedef struct VidDumpInfo {
int vid_width;
int vid_height;
FILE *pipe_mencoder;
void *image_buffer;
} VidDumpInfo;
void dump_frame(VidDumpInfo restrict * vdi)
{
if( !vdi || !vdi->image_buffer || !vdi->pipe_mencoder )
return;
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
if(viewport[2] != vdi->vid_width || viewport[3] !=
vdi->vid_height) {
/* error: Viewport size changed */
}
glPixelStorei(GL_PACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(viewport[0], viewport[1], viewport[2], viewport[3],
GL_BGR, GL_UNSIGNED_BYTE, vdi->image_buffer);
fwrite(vdi->image_buffer, vdi->vid_width*3, vdi->vid_height,
vdi->pipe_mencoder);
fflush(vdi->pipe_mencoder);
}
#define MAX_CMDLINE
int start_dump(VidDumpInfo *vdi, char const * const
output_filename)
{
int viewport[4];
char mencoder_cmdline[MAX_CMDLINE+1];
if( !vdi || ! output_filename )
return -1;
glGetIntegerv(GL_VIEWPORT, viewport);
vdi->vid_width = viewport[2];
vdi->vid_height = viewport[3];
vdi->image_buffer = (void*) realloc(image_buffer, 3*w*h);
if( !vdi->image_buffer ) {
return -1;
}
snprintf(mencoder_cmdline, MAX_CMDLINE,
"mencoder -"
" -demuxer rawvideo"
" -rawvideo w=%d:h=%d:format=bgr24:size=%d"
" -flip -nosound"
#if defined VIDEODUMP_XVID
" -ovc xvid -xvidencopts bitrate=3000"
#else if defined VIDEODUMP_X264
" -ovc lavc -lavcopts vcodec=libx264:threads=2"
#else if defined VIDEODUMP_NUV
" -ovc nuv"
#endif
" -o %s",
vdi->vid_width, vdi->vid_height,
vdi->vid_width*vdi->vid_height*3,
output_filename );
vdi->pipe_mencoder = popen(mencoder_cmdline, "w");
return 0;
}
void stop_dump(VidDumpInfo *vdi)
{
fclose(vdi->pipe_mencoder);
free(vdi->image_buffer);
}
Call start_dump with vdi pointing to a VidDumpInfo stored as some
persistent place in memory, to begin to caputure video
(eventually reduce the viewport size to something reasonable
beforehand). Then for each frame call dump_frame with the OpenGL
context being current. Once done call stop_dump. Both with the
vdi being the one, initialized by start_dump.
The main dependency on Unix or a alike comes from the use of
popen. Although Windows provides popen, too, it's implementation
is somewhat broken.
Note that this is a hack and in a big scale application you
should either implement a frameserver or use some encoder/muxer
library (like the one of ffmpeg).
Wolfgang Draxinger
--
E-Mail address works, Jabber: hexarith@jabber.org, ICQ: 134682867
|
|
0
|
|
|
|
Reply
|
Wolfgang
|
12/17/2008 9:26:39 AM
|
|
Hi Wolfang,
first of all thank you, I really appreciate your immediate response.
> Are you sure it's really clockwise? A screenshot/movie would be
> nice (if you got a Unix system you can use the code I append at
> the end of the post, to create a movie file, which you can
> upload to YouTube).
Well, I'm pretty sure, though not 100% =3D) I tried to chose unambigous
rotation angles, so that it's easy to make out the orientation (hence
20=B0). However, I think it's definitely a good idea to draw a small
coordinate system, just to be sure. Taking a video seems to be kind of
an overkill to me, though I'm really thankful for your video capturing
code. Don't you think 2 screenshots should identify each ratation's
orientation uniquely?
> Most people get confused by the right handedness of OpenGL,
> whereas in school they oftenly learn left handed coordinate
> systems. Now get one axis wrong and you end up, misinterpreting
> rotations. Small hint: Just draw a small coordinate system
> indicator into the scene: Just three lines originating at 0,0,0
> going to
> * 1,0,0 in red
> * 0,1,0 in green
> * 0,0,1 in blue
Yeah, probably something like that is right now happening to me and
I'm misinterpretating rotations. I currently assume that a texture's x
coordinate increases when moving "right", a y coordinate increases
when moving "up" and a z coordinate increses when moving "out of the
screen" I tried to compare the rotations of my 3d texture to that of
threedimensional vertices my manipulating openGL's modelview matrix.
Calling glRotate(20, 0.0, 0.0, 1.0) should rotate a perfectly
vertically oriented line through the origin, so that is runs from top
left to bottom right. Vertices behave exactly as expected while
textures seem to have this weird rotation "bug". I will try to come up
with a few screenhots as soon as possible.
> BTW: The way you address the texture coordinates, you're not
> going to hit the texels exactly.
What exactly do you mean by that? Can you explain that a little more
in detail?
Thanks,
Sebastian
|
|
0
|
|
|
|
Reply
|
sebastianthelen
|
12/18/2008 9:49:59 PM
|
|
sebastianthelen@googlemail.com wrote:
>> BTW: The way you address the texture coordinates, you're not
>> going to hit the texels exactly.
>
> What exactly do you mean by that? Can you explain that a little
> more in detail?
In your case it probably doesn't matter, but I noticed, that
you're drawing 128 slices, probably to match with 128 layers in
the texture volume.
What a lot of people get wrong with OpenGL textures is, how
OpenGL interprets the coordinate. Imagine a unit
line/square/cube/hypercube, etc. or in general a [0,1]^n
intervall. Now I give you the task to devide this intervall into
2^k subintervalls in each of the n dimensions; where are the
centers of those subintervalls. OpenGL treats textures exactly
in this way: A function
t_i -> r,g,b,a: [0,1]^n |-> [0,1]^4, i \in {1...n}
(I hope you're not scared of mathematical notation).
However this function is defined in terms of a descreete grid,
with the sampling points being the texel values, placed in the
center of the subintervalls (these subintervalls are, what one
refers to as texel or pixel). So the actual sampling value is
determined by interpolating between adjacent sampling point,
with the interpolation factor being the offset from the texel
center(s).
0, 1 and similair are _not_ located on the texel centers, but
their borders. So if you're in GL_LINEAR filtering, GL_WRAP
clamping, the texture coordinate 0 takes exactly 0.5 of the
first texel and 0.5 of the last texel in that dimension (well,
actually you've to consider the distance, so it's more like
sqrt(ds^2 + du^2 + ...), but I think you got the concept).
So to exactly hit a texel you've to lineary remap the texture
coordinates a bit. This can be done with a matrix and homogenous
coordinates of course, and OpenGL's 4x4 GL_TEXTURE_MATRIX can do
it for 3D textures. But for the general case I give you a code
snippet to calculate the texture coordinate to hit a desired
texel's "bull's eye":
GLfloat TexCoordByTexel(GLuint texture_dim, GLuint texel_pos)
{
return 0.5/texture_dim + texel_pos /
( (texture_dim) + 1./(texture_dim) );
}
should be easy enough to translate into other languages.
Wolfgang Draxinger
--
E-Mail address works, Jabber: hexarith@jabber.org, ICQ: 134682867
|
|
0
|
|
|
|
Reply
|
Wolfgang
|
12/18/2008 11:42:32 PM
|
|
On 18 Dez., 15:42, Wolfgang Draxinger <wdraxin...@darkstargames.de>
wrote:
> sebastianthe...@googlemail.com wrote:
> >> BTW: The way you address the texture coordinates, you're not
> >> going to hit the texels exactly.
>
> > What exactly do you mean by that? Can you explain that a little
> > more in detail?
>
> In your case it probably doesn't matter, but I noticed, that
> you're drawing 128 slices, probably to match with 128 layers in
> the texture volume.
>
> What a lot of people get wrong with OpenGL textures is, how
> OpenGL interprets the coordinate. Imagine a unit
> line/square/cube/hypercube, etc. or in general a [0,1]^n
> intervall. Now I give you the task to devide this intervall into
> 2^k subintervalls in each of the n dimensions; where are the
> centers of those subintervalls. OpenGL treats textures exactly
> in this way: A function
> t_i -> r,g,b,a: [0,1]^n |-> [0,1]^4, i \in {1...n}
> (I hope you're not scared of mathematical notation).
> However this function is defined in terms of a descreete grid,
> with the sampling points being the texel values, placed in the
> center of the subintervalls (these subintervalls are, what one
> refers to as texel or pixel). So the actual sampling value is
> determined by interpolating between adjacent sampling point,
> with the interpolation factor being the offset from the texel
> center(s).
>
> 0, 1 and similair are _not_ located on the texel centers, but
> their borders. So if you're in GL_LINEAR filtering, GL_WRAP
> clamping, the texture coordinate 0 takes exactly 0.5 of the
> first texel and 0.5 of the last texel in that dimension (well,
> actually you've to consider the distance, so it's more like
> sqrt(ds^2 + du^2 + ...), but I think you got the concept).
>
> So to exactly hit a texel you've to lineary remap the texture
> coordinates a bit. This can be done with a matrix and homogenous
> coordinates of course, and OpenGL's 4x4 GL_TEXTURE_MATRIX can do
> it for 3D textures. But for the general case I give you a code
> snippet to calculate the texture coordinate to hit a desired
> texel's "bull's eye":
>
> GLfloat TexCoordByTexel(GLuint texture_dim, GLuint texel_pos)
> {
> =A0 =A0 =A0 =A0 return 0.5/texture_dim + texel_pos /
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ( (texture_dim) + 1./(texture_dim) );
>
> }
>
> should be easy enough to translate into other languages.
>
> Wolfgang Draxinger
> --
> E-Mail address works, Jabber: hexar...@jabber.org, ICQ: 134682867
Hi Wolfgang,
I finally found some time to continue working on my little project. I
added a coordinate system and multiplied both the texture matrix and
modelview matrix by the same transformation matrix (glRotate(20, 0, 0,
1)). I took 2 screenshots and sent them to you via email. As you can
see, the effect of the transformation is inverse. The coordinate
system's tripod turns left, while the texture turns right. Weird,
huh?!
I thought a little about manipulating GL_TEXTURE and the consequences
for texture mapping. Is it possible, that I have to apply the inverse
transformation matrix to my texture? For example, think of a pointer
pointing at 12:00 on a clock. Now rotate this pointer 90=B0 left, i.e.
it will point at 9:00. If I understand texture mapping correctly, the
texel at 9:00 will be mapped the vertex, where the original 12:00
texel would have been mapped. That means although I apply a
counterclockwise rotation to the texture matrix, the optical effect
will be that of a clockwise rotation. If that was the case, I would
have to invert my transformation matrix. What concerns me a little is,
that my argumentation would only apply to the current z axis, since x-
and y rotation seem to work. Also tests with 2D textures seem to
contradict this argumentation.
Sebastian
|
|
0
|
|
|
|
Reply
|
sebastianthelen
|
12/21/2008 1:32:21 PM
|
|
|
4 Replies
192 Views
(page loaded in 0.065 seconds)
|