AreaLight : Rectangle

Started by
8 comments, last by Dawoodoz 10 years, 2 months ago

Hi all,

I'm working on some area light (forward rendering).

I searched on the net for some documentation and tried with what I found.

Here the actual code :


void Area( in SURFACE_DATA SurfaceData, in AREA_LIGHT Light, inout LIGHTING_RESULTS Result )
{
  float3 ToLightPosition = SurfaceData.Position - Light.Position;
  float DistOnLine = dot( ToLightPosition, Light.Direction );
  float3 PointOnLine = SurfaceData.Position - Light.Direction * DistOnLine;
  float3 FromCenter = PointOnLine - Light.Position;
  float DiagonalX = dot( FromCenter, Light.Right );
  float DiagonalY = dot( FromCenter, Light.Up );
  float NearestX = clamp( DiagonalX, -Light.HalfSize.x, Light.HalfSize.x );
  float NearestY = clamp( DiagonalY, -Light.HalfSize.y, Light.HalfSize.y );
  float3 NearestInside = Light.Position + ( Light.Right*NearestX + Light.Up*NearestY );
  float3 LightDirection = NearestInside - SurfaceData.Position;
  float d = length( LightDirection );
  if( d < Light.EndAttenuation )
  {
    LightDirection /= d;
    float NdotL = max( 0.0f, dot( SurfaceData.Normal, LightDirection ) );
    if( NdotL > 0.0f )
    {
      // Compute the specular factor.
      float SpecFactor = ComputePhongFactor( SurfaceData, LightDirection );
      
      // Compute the attenuation factor.
      float Attenuation = smoothstep( Light.EndAttenuation, Light.StartAttenuation, d );
      
      // Accumulate the light results.
      Result.Diffuse += Light.Intensity * DiffuseColor.rgb * Light.Color * Attenuation;
      Result.Specular += Light.Intensity * SpecularColor.rgb * SpecFactor * Attenuation;
    }
  }
}
I got bad result based on orientation and surely a better way exist for better result.
If someone can explain me what's going wrong and how get better result.
Thanks for the help
EDIT : change to if( d < Light.EndAttenuation && DistOnLine > 0.0f ) removes the problem of side but still a prob of rendering.
Advertisement

Check that the light sources and models are represented in the same space. Light sources are sometimes represented in camera space.

Output some simple colors to make sure that your constant buffers don't have padding or alignment mismatch between GPU and CPU.

Here an image to show you the problem :

http://uppix.com/f-AreaLight_Prob_L52ed025700154d41.png

I think you need to normalize both the surface normal and the light direction since it looks as if your dot product is returning huge values.

LightDirection is already normalized as you can see :


LightDirection /= d;

and the normal has no problem with other lighting type.

The problem looks like an angle who is missing, an idea ?

I don't see any multiplication with NdotL in the light intensity.

Thanks to have solved this problem !

The actual shader working is now :


void Area( in SURFACE_DATA SurfaceData, in AREA_LIGHT Light, inout LIGHTING_RESULTS Result )
{
  float3 ToLightPosition = SurfaceData.Position - Light.Position;
  float DistOnLine = dot( ToLightPosition, Light.Direction );
  float3 PointOnLine = SurfaceData.Position - Light.Direction * DistOnLine;
  float3 FromCenter = PointOnLine - Light.Position;
  float DiagonalX = dot( FromCenter, Light.Right );
  float DiagonalY = dot( FromCenter, Light.Up );
  float NearestX = clamp( DiagonalX, -Light.HalfSize.x, Light.HalfSize.x );
  float NearestY = clamp( DiagonalY, -Light.HalfSize.y, Light.HalfSize.y );
  float3 NearestInside = Light.Position + ( Light.Right*NearestX + Light.Up*NearestY );
  float3 LightDirection = NearestInside - SurfaceData.Position;
  float d = length( LightDirection );
  if( d < Light.EndAttenuation && DistOnLine > 0.0f )
  {
    LightDirection /= d;
    float NdotL = max( 0.0f, dot( SurfaceData.Normal, LightDirection ) );
    if( NdotL > 0.0f )
    {
      // Compute the specular factor.
      float SpecFactor = ComputePhongFactor( SurfaceData, LightDirection );
      
      // Compute the attenuation factor.
      float Attenuation = smoothstep( Light.EndAttenuation, Light.StartAttenuation, d );
      
      // Accumulate the light results.
      Result.Diffuse += Light.Intensity * DiffuseColor.rgb * Light.Color * NdotL * Attenuation;
      Result.Specular += Light.Intensity * SpecularColor.rgb * SpecFactor * Attenuation;
    }
  }
}

