So what are Fragment Program Partial Derivatives

NV30 question of course

What,How,Why ?

Originally posted by pocketmoon:
[b]NV30 question of course

What,How,Why ?

[/b]

Some shading effects want to know how fast a given quantity is changing in X and Y. The DDX and DDY instructions give you this information (or more accurately, approximate it). My background with fancy shading effects is really limited, so here’s a trivial example:

The extension SGIX_texture_lod_bias allowed applications to independently bias the “S”, “T”, and “R” partials used for LOD calculation. Some of the motivations for this approach rather than the simpler constant LOD bias in OpenGL 1.4 include (from the spec):

Examples of textures which can benefit from this LOD control include video-capture images which are filtered differently horizontally and vertically; a texture which appears blurry because it is mapped with a nonuniform scale, such as a road texture which is repeated hundreds of times in one dimension and only once in the other; and textures which had to be magnified to a power-of-two for mipmapping.

NV_fragment_program allows you to emulate this functionality using the following instruction sequence:

DDX R0, f[TEX0];
DDY R1, f[TEX0];

Scale the partial derivatives of R by 1.5

MUL R0, {1.5, 1.0, 1.0, 1.0};
MUL R1, {1.5, 1.0, 1.0, 1.0};

Do the lookup with program-specified partials.

TXD f[TEX0], R0, R1, TEX0, 2D;

I’m sure you can do a lot of fancier things. This is a relatively cheesy, but easy-to-understand example.

[This message has been edited by pbrown (edited 09-06-2002).]

First, do you know what a partial derivative is?

A partial derivative is the derivative of a multivariate function in only one of its dimensions. e.g., it is how fast a function in x and y is changing in only x or y. This is stuff ussually covered in a third semester Calculus class (which is basically Calculus I but you have to do everything 3 times because you end up having to take the x, y, and z partial deriviatives of a 3d function and then do some calculus I stuff on the 3 results).

If you think of the screen as a function of x and y, then the partial derivative in x or y describes how a fragment changes from pixel to pixel in either the x or y direction.

The fragment values in fragment programs are stored in registers, and you can take the derivative of any register and any particular point in the program.

It basically tells you what the difference between the contents of this register at this point in the program and the contents of this same register at this same point in the program for adjacent pixels.

Or, in other words, by how much will this register will be different when it is run for the pixel one over (dx) or one below (dy)

e.g., if you store a texture lookup result in the register, then take the partial derivative in x, then the result of the derivative will be the difference between the texture lookup this time and the texture lookup for the adjacent pixel. This is how fast the texture is changing.

The nvidia docs say its impossible to take a second derivative, which breeds some doubt into my thinking, because it seems I could just store the result of my partial derivative into another register and then take its derivative again.

Thanks, I’m happy with the definition of a partial derivative. I was really wondering what application it could have.

I suppose if your generating things like procedural textures where your not getting a nice filtered texel value from a texture map, it would be handy to know how fast you procedural texture is changing ?? To smooth things out ???

Yeah, if you take the derivative of your texture coordinates, you can get the projected area of the texture on the screen, which can be used to determine the sample kernel size.

if s and t are texture coordinates then

ds * dt = texel_area

texel_area is how many texels have been squished (minified) into the current screen pixel. Of course, if its being magnified, then texel_area will be less than one.

Actually, the area should be computed as the area of a parallelogram. You want a 2D cross product. But that’s another story…

• Matt

hehe

maybe ‘*’ meant ‘x’, how can you tell, yeah, that’s the ticket

I was not specifically thinking about it (in very general terms what I said was correct, I just didn’t think about details), but indeed, s and t are vectors with respect to x and y and the * is a cross product.

Lets express it more precisely:

texel_area = cross(vec(dx(s), dy(s)), vec(dx(t), dy(t)))

Better?

[This message has been edited by Nakoruru (edited 09-06-2002).]

What’s a 2D cross product?
It’s commonly only defined for 3D vectors, isn’t it?

Ya at first hearing 2D cross product is like WTF. But actually what it is, is where you rotate one vector CCW by 90 degrees and take the dot product between your two vectors. You can use this to determine if a set of points are in say clockwise order.

Oh BTW, I just found a page that talks about this exactly like i say. Here is the URL: http://www.geocities.com/SiliconValley/2151/math2d.html

-SirKnight

Originally posted by Nakoruru:
The nvidia docs say its impossible to take a second derivative, which breeds some doubt into my thinking, because it seems I could just store the result of my partial derivative into another register and then take its derivative again.

It’s not “impossible.” It’s definately possible to take a second derivative (and third, fourth…) but the language in that spec says doing so “may not yield accurate second derivatives.”

So, let’s back up from implementation for a moment (and since I don’t work for NVIDIA I’d just as soon not get into their implementation details anyway) and back up to the OpenGL machine. (This has come up in GL2 as well.)

So, for the OpenGL machine, we need to document a method for producing an approximate derivative, even though implementations may choose other methods to approximate a derivative.

Let’s take a closer look at two obvious methods to produce an approximate derivative.

Forward differencing:
eq. 1a: F(x+dx)-F(x) ~ dFdx(x) * dx
eq. 1b: dFdx(x) ~ ( F(x+dx)-F(x) ) / dx
Backward differencing:
eq. 2a: F(x-dx)-F(x) ~ -dFdx(x) * dx
eq. 2b: dFdx(x) ~ ( F(x)-F(x-dx) ) / dx

Either equation 1b or 2b produces an acceptable approximate derivative, so either can be used, or even both can be used. (There might be some invariance restrictions when both are used.)

Imagine now that we’ve got two neighboring fragments. The first fragment’s approximate derivative is calculated by 1b. The second fragment’s approximate derivative is calculated by 2b. We’ll assume single sampling, and set dx=1.

First fragment:
3a: F(x) = F(x0)
3b: F(x+dx) = F(x1)
Substituting into 1b:
3c: dFdx(x0) ~ ( F(x1)-F(x0) ) / dx

Second fragment:
4a: F(x) = F(x1)
4b: F(x-dx) = F(x0)
Substitution into 2b:
4c: dFdx(x1) ~ ( F(x1)-F(x0) ) / dx

Eek. 3c and 4c are the same!

Take the dFdx again, and what will you get?
Zero. Which isn’t a very accurate second derivative. (Or third, or fourth…)

Does this make sense? (Does this make no sense?)

-mr. bill