PDA

View Full Version : dPdx and dPdy from OpenGL 2.0 spec

Nakoruru
07-02-2002, 08:09 AM
Both of these fragment shader functions (dPdx(p) and dPdy(p)) are defined in the spec as derivative in x and y (respectively) using local differencing for the input argument p.

My question is: What is p?

Having just the functions dx() and dy() makes sense to me. What good is passing in some variable p? It can't mean 'with respect to p' because p ultimately just a constant, and the derivative of a constant is 0.

mrbill
07-02-2002, 08:34 AM
Originally posted by Nakoruru:
[B]Both of these fragment shader functions (dPdx(p) and dPdy(p)) are defined in the spec as derivative in x and y (respectively) using local differencing for the input argument p.

My question is: What is p?
B]

It's an expression, and you're right, if the expression is uniform, it's derivative will be zero.

But more commonly, (and more usefully) the expression will have a varying component to it. The simplest expression is a direct interpolated value coming into the fragment unit from the rasterizer. This can be used to estimate filter widths for anti-aliasing procedural textures. (There's even a built in function fwidth that is based on dpDx and dpDy.)

In the OpenGL 2.0 Shading Language Whitepaper, look at the anti-aliased checkerboard and anti-aliased simple brick shader examples for common usage.

-mr. bill

Nakoruru
07-02-2002, 10:47 AM
Well, the argument may be an expression, but expressions are evaluated before they are passed to functions. The argument is not an expression, its a genType, which is a float or, vecX

If you pass an expression, it will be resolved to a single value and then passed (unless there is more going on, but this is explained as being a function, not a special contruct that evaluates your expression. And wouldn't that be a bit complicated for a fragment shader?).

At first I thought that p might be an enumeration, and that you could perhaps pass 'MultiTexCoord0' or 'Color' and get the dx and dy. But that is not what appears to be going on.

It seems to be far from clear and I would love someone who knows for sure to explain what is going on.

fathom
07-02-2002, 11:09 AM
Sounds a bit like the Renderman Shading Language derivative functions. P in renderman sl is typically used to refer to a 3d geometric point. You'd use those functions to find the derivative of a particular variable (or of u or v as surface params) at a particular point on a surface. But I dunno if it's the same for the openGL sl.... pure speculation.

Nakoruru
07-02-2002, 11:18 AM
Combining what you said about uniform vs varying with the comment in the language specification (dunno if there is more detail in the whitepaper) I get the feeling that my confusion stems from the fact that this is not so much a function, as a special operator. It simply did not make any sense to take the derivative of a scalar or vector value.

I guess that the that this 'function' takes needs to be 'passed' the label (name) of a varying variable (ugh, that sounds redundant ^_^)

It 'returns' the rasterizers current dx or dy for the register its stored in.

Does this sound right? If this is true then they have totally failed to properly explain what is going on. If not then what is going on?

I originally ask 'what is p'. If I pass in 2.3 what will I get? If I pass TexCoord0 + Color * BackColor what do I get? Are these valid? The spec says they are but doesn't explain what I get. Placing a varying variable name will resolve to a scalar or vecX as well (like 2.3) unless this function is somehow special. It seems like you would need a unary operator like & in C++ in order to refer to some other aspect of the variable other than its value.

Maybe this just needs to be called an operator instead of a function.

I'm going to stop giving you all rope to hang me with now...

mrbill
07-02-2002, 11:38 AM
Originally posted by Nakoruru:
I get the feeling that my confusion stems from the fact that this is not so much a function, as a special operator. It simply did not make any sense to take the derivative of a scalar or vector value.

Not so much a function as a special fragment only function. Does that help? You can think of dPdx as a special function that is not a derivative of a single scalar or vector, but rather the delta(s) between the scalar or vector and one of its neighbors dx away.

The Renderman Companion and Advanced Renderman talk about this a bit. A couple of good published details about implementations of dPdx and dPdy can be found with "BMRT: A Global Illumination Implementation of the RenderMan Standard" [Gritz, Hahn] and "Implementing RenderMan - Practice, Problems and Enhancements" [Slusallek, Pflaum, Seidel].

-mr. bill

folker
07-02-2002, 12:06 PM
I assume the following:

If I pass in 2.3 what will I get?

Zero.

If I pass TexCoord0 + Color * BackColor what do I get?

Using usual derivation rules:

dPdx(TexCoord0 + Color * BackColor)
= dPdx(TexCoord0) + dPdx(Color) * BackColor + Color * dPdx(BackColor)

The meaning of dPdx(TexCoord0) is clear, it is TexCoord0 derived by the x coordinate. If Color is the vertex color passed from the vertex shader, then the meaning of dPdx(Color) is also clear in the same way. What is Backcolor?

Looking at the glslangspecv1.0.pdf specification, it includes the following note about the dPdx etc. functions:

These two functions are commonly used to estimate the
filter width used to anti-alias procedural textures.We
are assuming that the expression is being evaluated in
parallel on a SIMD array so that at any given point in
time the value of the function is known at the grid
points represented by the SIMD array. Local
differencing between SIMD array elements can
therefore be used to derive dPdx, dPdy, etc.

Conceptually (mathematically), a fragment shader does not operatate on normal values
(scalars, vectors etc.), but they operate on functions depending on x and y, and the fragment shader value value is evaluated only at end by fixing x and y for each fragment. Note that most values you work with in fragment shaders are calculated by interpolating vertex values, so they naturally are functions of x and y.
(What about frame buffer values? Probably dPdx is undefined for frame buffer values.)

Instead of this abstract picture, you may simply consider dPdx(p) as normal functions but wich have access to the values of p at neighboring pixels as simplified explanation.

Nakoruru
07-02-2002, 12:15 PM
First, I understand what the function returns and how it works. I guess now I am just complaining that its not property classified in the spec.

It can't be thought of as a function because it cannot be implemented by any facility in the language unlike all the other functions. This is because it takes as the type of its argument a register and not a value (unless you can explain to me what dPdx(3.14) means). It returns something associated with that register, in this case the dx or dy of the interpolators.

This makes it more closely related to the & operator in C++ (which returns the address associated with a lable instead of its value). Of course, the & operator could be written as get_addr(myvar), but that would not make it any less an operator.

It would fail on get_addr(3.14) just like I suspect that dPdx(3.14) will fail. I just saying that the spec says it takes a genTyp, while I really think it takes is an lvalue with a storage type of varying.

Since there is no way to express dPdx in the language because of its specialness, you would not even be able to write a proper replacement for it, so it should also not be listed as a function you can override (just like you can't override constructors which are also not functions).

At least, that is how I understand it. I am pretty much just stating what I believe until someone from 3dLabs clears things up. I just noticed that they have a new Bulletin board, maybe this would be a good thing to post.

folker
07-02-2002, 12:40 PM
Originally posted by Nakoruru:
First, I understand what the function returns and how it works. I guess now I am just complaining that its not property classified in the spec.

It can't be thought of as a function because it cannot be implemented by any facility in the language unlike all the other functions. This is because it takes as the type of its argument a register and not a value (unless you can explain to me what dPdx(3.14) means). It returns something associated with that register, in this case the dx or dy of the interpolators.

This makes it more closely related to the & operator in C++ (which returns the address associated with a lable instead of its value). Of course, the & operator could be written as get_addr(myvar), but that would not make it any less an operator.

It would fail on get_addr(3.14) just like I suspect that dPdx(3.14) will fail. I just saying that the spec says it takes a genTyp, while I really think it takes is an lvalue with a storage type of varying.

Since there is no way to express dPdx in the language because of its specialness, you would not even be able to write a proper replacement for it, so it should also not be listed as a function you can override (just like you can't override constructors which are also not functions).

At least, that is how I understand it. I am pretty much just stating what I believe until someone from 3dLabs clears things up. I just noticed that they have a new Bulletin board, maybe this would be a good thing to post.

In a simplified picture, a program is executed for each fragment, operating on values. But strictly speaking this is not true. In fact, the shader languages operates on functions. Thus, dPdx are also functions in the same way as all other functions, they are no special operators. So everything makes sense and is well-defined (except some exceptions, e.g. dPdx applied to a frame buffer value).

Especially note that dPdx etc. can be expressed in the language itself. They don't play a special role. Of course, they are build-in functions and cannot be implemented as user-defined functions, but this holds for most build-in functions.

BTW, my background is physics, there it is exactly the same: A electromagnetic field vector is not a simple value, but a function of each point in space. For most operations like addition, you can consider a vector as single value in space without problems. But special operations like gradient only make sense if you know that the vector in fact is a function. But everything is always well-defined.

folker
07-02-2002, 12:46 PM

Originally posted by Nakoruru:
It would fail on get_addr(3.14) just like I suspect that dPdx(3.14) will fail.

It may fail for a particular compiler. But the correct implementation simply would give zero.

I just saying that the spec says it takes a genTyp, while I really think it takes is an lvalue with a storage type of varying.

I disagree. You can pass everything. The spec is correct. Expressions depending on the frame buffer values may be a valid expression, but the actual value may be undefined. But any r-value expression depending on varying variables, constants etc. are no problem at all and well-defined.

Since there is no way to express dPdx in the language because of its specialness, you would not even be able to write a proper replacement for it, so it should also not be listed as a function you can override (just like you can't override constructors which are also not functions).

You can express these functions in the language. You can override it, you can also write a user-defined and user-implemented wrapper function for it, whatevey you want.

Of course, not every compiler might support it. But conceptually this is all no problem.

Nakoruru
07-02-2002, 01:35 PM
I do not think you understand exactly how programming languages work, or you would have caught yourself (or at least looked at the spec for SL).

To do what you describe, dPdx would have to take an EXPRESSION as its argument (which is not what the spec says). Being a function it takes a VALUE, that is what the spec says.

To show you what I mean, what do you think happens when I write this in C:

sqrt(2+2);

Does the function sqrt get some representation of the expression '2+2' or does it get the evaluated expression '4'

You should now see my problem. dPdx cannot take the derivative of an expression it never sees. If it was like you said it would be very special indeed, because the grammar of SL would have to contain some mechanism for passing an expression to a function, and then taking the derivative of it. But it doesn't, so what you propose cannot happen. I cannot just pass in an arbitraury expression, because that expression will get evaluated, and the value will get passed to the dPdx.

But, dPdx cannot do what it says it does with a value, it has to know the register it came from so it can figure out what that registers iterator delta is.

If it did what you said would definitely not be a function, but a special grammatical contruct. I do not think that is true at all.

Everyone should understand that I know what dPdx does, I am just arguing that its not explained adequately and that it is different enough from the other functions that it should be put in a different class altogether.

folker
07-02-2002, 01:55 PM
Originally posted by Nakoruru:
I do not think you understand exactly how programming languages work, or you would have caught yourself (or at least looked at the spec for SL).

To do what you describe, dPdx would have to take an EXPRESSION as its argument (which is not what the spec says). Being a function it takes a VALUE, that is what the spec says.

To show you what I mean, what do you think happens when I write this in C:

sqrt(2+2);

Does the function sqrt get some representation of the expression '2+2' or does it get the evaluated expression '4'

You should now see my problem. dPdx cannot take the derivative of an expression it never sees. If it was like you said it would be very special indeed, because the grammar of SL would have to contain some mechanism for passing an expression to a function, and then taking the derivative of it. But it doesn't, so what you propose cannot happen. I cannot just pass in an arbitraury expression, because that expression will get evaluated, and the value will get passed to the dPdx.

But, dPdx cannot do what it says it does with a value, it has to know the register it came from so it can figure out what that registers iterator delta is.

If it did what you said would definitely not be a function, but a special grammatical contruct. I do not think that is true at all.

Everyone should understand that I know what dPdx does, I am just arguing that its not explained adequately and that it is different enough from the other functions that it should be put in a different class altogether.

It is like in function theory (e.g. gradient, divergence etc.) in mathematics and physics:

sqrt(4) is exactly the same as sqrt(2+2),
because 2+2 is evaluated to 4 before sqrt is called. The point is, in case of the shader language, the basic elements are not numbers, but functions all the time.
This means, dPdx(a + b) also means
that a and b are first added, and then dPdx is called for the result. But a and b are functions of x and y, so the functions are added, and the resulting function is passed to dPdx. Also note that constants like 3.14 are only special cases of functions, namely functions which give the same value 3.14 for all x and y.

So dPdx takes an value as argument,
but the value is not only a number, but a function of x and y. dPdx does not take the derivation of an expression but of an value(of course, how the compiler works is a different issue).

It is the same as in linear algebra, for example:
grad(x * x) = 2 * x
sees the operation "*", but only the resulting function = value f(x) = x*x .

If you are used to fields in physics, you are also familiar with such kind of formalism. ;-)

I agree, the specs are too compact and don't explain all that. It may be easier to simply say that dPdx are special functions. But it is not necessary to treat dPdx as special functions if you know the underlaying formalism.

For example, it is perfectly valid and well-defined to define

float myFunction(float a)
{
return dPdx(a * a);
}

float myOtherFunction()
{
return myFunction(gl_MultiTexCoord0);
}

Also here, a is a value which implicitely is a function of x and y (as every quantity in the fragment program).

That you want to apply dPdx to general expressions can be very useful in practise.

Nakoruru
07-02-2002, 02:59 PM
Show me where in the grammar of the SL that dPdx takes an expression.

You must realize that I am speaking in terms of actual language productions. So when I say 'expression' and 'lvalue' I mean the literal definitions of them in the spec, not vague general notions, so you cannot say that the function takes a function as a value because that is not what the spec says. The spec says its a function like any other, and that means that the expression in the parenthesis has been lost before it gets to the part of the compiler that handles function calls. Its no longer a function, but a bit of code that puts the right value into a place that the function call expects it to me. If it was otherwise, it would be in the BNF of the language, because what you are saying requires a different bit of grammer, but its not, so you are absolutely wrong about this.

What you say sounds good, and it may even be possible, but its not that the language does, not by a long shot.

I will look in the grammar, just to make sure, but I guarantee you it isn't there.

mrbill
07-02-2002, 03:16 PM
Originally posted by Nakoruru:
I do not think you understand exactly how programming languages work, or you would have caught yourself (or at least looked at the spec for SL).

To do what you describe, dPdx would have to take an EXPRESSION as its argument (which is not what the spec says). Being a function it takes a VALUE, that is what the spec says.

Actually, the spec says that arguments to functions are passed by reference.

But especially look at page 37 of "The OpenGL Shading Language V1.0, 12-June-2002" [Baldwin, Rost, Kessenich]

See http://www.3dlabs.com/support/developer/ogl2/specs/glslangspecv1.0.pdf

There are functions in the shader langauge that can not be emulated by the user. Textures are one. dPdx, dPdy are others.

Again, take a close look at "The Renderman Companion." [Upstill] My copy is worn and yellowed. On page 314.

Some of the functions above might some problematic to implement. Just how, for example, do Du() and Dv() know the derivative of, not just *varying* variables, but expressions as well?

...

The existence of these functions is a benefit of *SPECIAL-PURPOSE* [emphasis mine] programming language design.

...

These kind of functions work well enough that it is not worth discussing their internals. However, there are small but significant inherent dangers associated with some of the functions to follow. These pitfalls will be highlighted as they arise."

So, bar=dPdx(3.14) *IS* zero. Not might be zero. *IS* zero. bar=dPdx(texcoord0) is absolutely defined. So too is your example bar=dPdx(TexCoord0 + Color * BackColor); or even foo=TexCoord0 + Color * BackColor; bar=dPdx(foo);

But there *ARE* cases where dPdx might be undefined. Here's one from "Advanced Renderman" [Apodaca, Gritz] (Thank you for printing it on acid free paper!)

"In addition, using varying variables in the condition of a loop or conditional is asking for trouble, because this can... produce incorrect results if derivatives are calculated in the body of the loop or conditional..."

So let the magic happen. The shading language is a *SPECIAL-PURPOSE* programming language. Perhaps we have to make it absolutely clear when it might deviate from false expectations.

But, you could also be right. If you could point to specific passage in the spec that you believe is in error, and propose alternate language that you believe is correct, then please do.

-mr. bill

folker
07-02-2002, 03:46 PM
Originally posted by Nakoruru:
Show me where in the grammar of the SL that dPdx takes an expression.

dPdx takes a VALUE, not an expression. But this value is a function of x and y, as every value in a shader program.

You must realize that I am speaking in terms of actual language productions. So when I say 'expression' and 'lvalue' I mean the literal definitions of them in the spec, not vague general notions, so you cannot say that the function takes a function as a value because that is not what the spec. The spec says its a function like any other, and that means that the expression in the parenthesis has been lost before it gets to the part of the compiler that handles function calls. Its no longer a function, but a bit of code that puts the right value into a place that the function call expects it to me. If it was otherwise, it would be in the BNF of the language, because what you are saying requires a different bit of grammer, but its not, so you are absolutely wrong about this.

What you say sounds good, and it may even be possible, but its not that the language does, not by a long shot.

I meant something different: The argument of dPdx is not only a function meaning an expression. I mean that EVERY value in every shader program strictly speaking is a function of x and y. Really EVERY value, also all arguments of user defined functions etc. etc. etc. In the same way as fields in physics are always functions of the location, even if you never write it explicitely.

For example, note that in

float myFunction(float a)
{
return dPdx(a * a);
}

float myOtherFunction()
{
return myFunction(gl_MultiTexCoord0);
}

the function myFunction is a user-defined function. Every value, including a, is depending on x and y. And the above myOtherFunction is equivalent to

float myOtherFunction()
{
return dPdx(gl_MultiTexCoord0 * gl_MultiTexCoord0);
}

Agreed, the above formalism is not explicitely mentioned in the specs. But on the other hand, this formalism is standard in mathematics (and phyics). Especially, it is standard to work with functions in the same way as with numbers and vectors. So "value" may be everything, and a value may depend on x and y, and so a value may be a function.

And the spec also does NOT say that values are NOT depending on x and y (functions on x and y). In contrary, it is obvious that all (most) values depend on x and y, so there is no problem as considering all values always as functions of x and y.

I am absolutely sure that everything is perfectly well-defined, both formally and the meaning.

folker
07-02-2002, 03:49 PM
Originally posted by mrbill:
But, you could also be right. If you could point to specific passage in the spec that you believe is in error, and propose alternate language that you believe is correct, then please do.

Suggestion:
Mention somewhere in the specs that all values in fragment programs are depending on x and y (including varying variables, expressions using varying variables, function results etc. etc), and so all values can be considered as functions of x and y.

Nakoruru
07-02-2002, 06:00 PM
Quoting how renderman works? *sigh*

I almost give up.

First of all, who cares if things are passed by reference or by value? Its ultimately a single object of a single type (a float or a vecX) and there is NO FACILITY IN THE SL for passing in expressions. If there was, that just proves my point that dPdx is a special grammarical feature of the language that needs to be further explained.

What can and cannot be passed to dPdx needs to be explained further.

I am almost completely sure that it does not take the derivative of expressions, if it is, then the spec (the SL spec, NOT RENDERMAN) is completely silent on this and it needs to not be explained (magic or not),

I believe that it simply reads a delta from the iterators associated with the lvalue (assignable expression) you pass it. meaning its limited to lvalues, not arbitruary expressions.

For instances, how would it get the symantic information nessecary to property resolve dPdx(sin(x))? I mean, how does it know that I have not redefined sin to be something completely different?

It doesn't seem right to me to say that everything in the fragment shader is a function of x and y. What if I create a variable float foo, and then take dPdx(foo)? foo is not a function of x and y, its just something I created and can have any value at all with no relationship at all to x or y

I think that dPdx is only a way to get at the internal iterator values. Its a good point that texture also cannot be implemented by the language as well, so that is a good argument that you can also call dPdx a function, but it needs more explaining. That is all I wanted, not a long speculation that goes of into fantasy land about how the hardware is going to take derivatives for me.

I'm getting angry, so I'm going to give up here, and wait for someone to reply to my post over at 3Dlabs.

davepermen
07-02-2002, 10:45 PM
hum no one sais it takes expressions..
it takes values/references and does a numerical differenciation on it.

sort of

float dx(texcoords c,texture_id tex) {
return sample(c,tex) - sample(c+(1,0,0),tex);
}

that takes values, but treats them as a function..

i don't really understand how these two funcs work, but its nothing magic. and they are simple functions you can code yourself in gl2.

folker
07-02-2002, 10:59 PM
Originally posted by Nakoruru:
For instances, how would it get the symantic information nessecary to property resolve dPdx(sin(x))? I mean, how does it know that I have not redefined sin to be something completely different?

In function formalism (e.g. in physics), you can define every normal function like sin, lg etc. also to functions in the obvious way by defining for example sin of a function f is defined as (sin(f))(x) := sin(f(x)). This is standard and used all the time, I was used to work in such way all the time.

Originally posted by Nakoruru:
It doesn't seem right to me to say that everything in the fragment shader is a function of x and y. What if I create a variable float foo, and then take dPdx(foo)? foo is not a function of x and y, its just something I created and can have any value at all with no relationship at all to x or y

A value not depending on x and y is a special case of a function of x and y.

I think that dPdx is only a way to get at the internal iterator values. Its a good point that texture also cannot be implemented by the language as well, so that is a good argument that you can also call dPdx a function, but it needs more explaining. That is all I wanted, not a long speculation that goes of into fantasy land about how the hardware is going to take derivatives for me.

An actual compiler implementation indeed may calculate the derivation by considering the expression and reducing it to dPdx(gl_MultiTexCoord0) etc.. But I suppose most compilers (at least at the beginning) won't support it.

I think there are two aspects:

a) What do we want?

Do we want that dPdx can be applied to any value? My suggestion: Yes. This is very important.

For example, suppose you write a user-defined helper function for distorting texture coordinates, maybe

vec2 myDistort(vec2 t)
{
return vec2(t.x + sin(t.x), t.y + cos(t.y));
}

Then suppose your main function uses such modified texture coordinates:

color = myDistort(texture2(0, glMultiTexCoord0))

Now if you want for example apply antialiasing to your texturing, you need to calculate dPdx from your modified texture coordinates, hence

dPdx(myDistort(texture2(0, glMultiTexCoord0)))

So I think it is very useful and very natural to calculate dPdx from arbitrary values, including results of user-defined functions. (Whether every compilers really supports this is a different iusse, but it would be very useful.)

The second question is:

b) Ok, we want it, but is it possible to define the meaning in an unique, well-defined way?
Answer: Yes. You "simply" realize that anyway all values depend on x and y, so formally everything depends on x and y.

But of course, if one knows how dPdx works, he does not necessarily have to know how exactly it is defined formally and have to go into details we are currently discussing about. But it may be good to know that it can be defined formally precise.

I'm getting angry, so I'm going to give up here, and wait for someone to reply to my post over at 3Dlabs.

;-)
Don't take it too serious... ;-)

folker
07-02-2002, 11:03 PM
Originally posted by davepermen:
hum no one sais it takes expressions..
it takes values/references and does a numerical differenciation on it.

sort of

float dx(texcoords c,texture_id tex) {
return sample(c,tex) - sample(c+(1,0,0),tex);
}

that takes values, but treats them as a function..

i don't really understand how these two funcs work, but its nothing magic. and they are simple functions you can code yourself in gl2.

That's not correct: You cannot code them by yourself since they calculate the gradient with respect to screen coordinates x and y. For example, how do you want to implement dPdx(gl_MultiTexCoord0) ? This is a build-in functionality. dPdx does not apply only to texture lookup, it also applies for example to texture coordinates itself.

Dave Baldwin
07-03-2002, 12:53 AM
mrbill and folker have been doing a sterling job of trying to explain the workings of dpdx and its family .

As the original author of the Shading Language White paper I must apologise if the explanation of this 'function' was not to Nakoruru's satisfaction and we will add some language to clarify things.

Derivatives can be derived in two ways - analytically or numerically. In the analytic case you need an expression while in the numerical case you just need to be able to find the value of the 'thing' you want to differentiate local to the point of interest.

Analytic differentiation is clearly not workable as any of the elements of the expression may not be differentiable - a lookup table (bump mapping, for example) would fall into this category.

For numeric differentiation if you make the assumption that the fragments are being evaluated on a SIMD array and you have access to your neighbour's values then the magic of the dPdx call is to ask their x neighbour's for their value of the 'thing' and subtract yours from it. The dx value is one, by definition, so the result is simple to provide.

At the evaluation of dPdx the 'thing' is just a number so how it was derived is immaterial. The function does accept an expression (as all functions do for their input arguments) and the expression is evaluated to a value. The expression can be anything, even the framebuffer colour value, and the only uncertainty arises if the expression is evaluated as part of a conditional (because the neighbour's values may not be defined).

Should it be a function or operator? There are a limited number of suitable characters in the 7 bit ASCII set and as soon as you start to reuse to ones defined by C the expressions can end up looking very confusing. Using a named function makes the operation more readable and allows the user to redefine it if they want to - operator overloading is not a supported language feature so if done through an operator user couldn't redefine the meaning.

We make a point in the white paper of saying that the built in functions are provided for convenience and to access hardware specific features. The convenience functions can easily be emulated by the user but the hardware ones may be impossible or just very difficult.

dPdx is in the impossible category to do a general solution, but it is possible to do a specific solution if you know what the analytic solution is or you can manually calculate the local differences. For you to calculate the local differences you will have to know the complete expression tree, maybe starting 5 subroutine levels back for example, so you can substitute x+1 into it. The expression tree may also use data you don't have access to, such as the framebuffer colour value of your neighbour.

Nakoruru
07-03-2002, 05:03 AM
I did have a long write up and how I finally get it and am sorry that I got carried away. But my browser hiccuped and I lost it. I think I'll start writing my posts in Notepad from now on ^_^

Nakoruru
07-03-2002, 06:04 AM
My misunderstanding 'derived' from the fact that I was imagining that dPdx worked by returning the delta of variables that are varying. In otherwords, it knew how the inputs to the fragment shader varied. That would mean that in addition to, say gl_TexCoord0, there is also somewhere a gl_TexCoord0_dx and a gl_TexCoord_dy. So, one theory of mine was that dPdx 'dereferenced' gl_TexCoord0 into gl_TexCoord0_dx. In that case, the function could only take lvalues (values associated with a storage location). I figured that since there was no mechanism in the grammar for understanding anything other than lvalues that it would not be able to take expressions.

If that mechanism did exist then it could do numerical derivation by rewriting any expression:

dPdx(f(gl_x)) = f(gl_x + gl_x_dv) - f(gl_x)

where gl_x is a varying,
f is some expression involving gl_x,
and gl_x_dv is the delta that I imagined

Its possible, but I didn't see where it could be done (there was no grammar to support it), so I refused to believe that was what was going on without proof (and good thing too, because that is not whats going on).

Dave Baldwin gave me the piece of the puzzle that makes it all come together. Apparently, dPdx has access to the results of the expression that 'will be' passed into its neighbor (it knows the future because its all being done in parallel). If it knows the result of the expression with the next value plugged into it, then all it has to do is return the difference.

I knew it had to be something that simple, because its something that has to be done a couple of billion times a second.

The main reason I ask what would happen with sin(x) (especially if I redefined it to be very un-sin-like) is because I was rejecting the notion that an analytical derivation was taking place. I was under the impression that Folker believed that, because he kept talking about things that only make sense with analysis (maybe I misunderstood). Thats what I refered to as fantasy. Maybe he is just trying to give me a calculus lesson ^_^

But, now I know what would happen if I gave it an arbitrary function. It would just work, because it has the parallel results of whatever function I write.

So, now I have new questions. Does this mean that an implementation would always have to run the fragment shader for one extra fragment in the x direction and one extra fragment in the y direction? meaning that if I draw a single point, that it actually has to calculate 4 pixels?

Next, what happens if I try to take the second derivative? dPdx(dPdx(blah))? Seems to me it would require you to calculate two fragments ahead to be correct.

I have a feeling the answers to these questions are related, and they reflect the fact that while what I explain above may be basically true, that its not the whole story about how this works.

Here is an example of how I could do something like what I now believe is going on using a C++ functor:

struct D {
float prev;

D(float first) : prev(first) {}

float operator () (float expression_result)
{
float rv = expression_result - prev;
prev = expression_result;
return rv;
}

};

You can see that with this class, I can call an instance of D like a function and it will remember the last value I passed to it and return the difference. For example:

float x = 0;
D Dsinx(x);

for (x = 0 + SMALL_DELTA; x < PI2; x += SMALL_DELTA)
{
mysin = sin(x);
mycos = Dsinx(mysin); // derivative of sine is cosine
}

Dave Baldwin
07-03-2002, 06:19 AM
Originally posted by Nakoruru:
So, now I have new questions. Does this mean that an implementation would always have to run the fragment shader for one extra fragment in the x direction and one extra fragment in the y direction? meaning that if I draw a single point, that it actually has to calculate 4 pixels?

[/QUOTE]

That would be one way to do this, although it really is an implementation detail.

Originally posted by Nakoruru:

Next, what happens if I try to take the second derivative? dPdx(dPdx(blah))? Seems to me it would require you to calculate two fragments ahead to be correct.
[/QUOTE]

You can next the dPdx as many times as you like and it will all hang together. The inner most dPdx is evaluated as already discussed for all the neighbouring fragments. This gets fed into the next evaluation as a value - recall dPdx doesn't know or care about where a value came from - and the process repeated.

Dave.
3Dlabs.

Nakoruru
07-03-2002, 06:47 AM
Thanks.

My main concern about second (and third) derivatives was that they would not be accurate until a few iterations in. Meaning that the left and top edge of primitives would be wrong. (assuming that things are rasterized from left to right, top to bottom...).

That makes me thing of something else. In RenderMan these derivatives are in the U and V directions of the primative, meaning that they are relative to the surface.

I assume that dPdx and dPdy are relative to the framebuffer, this means that they change relative to the surface when the surface is rotated. Doesn't this introduce some amount of variance under rotation? Would this be something to keep in mind as one translates RenderMan shaders to OpenGL SL?

folker
07-03-2002, 12:37 PM
Originally posted by Nakoruru:
Dave Baldwin gave me the piece of the puzzle that makes it all come together. Apparently, dPdx has access to the results of the expression that 'will be' passed into its neighbor (it knows the future because its all being done in parallel). If it knows the result of the expression with the next value plugged into it, then all it has to do is return the difference

Dave gave an explanation how dPdx can be implemented. My aim was different, I want to explain why the language is well-defined formally and mathematically.

Note that your questions about how dPdx functions are exactly defined formally is valid and very important. The first question is if there is a senseful behaviour and implementation of dPdx, for example also including expressions. This is answered by Dave. The second question is if you can define the language formally consisten, which is not possible by considering values simply as numbers as you correctly pointed out. And indeed it maybe that even if the meaning is plausible, a particular formal definition is not working out. I wanted to try to explain such a well-defined definition.

BTW, Daves implementation explanation and my formalism explanation expresses the same: Daves explanation means that in a shader program, a value is not only a single number but also the neighboring values are relevant. So a value is a collection of multiple numbers of neighboring fragments. Formally this means exactly that a value is a function of the (neighboring) fragment coordinates x and y.

folker
07-03-2002, 12:51 PM
Originally posted by Dave Baldwin:
mrbill and folker have been doing a sterling job of trying to explain the workings of dpdx and its family .

As the original author of the Shading Language White paper I must apologise if the explanation of this 'function' was not to Nakoruru's satisfaction and we will add some language to clarify things.

Derivatives can be derived in two ways - analytically or numerically. In the analytic case you need an expression while in the numerical case you just need to be able to find the value of the 'thing' you want to differentiate local to the point of interest.

Analytic differentiation is clearly not workable as any of the elements of the expression may not be differentiable - a lookup table (bump mapping, for example) would fall into this category.

For numeric differentiation if you make the assumption that the fragments are being evaluated on a SIMD array and you have access to your neighbour's values then the magic of the dPdx call is to ask their x neighbour's for their value of the 'thing' and subtract yours from it. The dx value is one, by definition, so the result is simple to provide.

At the evaluation of dPdx the 'thing' is just a number so how it was derived is immaterial. The function does accept an expression (as all functions do for their input arguments) and the expression is evaluated to a value. The expression can be anything, even the framebuffer colour value, and the only uncertainty arises if the expression is evaluated as part of a conditional (because the neighbour's values may not be defined).

Should it be a function or operator? There are a limited number of suitable characters in the 7 bit ASCII set and as soon as you start to reuse to ones defined by C the expressions can end up looking very confusing. Using a named function makes the operation more readable and allows the user to redefine it if they want to - operator overloading is not a supported language feature so if done through an operator user couldn't redefine the meaning.

We make a point in the white paper of saying that the built in functions are provided for convenience and to access hardware specific features. The convenience functions can easily be emulated by the user but the hardware ones may be impossible or just very difficult.

dPdx is in the impossible category to do a general solution, but it is possible to do a specific solution if you know what the analytic solution is or you can manually calculate the local differences. For you to calculate the local differences you will have to know the complete expression tree, maybe starting 5 subroutine levels back for example, so you can substitute x+1 into it. The expression tree may also use data you don't have access to, such as the framebuffer colour value of your neighbour.

Dave, one comment:
You explained possible implementations of dPdx. But Nakoruru point remains: How is dPdx is defined formally in the language specification? You cannot simply consider dPdx as function of atomic float values like 3.14 as pointed out correctly by Nakoruru.

So indeed it may be a good idea to solve this formal gap in the OpenGL 2.0 shader language spec. According to my explanations in previous posts, I would suggest the following:

a) Add a statement that formally all values are not only simple values (e.g. conventional floats), but functions of the fragment positions x and y. All usual arithmetic which works for plain values formally can be easily applied to values depending on x and y, too.

A note may clarify that this definition has formal reasons. The typical reader may still consider values as simple values, e.g. simple floats like 3.14. Unless he goes more in formal detail as Nakoruru... ;-)

b) For control statements like "if(value)", the value is collaped to a simple value by taking the simple value at the current fragment x and y.

