4

I'm finding myself a bit lost amongst the many many sign and domain conventions used in webGl. Take a gander at this regl example (it shows the baboon test image). Here's what I understand of it:

  • No primitive is specified; It likely infers GL_TRIANGLE.
  • As such, it creates a single triangular surface with vertices (-2, 0), (0, -2), (2, 2) in world-space. Drawing this out on paper, it appears this was chosen specifically because it encompasses the region [0,1] x [0,1].
  • uv takes its values from world space, including this [0,1] x [0,1] region (which is the domain of texture2d). If I understand correctly, the convention is that +u points towards the right edge of the image, +v points towards the top.
  • gl_Position is set to 1 - 2*uv, so that the image occupies [-1,1] x [-1,1], z=0 in "clip space," whatever that is.
  • More importantly, it also means that the +{u,v} directions correspond to -{x,y}!
  • The displayed image, is, in fact, mirrored horizontally!! (compare to this) But it is not mirrored vertically. There must be something else along the eventual conversion to/from onscreen pixel coordinates that cancels out the negative factor in y.
  • However, Google searches bring up no evidence that gl_Position is mirrored in any way relative to the screen. I see discussion that it is left-handed, but that is simply from negating z relative to world coordinates. (e.g. this question here)

In brief, it appears to me that this image ought to be mirrored vertically as well.

What am I missing?


For posterity, the code is reproduced below: (MIT licensed)

const regl = require('regl')()
const baboon = require('baboon-image')

regl({
  frag: `
  precision mediump float;
  uniform sampler2D texture;
  varying vec2 uv;
  void main () {
    gl_FragColor = texture2D(texture, uv);
  }`,

  vert: `
  precision mediump float;
  attribute vec2 position;
  varying vec2 uv;
  void main () {
    uv = position;
    gl_Position = vec4(1.0 - 2.0 * position, 0, 1);
  }`,

  attributes: {
    position: [
      -2, 0,
      0, -2,
      2, 2]
  },

  uniforms: {
    texture: regl.texture(baboon)
  },

  count: 3
})()
Community
  • 1
  • 1
Exp HP
  • 695
  • 6
  • 24
  • [*"clip space," whatever that is*](http://www.songho.ca/opengl/gl_transform.html), research is it that hard? Most of your other questions are framework specific and not related to the WebGL API. You conclusion that {u,v} = -{x,y} is wrong. U and X both go from left to right, Y however goes from bottom to top while V goes from top to bottom. – LJᛃ Oct 30 '16 at 22:51
  • @LJᛃ I see words defined in terms of words defined in terms of words... After seeking many pages about clip space, the best I could surmise was that, like the git staging area, *it will make sense later.* – Exp HP Oct 30 '16 at 23:26
  • Also, "your premise was wrong"/"the mapping of uv to image pixel coordinates isn't part of GL" sounds like a fine answer to me. I didn't realize samplers were constructed from raw data streams. – Exp HP Oct 30 '16 at 23:29
  • Let me refine my previous statement as its technically not correct, UV coordinates work the same way as position coordinates {u,v}={x,y} left to right, bottom to top. The thing is that texture data is(as you already noticed) mapped directly to memory so that the first row of pixels read from the image is at V coordinate 0(or to be precise at `.5 / height` if any sampling other than `NEAREST` is used) thats why they appear flipped and why I said V goes from top to bottom. – LJᛃ Oct 31 '16 at 01:22

1 Answers1

4

There's no such thing as "world space" in WebGL nor in your example above. WebGL only cares about clips space or (normalized device coordinates). World space is something the app/framework/library deals with and provides shaders to ultimately give WebGL clip space (or normalized device coordinates)

As pointed out by LJ the code above is not webgl it's regl, some library. So what it's going to do is up to that library not webgl. For example it could be flipping all textures it loads and how would we know? That said it's pretty easy to guess what it will do.

The short answer to your question is the entire image is flipped because of

gl_Position = vec4(1.0 - 2.0 * position, 0, 1);

change it to

gl_Position = vec4(position, 0, 1);

And you'll see it's upside down

It's drawing a giant triangle. The input values are

  position: [
      -2, 0,
      0, -2,
      2, 2]
  },

and this line

gl_Position = vec4(1.0 - 2.0 * position, 0, 1);

means the values written to gl_Position are

1 - 2 * -2, 1 - 2 *  0
1 - 2 *  0, 1 - 2 * -2
1 - 2 *  2, 1 - 2 *  2

Which is

 5,  1
 1,  5
-3, -3

If we plot that relative to the canvas/screen/framebuffer we get

triangle-diagram

Where the blue area is the screen/canvas

As for the flipping it's important to note that textures have no "up" concept. what's more important to remember is the texture coordinate 0,0 references the first pixel in the texture and 1,1 references the last pixel in the texture.

Here's the texture coords as they apply to that triangle

enter image description here

Where the red dot in the texture represents 0,0 in texture coordinates.

The reason to think of 0,0 as the start of the texture (rather than the bottom) is that when you're rendering to textures (through framebuffers) you don't need to do any flipping. The flipping only comes into play when you finally render to the screen because WebGL happens to put -1, -1 at the bottom left when drawing to the screen.

That's kind of hard to explain but if you look at this example you'll see when rendering offscreen to/from textures no flipping is required. It's only when rendering to the canvas/screen.

If you want to see the large triangle change the shader to this

gl_Position = vec4((1.0 - 2.0 * position) * 0.2, 0, 1);

To verify the texture is flipped here's the original

enter image description here

And you can see in the it's rendered flipped in the regl example

enter image description here

As for learing WebGL and clip space I'd recommend these tutorials

gman
  • 100,619
  • 31
  • 269
  • 393
  • *There's no such thing as "world space"* Huh, up until now it has not even occurred to me that `position` is nothing more than a user-defined attribute. So many depictions of the GL "transformation pipeline" include a model/view transform that I have failed to realize just how *arbitrary* it is! – Exp HP Oct 31 '16 at 11:59
  • 1
    The problem is GL used to have a *fixed function pipeline* and lots of old tutorials and literature sill bring that stuff up. Since shaders became a thing most of that has been deprecated (and completely removed in the ES branch of GL on which WebGL is based and which is what's in most mobile devices). So now [GL is just a rasterization engine](http://webglfundamentals.org/webgl/lessons/webgl-2d-vs-3d-library.html) and everything else is 100% user supplied. – gman Oct 31 '16 at 16:21