Archive for General

Derivative Map Artifacts

I had been suffering from some strange artifacts on the edges of my objects when using derivative maps. After much time spent in GPU Perf Studio, I finally realised that my mipmap generation was not correct. It was introducing one extra column of garbage at every level.

My use of FXAA and anisotropic filtering was just making the problem more evident. I would recommend using regular trilinear filtering for derivative maps anyway.

So, mea culpa and all that. Let the name of Morten Mikkelsen and derivative maps be cleared!

Comments

Derivative Maps vs Normal Maps

This post is a quick follow up to my previous post on derivative maps. This time I’m going to compare the quality and performance of derivative maps with the currently accepted technique, normal maps.

I’ll be using the precomputed derivative maps for comparison since the ddx/ddy technique just isn’t acceptable in terms of quality.

Quality

Here’s the close up shot of the sphere with the moon texture again. This shows the derivative map implementation, and if you mouse over, you’ll see the normal map version.

There are some slight differences because the height of the derivative map doesn’t quite match the heights used to precompute the normal map, but overall I would say that they are remarkably similar. It looks to me that the normal map is preserving more of the detail though.

Here’s a high-contrast checkerboard, again with the normal map shown if you mouse over.

I’m no artist, but I would say the the derivative map results are close enough to the normal maps to call the technique viable from a quality standpoint.

EDIT: I had some issues with artifacts which I posted here. It turns out they were (embarrassingly) caused by my mipmap generation which was introducing a row of garbage at each level. Combined with FXAA and anisotropic filtering, this caused the weird vertical stripes I posted before.

I’ve removed the images since I don’t want to give the wrong impression of the quality of derivative maps.

Performance

I ran these tests on my Macbook Pro which has an AMD 6750M. The shader in question is a simple shader for filling out the gbuffer render targets. All shaders were compiled using shader model 5. I took the frame times from the Fraps frame counter and the other numbers came from Gpu Perf Studio.

For comparison, I’ve included an implementation with no normal perturbation at all.

Perturbation Frame Pixels Tex Inst Tex Busy ALU Inst ALU Busy ALU/Tex
None 1.08 ms 262144 3 27.5 % 14 32.1 % 4.667
Normal map 1.37 ms 262144 4 36.5 % 23 52.4 % 5.75
Derivative map 1.36 ms 262144 9 82.0 % 28 63.8 % 3.11

Despite the extra shader instructions, the derivative map method is basically as fast as normal maps on my hardware. As Mikkelsen predicted, it seems like having one fewer vertex attribute interpolator offsets the cost of the extra ALU instructions.

Note that the derivative map shader has nine texture instructions compared to just four for the normal maps. The extra five instructions are the two sets of ddx/ddy instructions, and the instruction to get the texture dimensions. The pixel shader can issue one texture instruction and one ALU instruction on the same cycle, these are essentially free.

The only performance overhead which has any impact for derivative maps are the five extra ALU instructions.

Memory

As I mentioned in my previous post, derivative maps also have the tremendous benefit of not requiring tangent vectors. In my case, with a simple vertex containing position, normal, tangent and one set of texcoords, the tangent takes up 27% of the mesh space.

Given that most games these days have tens of megabytes of mesh data, this would turn into some pretty decent memory savings. There’s also a minor benefit on the tool-side to not having to spend time generating face tangents and merging them into the vertices.

Conclusion

Well, for me it’s pretty clear. On my setup, derivative maps have a similar quality with the same performance but less memory. This makes them a win in my book. Of course, these numbers will vary wildly based on the API and hardware, so this can’t be taken as a blanket ‘derivative maps are better than normal maps’ statement, but they look promising. Good job Morten Mikkelsen!

I would love to see a similar comparison for the current generation of console hardware (hint, hint!).

If you have DirectX 11, then you should be able to run the demo here.

Comments (21)

Cubemap Texel Solid Angle

Warning: This post is going to be pretty math heavy. If you suck at math, then go and read this first, then come back. If you still think that you suck, then I suggest going to Khan Academy and start watching videos. You won’t regret it!