If you have any idea to have better result, it's open on the discussion.

I think the attenuation needs a better physical calcule.

Thanks to have solved this problem !

The actual shader working is now :


void Area( in SURFACE_DATA SurfaceData, in AREA_LIGHT Light, inout LIGHTING_RESULTS Result )
{
  float3 ToLightPosition = SurfaceData.Position - Light.Position;
  float DistOnLine = dot( ToLightPosition, Light.Direction );
  float3 PointOnLine = SurfaceData.Position - Light.Direction * DistOnLine;
  float3 FromCenter = PointOnLine - Light.Position;
  float DiagonalX = dot( FromCenter, Light.Right );
  float DiagonalY = dot( FromCenter, Light.Up );
  float NearestX = clamp( DiagonalX, -Light.HalfSize.x, Light.HalfSize.x );
  float NearestY = clamp( DiagonalY, -Light.HalfSize.y, Light.HalfSize.y );
  float3 NearestInside = Light.Position + ( Light.Right*NearestX + Light.Up*NearestY );
  float3 LightDirection = NearestInside - SurfaceData.Position;
  float d = length( LightDirection );
  if( d < Light.EndAttenuation && DistOnLine > 0.0f )
  {
    LightDirection /= d;
    float NdotL = max( 0.0f, dot( SurfaceData.Normal, LightDirection ) );
    if( NdotL > 0.0f )
    {
      // Compute the specular factor.
      float SpecFactor = ComputePhongFactor( SurfaceData, LightDirection );
      
      // Compute the attenuation factor.
      float Attenuation = smoothstep( Light.EndAttenuation, Light.StartAttenuation, d );
      
      // Accumulate the light results.
      Result.Diffuse += Light.Intensity * DiffuseColor.rgb * Light.Color * NdotL * Attenuation;
      Result.Specular += Light.Intensity * SpecularColor.rgb * SpecFactor * Attenuation;
    }
  }
}

If you have any idea to have better result, it's open on the discussion.

I think the attenuation needs a better physical calcule.

I would put the product of (Light.Intensity * (NdotL * Attenuation)) in a new vector.

My engine use a quadratic function to approximate division of square distance without giving an infinite size to it's culling bounds.

https://code.google.com/p/david-piuvas-graphics-engine/source/browse/trunk/Engine/Core_ShaderCompiling.h

My engine use a quadratic function to approximate division of square distance without giving an infinite size to it's culling bounds.

You can have a look at :
You can have a look on this paper page 12-13 :
http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
It's the new attenuation in unreal engine 4.


float Factor = clamp(1.0f - pow(d / Light.EndAttenuation, 4.0f), 0.0f, 1.0f);
float Attenuation = (Factor * Factor) / ((d * d) + 1.0f);

Maybe you already tried this function ?

What do you think of it ?

My engine use a quadratic function to approximate division of square distance without giving an infinite size to it's culling bounds.

You can have a look at :
You can have a look on this paper page 12-13 :
http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
It's the new attenuation in unreal engine 4.


float Factor = clamp(1.0f - pow(d / Light.EndAttenuation, 4.0f), 0.0f, 1.0f);
float Attenuation = (Factor * Factor) / ((d * d) + 1.0f);

Maybe you already tried this function ?

What do you think of it ?

Seems a bit expensive for today's hardware.

This topic is closed to new replies.

Advertisement