This second statement is necessary to make control statements well-defined.

Note that the purpose of these additions to the specs is NOT to define the meaning or implementation of dPdx, but make the formal definition of the language precise and well-defined.

Nakoruru
07-03-2002, 06:32 PM
Folker,

I'm am now completely satisfied that dPdx is a function, with no syntactical specialness. It really does take the result of an expression as its argument, and it is able to return the delta because it knows what the value will passed to its neighbor in the next iteration.

Look at my example code where I show how you can calculate the derivative of sin(x) just because you remember the result of sin(x - epsilon).

The explaination that this function has special access to its 'neighbors' due to the nature of SIMD hardware and can thus know how much it differs from them is enough.

Even that is an implementation detail.

Nakoruru
07-03-2002, 06:44 PM
For some reason I cannot edit my posts (tells me the password is invalid).

Anyway, just wanted to add that I do believe that the grammar is correct, because dPdx can be implemented by passing a value. There is nothing which prevents functions from having access to special information (at least, not in computer science, I think that Calculus might object to 'magic' ^_^).

I find it exceedingly strange that you would now insist that I should not be satisfied after I have my answers *LOL*.

All it really has to say is that it returns the difference between the value passed to it for the fragment at position x, and the value passed to it for the fragment at position x+1. In fact, if it said that I would have been completely satisfied and never would have had any doubt in my mind as to its usefulness and behavior.