I was reading through the AMD CubeMapGen source last week and came across the code for calculating the solid angle of a cube map texel. This code piqued my interest, since it seemed very terse for what I thought would be a horrific calculation.

static float32 AreaElement( float32 x, float32 y )
{
	return atan2(x * y, sqrt(x * x + y * y + 1));
}

float32 TexelCoordSolidAngle(int32 a_FaceIdx, float32 a_U, float32 a_V, int32 a_Size)
{
   //scale up to [-1, 1] range (inclusive), offset by 0.5 to point to texel center.
   float32 U = (2.0f * ((float32)a_U + 0.5f) / (float32)a_Size ) - 1.0f;
   float32 V = (2.0f * ((float32)a_V + 0.5f) / (float32)a_Size ) - 1.0f;

   float32 InvResolution = 1.0f / a_Size;

	// U and V are the -1..1 texture coordinate on the current face.
	// Get projected area for this texel
	float32 x0 = U - InvResolution;
	float32 y0 = V - InvResolution;
	float32 x1 = U + InvResolution;
	float32 y1 = V + InvResolution;
	float32 SolidAngle = AreaElement(x0, y0) - AreaElement(x0, y1) - AreaElement(x1, y0) + AreaElement(x1, y1);

	return SolidAngle;
}

The source code for this particular part is well documented, and points you towards this thesis by Manne Öhrström (@manneohrstrom) where he gives a high level overview of the derivation. I was interested in finding out some more of the details, so I had a go myself, and this post is the result.

Why is it Useful?

When processing cube maps (for example, generating a diffuse irradiance map, or spherical harmonic approximation), you need to be able to integrate the texel values over a sphere.

One way of approximating this integral is to use a Monte Carlo estimator. This is a statistical technique that may oversample some texels and undersample other. This seems a bit wasteful considering that we have a finite number of input values. Ideally we’d like to use each texel value just once.

A naive approach to analytical integration where each texel has the same weight would result in overly bright values in the corner areas. This is because the texels in the corners project to smaller and smaller areas on the sphere. The correct approach is to factor in the solid angle during the integral, and this is what CubeMapGen does.

The Plan

Imagine a single cube map face placed at (0,0,1) and scaled such that the texel locations are all in [-1,1]. For any texel in this cube map, we want to project it onto a unit sphere sitting at the origin, then work out the area on the sphere. This area corresponds to the solid angle because the sphere is a unit sphere.

We can repeat this same calculation for any of the other cube map faces by first transforming them into the same range.

This is the high-level game plan for calculating out the solid angle:

  1. Determine a formula for projecting a position from texture-space onto the sphere.
  2. Work out how this projected position changes as the texture-space coordinates change in x and y.
  3. Imagining that these position change vectors define two sides of a microscopic quadrilateral, then calculate the microscopic area of this quad using the magnitude of the cross product.
  4. Integrate the microscopic area using the corner coordinates of a texel to calculate its area on the sphere, and solid angle.

The Details

We start off with the formula for projecting a point from its location on the texture face (x, y, 1) onto the unit sphere. This is just a standard vector normalization.

    \begin{align*} \vec{p} = \dfrac{\begin{pmatrix}x, y, 1 \end{pmatrix}}{\sqrt{x^2 + y^2+1}} \end{align*}

Note: I’ll be switching back and forth between negative and fractional exponents as I see fit. This makes things easier. Remember, x^{-n} = \frac{1}{x^n}, and x^{\frac{1}{2}} = \sqrt{x}.

We want to calculate how this projected point changes as the texture-space x and y coordinates change. We can do this separately for each axis using partial derivatives. First we’ll start by calculating how the projected z component changes along to the texture-space x axis.

Projected Z Change According to X

The z-component of p is simply:

    \begin{align*} p_z &= \dfrac{1}{\sqrt{x^2 + y^2+1}}\\ &= (x^2 + y^2+1)^{-\frac{1}{2}} \end{align*}

We need to differentiate this equation with respect to x. Because of the exponent, we need to use the chain rule to do this. The chain rule is a method for finding the derivative of the composition of two functions. First, we can reformulate the equation a little bit to make the two functions a bit clearer:

    \begin{align*} p_z = u^{-\frac{1}{2}}, u = x^2 + y^2+1 \end{align*}

