Author Topic: JPCT special effects(Film Grain and Motion Blur)  (Read 11139 times)

Offline .jayderyu

  • long
  • ***
  • Posts: 116
    • View Profile
Re: JPCT special effects(Film Grain and Motion Blur)
« Reply #15 on: January 24, 2010, 06:14:51 pm »
I appreciate all the insight as to how it works.

Here it is the finished JPCTFilmGrain.java file
I apologize for the lack of documentation. I just feel lazy for something I was goofing around with :P
Code: [Select]
/** FilmGrain
This api is designed to give an old time cinematic feel to images or animated
scenes. Also useful for in games effects like poor reception TV .

@author Jason T. Jarvis
@version  %I%, %G%
*/

package jpctfx;

import java.util.Random;
import java.awt.image.BufferedImage;

import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.FrameBuffer;


public class JPCTFilmGrain
{
  public static final int QUALITY_HIGH = 256;
  public static final int QUALITY_GOOD = 128;
  public static final int QUALITY_MEDIUM = 64;
  public static final int QUALITY_LOW = 32;
  public static final int QUALITY_VERYLOW = 16;
  
  private int _alpha = 20;
  private int _quality = QUALITY_HIGH;
  private int _frames = 3;
  private int _currentFrame = 0;
  private int _duration = 3;
  private int _durationCount = 0;
  private boolean _colourGrain = false;
  private int[][] _ptexture;
  private Texture[] _texture;
  
  public JPCTFilmGrain(boolean initialize)
  {
    if(initialize) init();
  }
  
  public void setAlpha(int alpha)
  {
    alpha = alpha < 0 ? 0   : alpha;
    alpha = alpha > 255? 255: alpha;
    
    _alpha = alpha;
  }
  
  public void setQuality(int quality)
  {
    quality = quality < 1 ? 1 : quality;
    quality = quality > 256? 256: quality;
    
    _quality = quality;
  }
  
  public void setFrames(int frames)
  {
    _frames = frames < 1 ? 1 : frames;
  }
  
  public void setDuration(int duration)
  {
    _duration = duration < 1? 1 : duration;
  }
  
  public void colourGrain(boolean on)
  {
    _colourGrain = on;
  }
  
  public void setTexture(int index, Texture tex)
  {
    if(index >= _texture.length)
      index = index % _texture.length;
    
    TextureManager.getInstance().removeTexture("FilmGrain"+index);
    TextureManager.getInstance().addTexture("FilmGrain"+index, tex);
    _texture[index] = tex;
  }
  
  
  public int getAlpha(){ return _alpha; }
  public int getQuality(){return _quality;}
  public int getNumFrames(){return _frames;}
  public int getDuratoin(){return _duration;}
  public int getCurrentFrame(){return _currentFrame;}
  public int getDurationCount(){return _durationCount;}
  public boolean isColour(){return _colourGrain;}
  public int[][] getPixelTextureArray(){return _ptexture;}
  public int[] getPixelTexture(int index){return _ptexture[index];}
  public Texture getTexture(int index){return _texture[index];}
  public Texture[] getTextureArray(){return _texture;}
  
  
  public void init()
  {
    _texture = new Texture[_frames];
    _ptexture= new int[_frames][_quality * _quality];
    BufferedImage src;
    
    for(int i = 0; i < _frames; i++)
    {
      src = new BufferedImage(_quality, _quality, BufferedImage.TYPE_INT_ARGB);
      _ptexture[i] = createGrain(src);
      _texture[i] = new Texture(src, true);
      TextureManager.getInstance().removeTexture("FilmGrain"+i);
      TextureManager.getInstance().addTexture("FilmGrain"+i, _texture[i]);
    }
  }
  
  
  
  public void apply(FrameBuffer buffer)
  {
    if(_durationCount >= _duration)
    {
      _currentFrame++;
      
      if(_currentFrame >= _frames)
      {
        _currentFrame = 0;
      }
    }
    
    if(_texture[_currentFrame] == null)
    {
       return;
    }

    buffer.blit(_texture[_currentFrame],
      0, 0, 0, 0,
      _quality, _quality,
      buffer.getOutputWidth(),
      buffer.getOutputHeight(),
      0, false, null);
      
    _durationCount++;
  }
  
  private int[] createGrain(BufferedImage image)
  {
    int argb;
    int r, g, b;
    int pixels[] = new int[_quality * _quality];
    Random random = new Random();
    
    for(int i = 0, y = 0; y < _quality; y++)
    {
      for(int x = 0; x < _quality; x++)
      {
        r = random.nextInt(256);
        g = _colourGrain ? random.nextInt(256) : r;
        b = _colourGrain ? random.nextInt(256) : r;
        
        argb = ( (_alpha<<24) | (r<<16) | (g<<8) | b );
        
        pixels[i++] = argb;
        
        image.setRGB(x, y, argb);
      }
    }
    
    return pixels;
  }
  
}

How to use it
The easiest way is to call the default settings. This creates a default number of frames, quality and duration in the grey scale.
Code: [Select]
               filmgrain = new JPCTFilmGrain(true);

There are however more options to allow for custom settings. Different resolutions, art styles colours can impact the effect FilmGrain has.
Code: [Select]
filmgrain = new JPCTFilmGrain(false);
filmgrain.setFrames(5);
filmgrain.setDuration(3);
filmgrain.setAlpha(20);
filmgrain.setQuality(JPCTFilmGrain.QUALITY_HIGH);
filmgrain.colourGrain(true);
filmgrain.init();
First start by not allowing the default initialization. Pass  a false value to the constructor. Everything is optional as all of them have default values. Only the last line init() is required. The default vales are within the parameter.

setFrames(3) determines the number of static images to be created and cycle through.
setDuration(3) determines how many calls go by before the next static frame is used.
setAlpha(20) determines the transparency of the static frame. 0 should be full while 255 is solid.
setQuality(256) quality of the grain or size of the texture. There is QUALITY_HIGH|GOOD|MEDIUM|LOW. 256|128|64|32.
colourGrain(false) uses colours as the grain rather than grey scale.

init() must be called before use if not using the initialize/true parameter.

How to apply the grain
Code: [Select]
buffer.clear();
world.renderScene(buffer);
world.draw(buffer);
   buffer.update();

filmgrain.apply(buffer);   // <==== HERE

buffer.display(frame.getGraphics());
yep, just one line before the display is drawn to the screen. That is right. Minimally it only takes three lines of code to get FilmGrain into your projects(don't forget to import). Have fun downgrading :P
« Last Edit: January 24, 2010, 06:18:16 pm by .jayderyu »