This topic is 2931 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

##### Share on other sites
Managed to figure this out myself.

The multiply-by-half-the-shadow-map-size at step (2) is to go from projected homogenous light space coords to shadow texture space.

What the ShaderX6 article doesn't make explicit is that at step (3), the origin_rounded - origin_shadow offset must be expressed in homogenous light space.

Just on the off-chance I'm not just talking to myself, here's how I calculate the 'rounding' matrix (DirextX):

// xShadowMatrix is the light view projection matrix

// Find nearest shadow map texel. The 0.5f is because x,y are in the
// range -1 .. 1 and we need them in the range 0 .. 1

// Round to the nearest 'whole' texel
float texCoordRoundedX = round(texCoordX);
float texCoordRoundedY = round(texCoordY);

// The difference between the rounded and actual tex coordinate is the
// amount by which we need to translate the shadow matrix in order to
// cancel sub-texel movement
float dx = texCoordRoundedX - texCoordX;
float dy = texCoordRoundedY - texCoordY;

// Transform dx, dy back to homogenous light space

D3DXMATRIX xRounding;
D3DXMatrixTranslation(&xRounding, dx, dy, 0);

##### Share on other sites
Thank you for posting your solution...I was planning on tackling this particular problem at some point in the near future and it's nice to have a reference. [smile]

##### Share on other sites
Thanks for sharing this.

There will be a ShaderX7 article in which I will describe a slight improvement to Michal's approach. Michal picks the right shadow map with a rather cool trick. Mine is a bit different but it might be more efficient. So what I do to pick the right map is send down the sphere that is constructed for the light view frustum. I then check if the pixel is in the sphere. If it is I pick that shadow map, if it isn't I go to the next sphere. I also early out if it is not in a sphere by returning white.
At first sight it does not look like a trick but if you think about the spheres lined up along the view frustum and the way they intersect, it is actually pretty efficient and fast.
On my target platforms, especially on the one that Michal likes a lot, this makes a difference.

##### Share on other sites
Hi guys,

Thanks for posting this - I guess this proves I was not clear enough in the article.

cheers,
Michal

##### Share on other sites

The ShaderX6 article is probably fine - I wouldn't read too much into my amateurish fumblings (Michal).

I look forward to your ShaderX7 article (Wolf). Being new to 3D graphics (though an experienced programmer) I've been less interested in rendering efficiency so far and more interested in achieving a decent looking result. I figure by the time I actually have something 'finished' the hardware will make any optimisations I attempt today redundant anyway.

Well... that's how I console myself when I fail to understand the ShaderX articles anyway ;-)

##### Share on other sites
Hi,

Maybe someone could explain how in the Shaderx6 article right shadow map is picked? As I don't have this book.

How does the light position and direction quantization work with this method? Does it produce nice results?

And the last one, maybe someone knows how to quantize the light direction?

##### Share on other sites
I haven't implemented the ShaderX6 technique for picking the right shadow map (I've plumped for a straight-forward depth check), but conceptually it starts by noticing that the n'th shadow matrix is identical to the shadow matrix at the first cascade, only shifted and scaled. Then determine the xyz shift and scale required to compute the 2nd, 3rd and 4th cascades, and pass these to the shader. In the shader perform some cunning masking and component-wise multiplication of these values with the depths of the splits to select the correct shift and scale to apply for the current screen pixel. Can you tell I don't really understand it yet?

I don't follow what you mean about quantizing the light direction... I'm using a directional light source (orthographic projection) so there's no quantization to worry about. All I do is render 4 separate shadow maps (one for each cascade), combine them into a single screen-space shadow coefficient map, then pass this map to the lighting stage. All very simple, robust and hideously inefficient.

The quality of the result mostly boils down to the quality at each cascade (and you can plug-in just about any shadow mapping algorithm you want). And after all... don't many leading current games use the technique?

##### Share on other sites
Revived by request.

##### Share on other sites
I have two questions regared to topic:

first:
Quote:
 // Find nearest shadow map texel. The 0.5f is because x,y are in the// range -1 .. 1 and we need them in the range 0 .. 1float texCoordX = ptOriginShadow.x * SHADOW_MAP_SIZE * 0.5f;float texCoordY = ptOriginShadow.y * SHADOW_MAP_SIZE * 0.5f;