In our case our first function is a function of u and our second function is a function of x and y. Given this, the chain rule says:

    \begin{align*} \frac{\partial p_z}{\partial x} = \frac{\partial p_z}{\partial u} \frac{\partial u}{\partial x} \end{align*}

We can apply this rule very easily to our reformulated functions:

    \begin{align*} \frac{\partial p_z}{\partial u} &=-\frac{u^{-\frac{3}{2}}}{2}\\ &=-\frac{1}{2(x^2+y^2+1)^{\frac{3}{2}}} \\[10] \frac{\partial u}{\partial x}&=2x\\[10] \frac{\partial p_z}{\partial x} &= -\frac{x}{(x^2+y^2+1)^{\frac{3}{2}}} \end{align*}

This equation tells us exactly how the z component of the projected point changes as the texture-space position moves along the x axis.

Projected X Change According to X

Now we’ve found the projected z component derivative, it’s going to make finding the x and y components a little easier. Why? Because we can express the x and y components in terms of the z component.

    \begin{align*} p_x &= \frac{x}{\sqrt{x^2 + y^2+1}}\\ &= x p_z \end{align*}

We don’t have the same ‘composition of functions’ setup that we did last time, so we can’t use the chain rule to differentiate this. Instead, we can use the product rule. The product rule in our case says:

    \begin{align*} \frac{\partial p_x}{\partial x} &= p_z\frac{\partial (x)}{\partial x} + x\frac{\partial (p_z)}{\partial x} \end{align*}

Applying this to equation for for the projected x component:

    \begin{align*} \frac{\partial p_x}{\partial x} &= \frac{1}{(x^2 + y^2+1)^{\frac{1}{2}}}-\frac{x^2}{(x^2+y^2+1)^{\frac{3}{2}}}\\ &= \frac{y^2+1}{(x^2+y^2+1)^{\frac{3}{2}}} \end{align*}

Projected Y Change According to X

We have a very similar derivation for the projected y derivative:

    \begin{align*} p_y = \frac{y}{\sqrt{x^2 + y^2+1}} = y p_z \end{align*}

We can use the product rule again:

    \begin{align*} \frac{\partial p_y}{\partial x} &= p_z\frac{\partial (y)}{\partial x} + y\frac{\partial (p_z)}{\partial x}\\ &= -\frac{xy}{(x^2+y^2+1)^{\frac{3}{2}}} \end{align*}

Projected Position Change According to X and Y

Putting this all together, we have our equation showing how the projected position changes as the texture-space position changes in the x direction. We can use the exact same process to work out how it moves in the y direction.

    \begin{align*} \frac{\partial \vec{p}}{\partial x} &= \frac{\begin{pmatrix}y^2+1,-xy, -x\end{pmatrix}}{(x^2+y^2+1)^{\frac{3}{2}}}\\ \frac{\partial \vec{p}}{\partial y} &= \frac{\begin{pmatrix}-xy, x^2+1,-y\end{pmatrix}}{(x^2+y^2+1)^{\frac{3}{2}}} \end{align*}

Differential Area

The next step is to calculate the differential (microscopic) area of the projected point using the partial derivatives we just calculated. Clearly at a normal scale, we wouldn’t be able to take the cross product of two projected vectors on a sphere and expect the magnitude to be the area they define on the sphere. But at this differential scale, we can treat the surface as if it is flat, so this works.

The first thing we need to do is to calculate the cross product of the partial derivatives.

    \begin{align*} \vec{r} &= \frac{\partial \vec{p}}{\partial x} \times \frac{\partial \vec{p}}{\partial y}\\ &= \frac{\begin{pmatrix}y^2+1,-xy, -x\end{pmatrix}}{(x^2 + y^2+1)^{\frac{3}{2}}} \times \frac{\begin{pmatrix}-xy, x^2+1,-y\end{pmatrix}}{(x^2 + y^2+1)^{\frac{3}{2}}} \end{align*}

