last day (16 days later) » 

7:38 PM
0
A: TBN Matrix : Eye vs. World Space Conflict

Nicol BolasSince you didn't provide links to these tutorials that confused you, I am going to assume that they weren't written by idiots and that the code in them is correct (where possible). They're all correct (well, except transpose guy; I'll get to him). Your problem (likely stemming from the writers n...

 
"Therefore, if you construct a matrix from these 3 normals, using them as the columns of the matrix, you will have created a matrix that goes from whatever space those normals were in to tangent space." I think you've got it backward - that matrix goes from tangent space to the source space. Consider if you multiply that matrix by (1, 0, 0) - you get the original tangent vector, in the source space. Multiplying by (0, 1, 0) gives you the bitangent and (0, 0, 1) the normal. So it's transforming from tangent space to the original space the vectors were in.
"if you see a tutorial that suggests you do lighting in world space, stop reading it. Read something written by someone who's not an idiot." A little more nuance, please. Pure world space can give you precision problems when far from the origin, but camera-centered world space is fine and quite useful, e.g. for sampling cubemaps or SH lighting that are defined relative to world space.
 
@NathanReed: "camera-centered world space" is functionally no different from "camera space".
 
@NicolBolas Camera space as I understand the term is oriented with the camera, which makes it more difficult to sample world-aligned cubemaps, as you need an extra transformation step.
 
@NathanReed: And how would you be sampling this "world-aligned cubemap"? With a normal. A normal that's in... some other space. So you're going to have to transform that normal anyway. You may as well throw in the transform to the cubemap's space while you're at it. That way, it'll work with cubemaps aligned to any other space too.
@NathanReed: "I think you've got it backward - that matrix goes from tangent space to the source space." No, that's how matrices work. The columns of a transformation matrix are the basis of the destination space, expressed relative to the source space. T is the tangent-space X axis, B is the tangent-space Y axis, and N is the tangent-space Z axis.
 
Let's talk about the matrix thing first
I still think you've got it backward - the columns of are the basis of the source space, expressed in the destination space
Think about it - in tangent space, the tangent is (1, 0, 0), right? And if you multiply that by a matrix whose first column is T in world space, then you'll get...T in world space.
 
7:49 PM
I need to think on this a bit more.
 
OK. And as for the cubemap - yes, you need to transform the normal from whatever space it's in to world space, so you might as well do the rest of the lighting in (camera-centered) world space too.
It's easy to calculate the eye and light vectors in world space since you have their positions in world space already, and then you don't hae to transform the normal twice (once into world space, and once into whatever space you're doing lighting in).
 
"yes, you need to transform the normal from whatever space it's in to world space" No, you need to transform the normal from whatever space it's in to some space. That doesn't have to be world space. It can be any arbitrary space. And you don't have to transform the normal twice; one matrix multiply is sufficient to get to any space.
 
But you need it in world space for the cubemap, in this scenario.
I'm thinking of a pre-baked specular cubemap, for instance, that represents the environment around you. It can't be in any arbitrary space because it represents an environment that's fixed in world space.
You could do the majority of your lighting in any arbitrary space, and then transform the cubemap vector to world space just to do the cubemap sample...but then you're adding extra transforms that you wouldn't need if you did the lighting in world space.
 
Yes, and you would still need that extra transform if you were using deferred rendering, since deferred rendering will generate positions that are in camera space, not world space (at least, using the usual reverse-transform methods).
 
8:04 PM
You can reconstruct position from depth in whatever space you want - it's just linear depth * view-vector interpolated from the vertex shader + camera pos. In camera space all you'd save is that the camera pos is 0, but that just turns a MAD into a MUL - so it really doesn't save anything.
 
"it's just linear depth * view-vector interpolated from the vertex shader + camera pos" What are you talking about? The math to reconstruct the position from the depth buffer requires a lot more than that. At the very least, you need the full inverse of the perspective matrix.
 
I think you're thinking of the math to convert the post-projective depth to linear depth. That requires a couple components of the perspective matrix, but hardly a full inverse - it comes down to 1 / (a * depth + b), where a and b are constants computed from the near and far plane depths.
You need to do that much regardless of what space you're reconstructing into; but once you have linear depth, it doesn't really matter what space you want to go to.
 
Linear depth in what space?
 
It's the z in camera space.
 
You can't compute the camera-space Z without the clip-space Z and clip-space W.
 
8:14 PM
Guys, so am I misinformed again, can you please summarize that matrix thing ? What should I do to convert my lighting vectors in eye space, to tangent space so that fragment shader can compute lightning ?
 
You sample the depth buffer, which gives you your post-projective depth, then to convert that to linear depth is just a rational function, 1 / (a * depth + b).
See the second equation on this page: reedbeta.com/blog/2012/05/26/…
@deniz, I wouldn't do lighting in tangent space - as Nicol pointed out in his answer, it's not necessarily an orthonormal basis, so dot products and such aren't guaranteed to "do the right thing" in that space
Instead, transform the normal vector from tangent space to some other space - I'm advocating world space, though there is some freedom to choose here - and do the lighting there.
 
but it is too expensive option because of its presence in fragment shader.
 
Doing a single matrix multiply in the fragment shader isn't very expensive.
Depends on your hardware, of course, but on modern hardware I wouldn't bat an eye at that.
 
You're going to have to do a matrix multiply there no matter what, since the fragment shader is where you fetch your normal.
So you're either going to transform your light into tangent space, or your normal into lighting space.
 
will converting to tangent space in vertex shader produce "low quality" result or corrupted result ?
 
8:23 PM
@NathanReed: I've looked at "reedbeta.com/blog/2012/05/26/…", and that computes the clip-space Z, not the camera-space Z. It also doesn't take into account the depth near/far range.
So I'm not really buying that as functional math.
 
@NicolBolas Um. Second equation on that page, right? 'n' and 'f' are the near/far range. They're right there in the equation.
 
@NathanReed: No, that's the perspective near/far. The depth range near/far set by glDepthRange is different.
 
Oh, OK. Sure, but that just adds another linear scale/bias on top of it. No big deal.
In Direct3D there's no equivalent to glDepthRange, AFAIK.
 
@NathanReed: Sure there is; it's part of the viewport structure.
It's the MinDepth/MaxDepth values.
 
Oh! So there is.
But people usually just leave those at the defaults (0 and 1). It's easy to take account of it if you need to support different values there, though.
 
8:29 PM
But my point is this: that equation doesn't compute camera-space Z. It computes the clip-space Z, which is in fact a linear Z, but it isn't in the same space as the camera.
 
guys can i ask you about skeletal animation if you have spare time ?
 
If the near and far plane values you plug in are in your regular world units, then the result will be in regular world units.
@deniz If you have a question about skeletal animation then I'd post it as a separate question on the main site.
 
I think it wouldn't be right to post a new question for it because i was just going to ask why I need inverse of bind poses.
 
Why you need the inverse bind-pose has nothing to do with bump mapping specifically. So it would be a different topic.
 
okay i am starting new thread
 
8:34 PM
@NicolBolas You can re-derive the equation yourself if you like - it's purely an inversion of what happens to the Z-component of a vector when it's multiplied by the perspective matrix generated in D3DXMatrixPerspectiveLH, and divided by W.
 
by the way @NathanReed you mentioned that "you got it backward" but when i create a TBN matrix using gl_NormalSpace and multiply my light vector, it works (it has just worked)
 
@deniz: And what does your TBN matrix look like?
 
 vec3 n = normalize(gl_NormalMatrix * gl_Normal);
    vec3 t = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz);
    vec3 b = cross(n, t) * gl_MultiTexCoord1.w;

    mat3 tbnMatrix = mat3(t.x, b.x, n.x,
                          t.y, b.y, n.y,
                          t.z, b.z, n.z);
lightDir = gl_LightSource[0].position.xyz;
    lightDir = tbnMatrix * lightDir;

    halfVector = gl_LightSource[0].halfVector.xyz;
    halfVector = tbnMatrix * halfVector;
 
No, the actual numbers. Also, that's transposed. OpenGL is column-major.
The first three numbers for mat3's constructor are a column, not a row.
So you should write that as mat3 tbnMatrix = mat3(t, b, n);
 
but it works :/
 
8:39 PM
Just because something appears to work in one case does not mean that it is actually working.
 
I think it's just happening to work because you are getting it backward twice :)
 
Exactly.
 
okay let me try the oother way
it is working reversed lol
 
Exactly, which means your example is probably too simple to find the places where it wouldn't work.
 
[vert]

#version 110

varying vec3 lightDir;
varying vec3 halfVector;

void main()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_TexCoord[0] = gl_MultiTexCoord0;

    vec3 n = normalize(gl_NormalMatrix * gl_Normal);
    vec3 t = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz);
    vec3 b = cross(n, t) * gl_MultiTexCoord1.w;

    mat3 tbnMatrix = mat3(t.x, b.x, n.x,
                          t.y, b.y, n.y,
                          t.z, b.z, n.z);

    lightDir = gl_LightSource[0].position.xyz;
 