After mul by 0.5, the coords are in range -0.5 and 0.5, not 0 and 1. Yet it works. So I suppose that we actually don't need the have values in range 0..1 but it is about having range *spanned* across the "length" of 1.0, because shadow map tex coords are spanned across such a "length". Correct me if I'm wrong.

second:
I also have some really small problem with stability. Indvidual texels of my shadow map just flicker. It's really few of them (literally - "individual") but I'm a perfectionist and it annoys me :). I suppose it's due to floats' stability, isn't it?

I've just noticed another problem. Points that are farther from the origin, with greater shadow map size, flicker much more. With shadow map's size 4096 and at some small distance from the origin, almost all of the texels flicker. If I change the "input point" and choose not the origin but some point near the camera it's all fine. So I have to update this starting point from time to time and it causes some visible translations of shadow map texels.
How to get around that problem? Use doubles?

[Edited by - Maxest on May 24, 2009 7:02:15 AM]

##### Share on other sites
first figure out where the problem is... float values inherently have issues when using large integer + small fraction... further look towards using more temporaries and ensuring the math you do maintains as much accuracy as possible.

double will likely help you in this area but it would be best to figure out the issue before going there.

##### Share on other sites
It must be some numerical problem. I've just rescaled my world down by 0.1 in each axis and flickering is much smaller farther from the camera. But now "farther" means not 300 units but 30 units from the origin. Of course I cannot continue to scale down cause small values causes other problems :)
So right now my code looks like this:
static CVector3 focusPoint = eye;if (getDistanceBetweenPoints(focusPoint, eye) > 100.0f)	focusPoint = eye;CVector4 anyPointOnShadowMap(focusPoint);anyPointOnShadowMap *= sunLightViewProjTransform;// find nearest shadow map texel. The 0.5 is because texCoords are spanned// across a value of 2.0 (-1..1) and they need to be spanned across a value of 1.0float texCoordX = shadowMapSize * 0.5f * anyPointOnShadowMap.x;float texCoordY = shadowMapSize * 0.5f * anyPointOnShadowMap.y;// round to the nearest "whole" texelfloat roundedTexCoordX = round(texCoordX);float roundedTexCoordY = round(texCoordY);// the difference between the rounded and actual texCoord is the// amount by which we need to translate the shadow matrix in order to// cancel sub-texel movementfloat dx = roundedTexCoordX - texCoordX;float dy = roundedTexCoordY - texCoordY;// transform dx, dy back to homogenous light spacedx /= shadowMapSize * 0.5f;dy /= shadowMapSize * 0.5f;CMatrix roundingTransform;roundingTransform.loadTranslate(dx, dy, 0);sunLightViewProjTransform *= roundingTransform;

Revived again.

##### Share on other sites
I've managed to solve my issue. The problem was.. few code lines up:

    sunLightProjTransform.loadOrthoRH(shadowMapFocusingSphereDiameterRescale*2.0f*sphereRadius, shadowMapFocusingSphereDiameterRescale*2.0f*sphereRadius, zNear, zFar);

Every frame I was computing center and radius of view frustum's bounding sphere. And because of the numerical problems the radius was every frame slightly different. This was causing slightly different projection's plane width and height every frame, what further means that some texels were unable to do exact one-texel movement. After I have computed the radius once, everything works fine.

##### Share on other sites
Hey guys,

I've also hit the shadow stability problem with our pssm implementation. However in our case the pssm is combined with the post perspective transformation from the trapezoidal shadow maps paper for better texture coverage - as it turns out this invalidates the above fix for us (which works nicely with a simple bounding box based test version).
Unfortunately I don't have access to the ShaderX articles so I'm not sure if anything in them could point me to a possible solution. I'm hoping someone here has faced this problem before and can help :)

BoyC

##### Share on other sites
Wolf, what happened to your ShaderX7 article? I have the book on my desk but there is nothing on the "bounding spheres" trick. I don't really like having conditionals in my shaders (on a certain console they cost a lot!) and was curious to see how you resolved this.

• 11
• 11
• 10
• 11
• 12