Calculating the cross product for each of the components relatively straightforward.

    \begin{align*} r_x &= \frac{x^3+xy^2+x}{(x^2 + y^2+1)^4}\\ r_y &= \frac{x^2y+y^3+y}{(x^2 + y^2+1)^4}\\ r_z &= \frac{(y^2+1)(x^2+1)-x^2y^2}{(x^2 + y^2+1)^4}\\ \end{align*}

If you’re reading carefully, you’ll notice that each component has a factor of x^2+y^2+1 on the top and the bottom, so we can divide through. Combining all the components back together again, we arrive at the final equation for the perpendicular vector.

    \begin{align*} \vec{r} &= \frac{(x,y,1)}{(x^2 + y^2+1)^2} \end{align*}

Now we simply need to take the length of the result of the cross product to find the differential area on the sphere.

    \begin{align*} \partial A &= \sqrt{\vec{r} \cdot \vec{r}}\\[5] &= \sqrt{\frac{x^2+y^2+1}{(x^2 + y^2+1)^4}}\\[5] &= \frac{1}{(x^2 + y^2+1)^{\frac{3}{2}}} \end{align*}

Solid Angle

The final step is to integrate the differential area over our range of texture-space values to get the solid angle of the texel. We can start by calculating the integral between (0,0) and some point (s,t) on the cube map face.

    \begin{align*} f(s,t)&=\int_{y=0}^t\int_{x=0}^s \frac{1}{(x^2+y^2+1)^{\frac{3}{2}}} \,\mathrm{d}x\, \mathrm{d}y\\ &=\mathrm{tan}^{-1}\frac{st}{\sqrt{s^2+t^2+1}} \end{align*}

From this formula, we can calculate the area of any texel in the cube map face by adding together the two right-diagonal corners, A and C, and subtracting the left-diagonal corners, B and D.

    \begin{align*} S=f(A) - f(B) + f(C) - f(D) \end{align*}

You can see on the image below that the added areas in green are canceled out by the subtracted areas in red.

That should look familiar, since that’s exactly what the CubeMapGen code does. If you look at the surrounding source code to TexelCoordSolidAngle, then you’ll notice that there’s another method mentioned for calculating the solid angle of a texel. This method is based on Girard’s theorem, which describes how to calculate the area of a spherical triangle based on the excess of the sum of its interior angles. This method was also suggested to me on Twitter by Ignacio Castaño (@castano). I haven’t actually tried it, but it looks fascinating!

Is it Correct?

It’s always a bit daunting to get to the end of a derivation like this, and not know if the answer is correct or not. In this case, it’s pretty easy to verify if this result is correct.

Remember that the texture-space coordinates are in range [-1,1]. If we set our (x,y) values to 1, that corresponds to the top right quarter of the cube map face. We know that there are 4\pi steradians in a sphere, so that means that each face gets \frac{2\pi}{3} steradians. Since we’re only calculating for a quarter of a face we expect our result to be \frac{\pi}{6}.

    \begin{align*} f(1,1)&=\mathrm{tan}^{-1}\frac{1}{\sqrt{3}}\\ &= \frac{\pi}{6} \end{align*}

And it does. Thanks to the various people on twitter (@SebLagarde, @mattpharr, @manneohrstrom, @castano and @ChristerEricson) for engaging me in conversation over this.

Please let me know in the comments if you spot an error in this post, or if anything needs to be explained better or more easily.

Comments (13)

UI Anti-Aliasing

I’ve been working on making a really simple IMGUI implementation for my engine at home. I like to do a little bit of research when I’m approaching something new to me like this, so I went hunting around for publicly available implementations. While doing this, I came across Mikko Mononen’s implementation in Recast.

I was impressed when I ran the demo with how smooth his UI looked. It turns out that he’s using a little trick (which I’d never seen before, but I’m sure is old to many) to smooth of the edges of his UI elements.

Basically, the trick is to create a ring of extra vertices by extruding the edges of the polygon out by a certain amount. These extra vertices take the same color as the originals, but their alpha is set to zero. Mikko calls this ‘feathering’.

In my case, I found that I got good results by feathering just one pixel. Here’s a quick before/after comparison of the my IMGUI check box at 800% zoom:

And here’s a 1-to-1 example showing rounded button corners:

