Light shafts problem

671f0d10b08dc9b3a8662f9eacae0798
0
sukovf 101 Mar 01, 2012 at 14:22

Hi guys.

I am trying to achieve simple light shafts effect and have some weird problem with it.

0e9ce3c91a.jpg

As you can see the 3/4 of the effect look quite good but there are weird streaks going top left. This is the HLSL code:

float3 lightPos;

float2 texCoords=input.TexCoords;
float4 pos=lightPos;
pos.x=((lightPos.x+1.0f)/2.0f);
pos.y=((-lightPos.y+1.0f)/2.0f);

float2 dir=(texCoords-pos.xy)/SAMPLES;
float3 sum=0.0f;
float add=1.0f;
for(int i=0;i<SAMPLES;i++)
{
  float val=tex2D(tex, texCoords).rgb;  
  sum+=val*add;  
  add*=0.94f;  
  texCoords-=dir;
}

return float4(sum, 1.0f);

Light position in screen space is computed in C++ this way:

D3DXVec3Transform(&outPos, &lightPosWorldSpace, &(camera->GetViewMatrix()*camera->GetProjMatrix()));

“outPos” is a D3DXVECTOR3 and is passed to the shader.

Screen space position of the light looks good. I have no idea what is wrong.

Any advice please?

6 Replies

Please log in or register to post a reply.

Cd586a7130b6cb95bed9ae57223fad5c
0
SuperPixel 101 Mar 01, 2012 at 16:53

@sukovf

Hi guys.

I am trying to achieve simple light shafts effect and have some weird problem with it.

0e9ce3c91a.jpg

As you can see the 3/4 of the effect look quite good but there are weird streaks going top left. This is the HLSL code:

float3 lightPos;

float2 texCoords=input.TexCoords;
float4 pos=lightPos;
pos.x=((lightPos.x+1.0f)/2.0f);
pos.y=((-lightPos.y+1.0f)/2.0f);

float2 dir=(texCoords-pos.xy)/SAMPLES;
float3 sum=0.0f;
float add=1.0f;
for(int i=0;i<SAMPLES;i++)
{
  float val=tex2D(tex, texCoords).rgb;  
  sum+=val*add;  
  add*=0.94f;  
  texCoords-=dir;
}

return float4(sum, 1.0f);

Light position in screen space is computed in C++ this way:

D3DXVec3Transform(&outPos, &lightPosWorldSpace, &(camera->GetViewMatrix()*camera->GetProjMatrix()));

“outPos” is a D3DXVECTOR3 and is passed to the shader.

Screen space position of the light looks good. I have no idea what is wrong.

Any advice please?

If you don’t perform homogeneous division by w that can lead to problems ! Therefore, I suggest you to perform the light position transform using a 4D vector like this: lightPos4D = D3DXVECTOR4(lightPosWorldSpace,1.f);

(supposing that lightPosWorldSpace is a 3D vector of course).

Then you transform that 4D vector by viewProjection and then perform division by w and only then your lightPos will be in normalized screen space which goes from 0,1 and not from -w,w (as in the case of clip space)

D3DXVec4Transform(&clipSpaceLightPos,&D3DXVECTOR4(lightPosWorldSpace,1.f),&(camera->GetViewMatrix()*camera->GetProjMatrix()));
D3DXVECTOR4 screenSpaceLightPos = clipSpaceLightPos / clipSpaceLightPos.w; //after the division by w your light position will be in screen space

// so you pass screenSpaceLightPos to the shader (of course you can just pass it as a 3D vector now if you want, as after the homogeneous division w would equal to 1 )

Also remember that you should always transform the points (and therefore not the directions) in the form of 4D vectors and only directions should be threated as 3D vectors eventually. The reason is simple: directions are translation invariant (therefore no need to specify the 4th component which obviously would be 0). Points are points !!! Not directions so they are not translation invariant (therefore you need the 4th component specyfing 1 to account for the translation part of the 4x4 matrix).
Directions are not rotation nor scaling invariant that’s why the only thing that will change if you transform a direction will be it’s direction :D and eventually its length (if the scaling part is not 1).

