D3DBook:Useful Effect Snippets

From GDWiki
Jump to: navigation, search

Contents

Useful Effect Snippets

The following collection of effect snippets mimic old TV or camera effects. This can be useful to achieve flash back or old TV effects or low quality cameras that are used by the police.

Sepia

Sepia tone is a color space that is used to give images an aged or antique feel. The following implementation follows [Ansari].

To achieve a good looking sepia tone effect, the original color space YIQ that was used to encode NTSC video in 1953 is used. To perform the conversion, the RGB values need to be transformed into YIQ color space and vice versa.

The matrix to convert an RGB sample into YIQ is

Image:ConvertFromRGBToYIQ.gif
Equation 29

The first row of M computes the luminance of the input RGB samples, which corresponds to the Y component of YIQ. The second and third row encode the chromacity into I and Q.

The matrix to convert YIQ back to RGB is M's inverse.

Image:ConvertFromYIQtoRGB.gif
Equation 30

To optimize the RGB to YIQ and vice versa conversion for Sepia, the RGB to YIQ conversion can be reduced to the luminance value; Y. This comes down to a dot product between the RGB value and the first row of the RGB-To-YIQ matrix.

To simplify the operation while converting back to RGB from Y  -Sepia-, we can replace the I value with 0.2 and the Q value with 0.0 for a Sepia effect. This comes down to a vector  consisting of the following values.

Image:SepiaYIQtoRGB conversion.gif
Equation 31

The values 0.2 and 0.0 were obtained by experimentation, and other values might look better in different applications.

Implementation

The implementation to convert from RGB to Y can be achieved with the following line of HLSL code.

...
float3 IntensityConverter={0.299, 0.587, 0.114};
Y = dot(IntensityConverter, Color);
...

Converting back to RGB can happen with the following code.

...
float4 sepiaConvert ={0.191, -0.054,-0.221,0.0};
return Y + sepiaConvert;
...

Film Grain

A film grain filter can be used to mimic the bad reception of a TV sender. This filter can be achieved by fetching a wrapped 64x64x64 or 32x32x32 3D volume texture that holds random noise values by running through its z slices based on a time counter. The following source code shows how this can be achieved:

...
// pos has a value range of -1..1
// the texture address mode is wrapped
float rand = tex3D(RandomSampler, float3(pos.x, pos.y, counter));
 
float4 image = tex2D(ImageSampler, Screenpos.xy);
 
return rand + image;
...

Frame border

A border around the screen can be achieved by applying a power function to the extrema of the screen. The result is then clamped to 0..1 and used to multiply the end result of the other two filters like this:

...
float f = (1-pos.x*pos.y) * (1-pos.y*pos.y);
float frame = saturate(frameSharpness * (pow(f, frameShape) - frameLimit));
 
return frame * (rand + image);
...

The variable frameSharpness makes the transition between the frame and the visible scene softer. The variable frameshape makes the shape of the frame rounder or more edgy and the variable framelimit makes the frame bigger or smaller.

Median Filter

A median filter is usually used to remove "salt and pepper noise" from a picture [Mitchell]. For example if you have the following set of numbers: {9, 3, 6, 1, 2, 2, 8}, you can sort them to get {1, 2, 2, 3, 6, 8, 9} and select the middle value 3. Hence the median of these values is 3. This is better than taking the mean, because this value actually appears in the image. This filter can be also used to mimic MPEG compressed movies from a cheap camcorders.

As it turns out an approximation to a 2D median filter can be efficiently in a separable manner. For example with the following data we can get the median in figure 15:

Image:Median.jpg
Figure 15 - Median Filter

An implementation picks out of three values the median by using the following algorithm.

float FindMedian(float a, float b, float c)
{
    float Median;
 
    if(a < b)
    {
        if(b<c)
            Median = b;
        else
            Median = max(a, c);
    }
    else
    {
        if(a < c)
            Median = a;
        else
            Median = max(b, c);
    }
 
    return Median;
}

This can be vectorized to the following code:

float3 FindMedian(float3 RGB,&nbsp;float3&nbsp;RGB2,&nbsp;float3&nbsp;RGB3)
{ 
    return (RGB < RGB2) ? ((RGB2 < RGB3) ? RGB2 : max(RGB,RGB3) ) : ((RGB < RGB3) ? RGB : max(RGB2,RGB3));
}

Interlace Effect

Any interlace effect modifies every second line of the image differently than the other line. The lines are separated by taking the fraction of the unnormalized coordinates in the y direction like this:

float fraction = frac(UnnormCoord.y * 0.5);
 
if (fraction)
    ... // do something with this line
else
    ... // do something different with every second line
Personal tools