Some warnings about conditionals, and _maybe_ some explanation about what happens if those x+1 fragments get killed by per fragment tests or the edge of the primitive and we're golden ^_^

folker
07-03-2002, 08:46 PM
Originally posted by Nakoruru:
For some reason I cannot edit my posts (tells me the password is invalid).

Anyway, just wanted to add that I do believe that the grammar is correct, because dPdx can be implemented by passing a value. There is nothing which prevents functions from having access to special information (at least, not in computer science, I think that Calculus might object to 'magic' ^_^).

I find it exceedingly strange that you would now insist that I should not be satisfied after I have my answers *LOL*.

All it really has to say is that it returns the difference between the value passed to it for the fragment at position x, and the value passed to it for the fragment at position x+1. In fact, if it said that I would have been completely satisfied and never would have had any doubt in my mind as to its usefulness and behavior.

Some warnings about conditionals, and _maybe_ some explanation about what happens if those x+1 fragments get killed by per fragment tests or the edge of the primitive and we're golden ^_^

Talking about neighboring values exactly means that formally each value depends also on x and y (x and y are used to address the value), so we indeed agree completely. BTW, as soon as you define this precisely (e.g. as in my previous post), conditionals, kills, the question of border pixels etc. are also well-defined.

Maybe my posts look very formal and pedantic and that the sound like a lession of algebra ;-), but I think that is important to not stop with "it is plausible and will work somehow", but to make specifications precise.