So, when you transform directions you should always use the inverseTranspose of your transformation matrix to account for the general case.

Let me know if it works for you and show me the results ;)

671f0d10b08dc9b3a8662f9eacae0798
0
sukovf 101 Mar 01, 2012 at 17:26

Well, the problme was somewhere else. Screen space position of the lights was correct. BUT. I was firstly rendering colored spheres representing light sources (light bulbs, etc.) and then performing the shafts effect for every light. The shader just radialy blurs rendered spheres, but not only the sphere belonging to current light, it also takes into account other spheres as they are also rendered and makes these nasty streaks.

The solution is for each light, render a colored sphere, perform the shafts effect and additively blend it to the shafts accumulating buffer. So this problem is solved as you can see on the following image:

61413e14c7.jpg

Thanks for your time anyway.

Cd586a7130b6cb95bed9ae57223fad5c
0
SuperPixel 101 Mar 01, 2012 at 17:37

@sukovf

Well, the problme was somewhere else. Screen space position of the lights was correct. BUT. I was firstly rendering colored spheres representing light sources (light bulbs, etc.) and then performing the shafts effect for every light. The shader just radialy blurs rendered spheres, but not only the sphere belonging to current light, it also takes into account other spheres as they are also rendered and makes these nasty streaks.

The solution is for each light, render a colored sphere, perform the shafts effect and additively blend it to the shafts accumulating buffer. So this problem is solved as you can see on the following image:

61413e14c7.jpg

Thanks for your time anyway.

Looks good to me.

So you where actually dividing by w before to pass the transformed lightPosition to the shader ?

Side track question: If you’d like to make sun shafts you would equally render a sphere at the sun position performing the same radial blur, or there is a better approach for that ? And how many SAMPLES you use ?

671f0d10b08dc9b3a8662f9eacae0798
0
sukovf 101 Mar 01, 2012 at 18:06

I must appologize for a little mystification. I am dividing the position by W in the shader. For sun shafts you can render sky with texture or some light scattering shader and perform blur on it. Also some bright limit should be applied before blurring so the sun will be visible but the rest of the sky not.

I am using 100 samples. It is quite a lot, but lowering number of samples leads in nasty flickering artifacts. And finally I aply simple gauss blur on the whole shafts buffer. The buffer is half resolution of back buffer.

Cd586a7130b6cb95bed9ae57223fad5c
0
SuperPixel 101 Mar 02, 2012 at 09:25

@sukovf

I must appologize for a little mystification. I am dividing the position by W in the shader. For sun shafts you can render sky with texture or some light scattering shader and perform blur on it. Also some bright limit should be applied before blurring so the sun will be visible but the rest of the sky not. I am using 100 samples. It is quite a lot, but lowering number of samples leads in nasty flickering artifacts. And finally I aply simple gauss blur on the whole shafts buffer. The buffer is half resolution of back buffer.

For what that concern the sky … should be a texture with a sun already included in the texture ? a billboarded flare ? or what ?

As for your solution you could actually render a glow card instead of blurring a sphere … is cheaper and the visual result is exactly the same. Radially blur something is useful I think only if you want the shafts to be camera driven as is the case of lens flare fx !

What do you think ?

671f0d10b08dc9b3a8662f9eacae0798
0
sukovf 101 Mar 02, 2012 at 12:34

No, just render your scene normaly and then render sky geometry occluded by obtained depth buffer to a render target used as a source for shafts effect.

A billboard would not be enought. Look at this:

7d631c36e5.jpg

I will never be able to achieve this with simple billboard. I want the light to “god raying” through fences, trees, etc. Btw. light shafts aka “god rays” have nothing to do with lens flares.