It’s a pretty nice improvement for a very simple technique! If you’re interested in what the code looks like, then either take a look at Mikko’s IMGUI implementation, or you can find the code I use to feather my convex polygons below.

My implementation is a little less efficient since I recalculate each edge normal twice, but I chose to keep it simple for readability.

void CalculateEdgeNormal(float& nx, float& ny, float x0, float y0, float x1, float y1)
{
const float x01 = x1 - x0;
const float y01 = y1 - y0;

const float length = Sqrt(x01 * x01 + y01 * y01);

const float dx = x01 / length;
const float dy = y01 / length;

nx = dy;
ny = -dx;
}

void FeatherConvexPolygon(Primitives& primitives, const Vertex* vertices, int count, float amount, const Texture* texture)
{
Vertex* extruded = Memory::Allocate<Vertex>(Memory::Temp, sizeof(Vertex) * count);

for (int i = 0; i < count; ++i)
{
const Vertex& previous = vertices[(i + count - 1) % count];
const Vertex& current = vertices[i];
const Vertex& next = vertices[(i + 1) % count];

float nx0, ny0, nx1, ny1;

CalculateEdgeNormal(nx0, ny0, previous.x, previous.y, current.x, current.y);
CalculateEdgeNormal(nx1, ny1, current.x, current.y, next.x, next.y);

float nx = (nx0 + nx1) * 0.5f;
float ny = (ny0 + ny1) * 0.5f;

extruded[i] = Vertex(current.x + nx * amount, current.y + ny * amount, Color(current.r, current.g, current.b, 0.0f));
}

for (int i = 0; i < count; ++i)
{
const int j = (i + 1) % count;
AddQuad(primitives, vertices[i], extruded[i], extruded[j], vertices[j], texture);
}

Memory::Free(extruded);
}
view raw FeatherUI.cpp This Gist brought to you by GitHub.

Comments

MockItNow: Now with Win64 goodness!

A couple of fine chaps named Julian Adams and Clement Dagneau from my previous company Black Rock Studios (née Climax Racing) took it upon themselves to tackle the daunting task of  porting MockItNow to x64 (MSVC).

They’ve kindly shared their efforts back with the main repository in google code. Feel free to profit from their hard work!

Thanks a lot Julian & Clement!

Comments

Another MockItNow Update

I fixed a problem with the EXPECT_THROW macro (I had uploaded the wrong file). Also, I added the ability to stub out results for function arguments passed by pointer. I would recommend downloading the zip file to take a look since there were a few files that had to change this time. I added one new example to show the new functionality.

Comments

Books

A couple of days ago, I stacked the random assortment of books I had lying on my desk at work, and for some reason I decided to take a photo. It’s a bit of a mixed bag of good and not-so-good books.

books

Every now and then I know I’m in trouble because I have to whip out the yellow maths book to remind myself of something that I have forgotten. I enjoy the challenge of reading the quaternions book, but it’s pretty hard to motivate myself to really get into that one.

My favourite at the moment is probably Real-Time Rendering (the latest edition which I have at home to be more specific), and my least favourite is probably Clean Code.

Clean Code seems to take the good ideas from other books (like C++ Coding Standards), and then adds some more which don’t make as much sense (I can’t think of a specific example off-hand). The customer reviews on Amazon.com are pretty good though. I definitely had an ‘uh-oh’ moment when I read the buzzword-laden full title – Clean Code: A Handbook Of Agile Software Craftmanship.

Comments (2)

Nothing To Report

Well, there’s not much to report really. I’ve been spreading myself very thin recently, investigating a few different things, so I thought might be nice to post a status update.

Variance Shadow Maps

Yes, I’m jumping on the variance shadow maps boat. They look nice, they’re easy to implement, and have relatively small overhead over traditional shadow maps. I read the article in GPU Gems 3, which is also available here, and it was pretty easy to implement from there. It probably took me about 2 hours to go from no shadows at all to variance shadow maps. After a bit of tomfoolery to get rid of the light-leaking I ended up with the image below. I haven’t implemented any bias yet, so there are some errors there, but the VSMs appear to be working very nicely.

Read the rest of this entry »

Comments (3)