8:42 PM
I meant your model data, not your shader.
 
it's just a cube
 
Yes, and a cube is a very simple object. Specifically, one where the TBN matrix is orthonormal. Indeed, odds are good that your TBN matrix is the identity matrix. An example where this is not the case could reveal the problems in your code.
@NathanReed: OK, back to this point: "linear depth * view-vector interpolated from the vertex shader + camera pos". Let's say we have the camera-space Z coordinate. Multiplying the direction from the camera by the camera-space Z will not give you the camera-space position. Also, it's not clear what you mean by "view-vector interpolated from the vertex shader". The vector from the camera to what? We're doing the deferred pass; we don't have a camera at all.
 
8:57 PM
When you draw your full-screen pass, in the vertex shader you can calculate a vector from the camera position to the vertex, thinking of the vertex as being at depth 1.0 from the camera, in whatever units, in whatever space
I.e. if it's a full-screen quad, you'll basically get four points at the corners of the view frustum, but at depth 1.0 (meters, let's say) from the camera
Or rather, you want to compute the vectors from the camera to those points, in world space or whatever space.
That vector will be linearly interpolated across the screen, and in the fragment shader you multiply it by the linear depth - that results in a vector from the camera to the pixel, in whatever space you wanted. Then you just add the camera pos, to get the pixel pos.
 
Right, that gives you a direction towards the point. But simply multiplying that direction by the Z component isn't enough to get you the rest. You would need to rescale the direction so that the direction vector's Z is one, then do the multiplication.
 
That's why you set it up in the vertex shader so that it's at depth 1.0.
It stays that way through the linear interpolation, so no extra rescaling needed in the fragment shader.
 
The Z of the direction you multiply with must be 1.0. Otherwise, the math doesn't work out.
 
Only if you're trying to reconstruct the position in camera space. The vector you interpolate can be in world space or any other space, too.
Then the Z-component of the interpolated vector doesn't have any special significance. The vector just points from the camera to a plane 1.0 units in front of the camera, in whatever space.
 
So now we're not talking about a "view-vector" (which I assumed meant "direction from a point to the view", as it often does). You're talking about a position.
 
9:04 PM
It's a vector from the camera to a point on a plane 1.0 units in front of the camera. So no, it's not a position.
And its direction is from the camera toward the pixel.
(Maybe there could be a better term for it - you're right that it's pointing the opposite direction to what "view vector" often means...)
 
Generally, when I think "direction", I substitute "unit vector". So maybe I'm just confusing myself.
 
Right, this is not a unit vector. It's scaled so that it points to a plane 1.0 units in front of the camera, so at the corners it will be a bit longer than a unit vector.
Only right in the center will it be a unit vector.
 
That's why I prefer thinking of it as the position on a plane 1 unit from the camera (in camera-space). Which you then scale out to the actual position.
 
Yep, that's probably a more precise way of putting it. But it doesn't have to be in camera-space, of course; it can be in world space or any space.
OK, I have to get going. Good chatting with you, Nicol.
 

  last day (16 days later) »