Skip to content

Post Processing Shaders

Tinob edited this page May 27, 2016 · 11 revisions

Starting with Dolphin 4.0-2554 there has been work done to improve post processing shaders to allow more user configurability without having to modify the shader sources themselves. In order to take advantage of these features, post processing shaders must be updated to take advantage of these new features. After that, Starting From Ishiiruka version 336 Compatibility with dx11 backend has been added allowing a better cross platform experience. Then since Ishiiruka 524 a the post proccesing interface has been enchanged to support shader stacking and new function to make easy porting external shaders, all this Thanks to the work from stenzek.

Staying Portable


The first step to making a great user experience is to try and be as neutral as possible for a Direct3D or OpenGL backend. Some tips for making sure your shader will compile on all backends.

  • Instead of using GLSL vecX types, using HLSL floatX types. Our shader compiler supports floatX in GLSL, but not the other way around.

  • Use our embedded functions for determining UV location and sampling due to that location. In the future we can work around D3D having a different coordinate system using our own functions.

  • Try to stay away from HLSL only functions, Our shader compiler supports frac and lerp. But nothing else.

  • We try to support GLSL 1.30 and GLSL ES 3.00 minimum. If you don't have a good reason, try to stay away from using OpenGL shader extensions. These aren't portable and we won't be able to work around them. If the functionality of the extension is absolutely necessary, try to talk with some developers to see if we can implement the functionality in both D3D and OGL in a clean fashion using our own functions.

  • Matrix multiplication is abtracted in the interface to allow compatibility so to avoid problems use the interface function mult instead of the native multiplication operators. also for matrix types use hlsl notation. Types supported:

    • float1x1 same as mat1 on OpenGL
    • float2x2 same as mat2 on OpenGL
    • float3x3 same as mat3 on OpenGL
    • float4x4 same as mat4 on OpenGL
    • float4x3 same as mat4x3 on OpenGL
    • float3x4 same as mat3x4 on OpenGL

Configuration Setup


The Post Processing shader's configuration sits inside of the actual post processing shader code. It's recommended to have the configuration towards the top of the shader, outside of the main function. There are two requirements to this

  • It must be surrounded by [configuration] and [/configuration]

The configuration inside this block is similar to how Windows' INI files work and we have four sections that we support for different options.

  • [OptionBool]
    • This is a boolean option, this'll show as a checkbox in the GUI
  • [OptionRangeInteger] or [OptionRangeFloat]
    • This is a integer or float range that is anywhere from one to four elements
    • This will show up as one to four sliders in the GUI
  • [Pass]
    • Allows the definition of multiple Passes. Multi-pass algorithms are a powerful tool to allow complex effects that can’t be achieved in a single pass. Every stage must define and entry point.

The Stage block requires the definition of these settings

  • EntryPoint
    • Required name of the specific function that will implement the logic used, the keyword that designates the name of the entry point must be unique and cannot be repeated in other parts of the shader.
  • OutputScale
    • Optional float value, is a multiplier that scales the resolution of the framebuffer used as destination for the pass. for example if the Input resolution is 1280x720 and you define a scale of 0.5 the pass will use a output framebuffer of 640x360 pixels.
  • DependentOption
    • Optional Keyword that points to the OptionName from an existing boolean option. If the option is disabled the stage will be skipped. This allows optimal performance skipping unused Stages.
  • Input[Number]=[Parameter]
    • Set the configuration option for every input you can have up to 4 inputs for every pass.
    • [Number] input pass id goes from 0 to 3
    • [Parameter] set te parameters for the current input. Posible values are:
      • [ColorBuffer|DepthBuffer|Image|PreviousPass|PassX]
      • ColorBuffer input comes from the original color buffer
      • DepthBuffer input comes from the original depth buffer
      • Image imput comes from an external image, the image must be located inside the shader folder. the path is configured with the Source Parameter.
      • PreviousPass inputs comes from the inmediate previous pass this input is invalid in the first pass.
      • PassX id of the previous pass that is taken as an input is encoded in X.
      • Filter=[Nearest|Linear]
      • The filter used when sampling the specific input
      • Mode=[Clamp|Wrap|Border]
      • The mode used when sampling outside the internal textures coordinates.
      • Source
        • Path where the source image is located. absolute or relative paths from the shader folder can be used.

Options require all three settings set at all times

  • GUIName
    • This is a string that will show up in the GUI for that corresponding option
  • GUIDescription
    • This is a string that will show in the GUI in the description panel for that corresponding option
  • OptionName
    • This is a case sensitive unique identifier that is used in the post processing shader
    • You'll get errors if this isn't unique
  • DefaultValue
    • This is the default value that is set prior to any sort of user configuration
    • For [OptionBool] this can be 'True' or 'False'
    • For [OptionRangeInteger] and [OptionRangeFloat] this can be a single value or a vector
    • How many values in this setting determine how many values show up in the UI
    • Examples
      • DefaultValue = True
      • DefaultValue = 1
      • DefaultValue = 1.0, 1.0, 1.0, 1.0

GUIName and GUIDescription localization

This option can be localized depending on specific cultures. to replaze the default value for a specific culture just add a new key with a specific culture suffix. for example for spanish and german overrides:

  • GUIName.SPA =
  • GUIDescription.SPA =
  • GUIName.GER =
  • GUIDescription.GER =

The available codes are:

  • CATALAN ".CAT"
  • CZECH ".CZE"
  • GERMAN ".GER"
  • ENGLISH ".ENG"
  • SPANISH ".SPA"
  • FRENCH ".FRE"
  • ITALIAN ".ITA"
  • HUNGARIAN ".HUN"
  • DUTCH ".DUT"
  • NORWEGIAN BOKMAL ".NOR"
  • POLISH ".POL"
  • PORTUGUESE ".POR"
  • PORTUGUESE BRAZILIAN ".BRA"
  • SERBIAN ".SER"
  • SWEDISH ".SWE"
  • TURKISH ".TUR"
  • GREEK ".GRE"
  • RUSSIAN ".RUS"
  • HEBREW ".HEB"
  • ARABIC ".ARA"
  • FARSI ".FAR"
  • KOREAN ".KOR"
  • JAPANESE ".JAP"
  • CHINESE SIMPLIFIED ".CHS"
  • CHINESE TRADITIONAL ".CHT"

There are two optional setting that all three configuration types can have; This are 'DependentOption' and 'ResolveAtCompilation'.

'DependentOption' allows options to be children of [OptionBool] options. This allows basic grouping of options, which effects how options will show up in the GUI. If a option has children they will show up in the same tab, if there aren't any children they will show up in a general option tab. The value of this option must be the 'OptionName' of a [OptionBool] If you make circular dependencies of options, you reap what you sow.

'ResolveAtCompilation' changes the behavior of the option, instead of a dynamic parameter, a preprocessor definition is used. This gives the ability to define compilation time options useful for some compilers with restrictions. You have to use preprocessor syntax to access this options. For example if you define an option called BLUR_ENABLED you have to use it like this:

#IF BLUR_ENABLED = 1
#ENDIF

[OptionRangeInteger] and [OptionRangeFloat] have other required settings to be set

  • MinValue
    • This is the minimum value that a integer or float range has
    • In a vector each value can have a different value
  • MaxValue
    • This is the maximum value that a integer or float range has
    • In a vector each value can have a different value
  • StepAmount
    • This is how much the range changes in the UI per step
    • In a vector each value can have a different value

Sample Configuration

/*
[configuration]

[OptionBool]
GUIName = Skylight Bevel Shader
OptionName = BEVEL
DefaultValue = true

[OptionRangeFloat]
GUIName = Skylight Amount
OptionName = SKYLIGHT_AMOUNT
MinValue = 0.0
MaxValue = 1.0
StepAmount = 0.05,
DefaultValue = 0.5
DependentOption = BEVEL

[OptionRangeFloat]
GUIName = Sky Color
OptionName = SKY_COLOR
MinValue = 0.0, 0.0, 0.0
MaxValue = 1.0, 1.0, 1.0
StepAmount = 0.05, 0.05, 0.05
DefaultValue = 0.1, 0.1, 0.5
DependentOption = BEVEL

[OptionRangeInteger]
GUIName = Ground Color
OptionName = GROUND_COLOR
MinValue = 0, 0, 0
MaxValue = 256, 256, 256
StepAmount = 1, 1, 1
DefaultValue = 1, 1, 1
DependentOption = BEVEL
[Stage]
EntryPoint = ApplySkylight
DependentOption = BEVEL
[/configuration]
*/

Embedded Functions


float4 Sample();
float  SampleDepth();
float  SampleDepthRaw();
float4 SamplePrev();
float4 SamplePrev(int idx);

Samples the current location. Sample returns a vector containing the colour of the current sample. SampleDepth the linear depth value. SampleDepthRaw the raw depth value. SamplePrev returns a vector containing the colour of the first input . SamplePrev(int) returns a vector containing the colour of a specific input.


float4 SampleLocation(float2 location);
float  SampleDepthLocation(float2 location);
float  SampleDepthRawLocation(float2 location);
float4 SamplePrevLocation();
float4 SamplePrevLocation(int idx);

Samples a location that doesn't have to be the current sample location location is a float vector which contains the X Y coordinates of where to sample from. The sample range is 0.0 to 1.0 using the OpenGL coordinate system


float4 SampleOffset(int2 offset);
float  SampleDepthOffset(int2 offset);
float  SampleDepthRawOffset(int2 offset);
float4 SamplePrevOffset(int2 offset);
float4 SamplePrevOffset(int idx, int2 offset);

This samples at a offset of the current location. The argument is a integer vector containing the X Y offset of where to sample from. The offset is in pixels instead of using UV coordinates.


float4 SampleLayer(int l);
float4 SampleDepthLayer(int l);
float4 SampleDepthRawLayer(int l);

This samples at a specific layer in the current location. The argument is a integer containing the layer index. this apply only when using 3d visualization. the posible values are 0 or 1.


float4 Sample(float2 loc, int l);
float4 SampleDepth(float2 loc, int l);
float4 SampleDepthRaw(float2 loc, int l);

This samples at a "l" layer in "loc" location. "loc" is a float vector which contains the X Y coordinates of where to sample from. The sample range is 0.0 to 1.0 using the OpenGL coordinate system "l" argument is a integer containing the layer index. this apply only when using 3d visualization. the posible values are 0 or 1.


float4 SampleFontLocation(float2 location);

This doesn't sample the framebuffer like the previous functions. This actually samples a location from Dolphin's bitmap font that it uses for on screen text. The best example of how to use this is to look at Dolphin's asciiart PP shader


float2 GetFragmentCoord();

Returns a float vector contain the current fragment coords offseted by 0.5.


float2 GetViewportRect();

Returns a float vector contain the current vieport position.


float2 GetWindowRect();

Returns a float vector contain the current window pos in the screen.


float2 GetTargetRectOrigin();
float2 GetTargetRectSize();

Returns the position and size of the target rectangle.


float2 GetInputResolution(int index);
float2 GetTargetRectSize(int index);

Returns the size data for a specific input.


float2 GetPrevResolution();
float2 GetInvPrevResolution();

Returns the size data for The previous pass if is configured as an input. If not it just returns the size of the first input.


float2 GetTargetResolution(int index);
float2 GetInvTargetResolution(int index);

Returns the size data for the target texture.


float2 GetSourceRectOrigin();
float2 GetSourceRectSize();

Returns the size and position data for the source rectangle.


float2 GetTargetRectOrigin();
float2 GetTargetRectSize();

Returns the size and position data for the Target rectangle.


float4 ApplyGCGamma(float4 col);

Apply the current native Gamma defined by the running game.


float2 GetResolution();

Returns a float vector contain the resolution of the framebuffer


float2 GetInvResolution();

Returns a float vector containing the reciprocal of the framebuffer resolution This can be used to quickly get a float offset from the current sample position


float2 GetCoordinates();

Returns a float vector containing the UV coordinates where we are currently sampling from This can be used when sampling from a location offset from the current location. Tends to be used in conjunction with GetInvResolution() Recommended to use SampleOffset instead if the offset is a compile time constant


uint GetTime();

This returns a 32bit unsigned integer of the elapsed time since emulation has started The time is in milliseconds This allows for shaders that can do certain effects with the passage of time


void SetOutput(float4 colour);

The argument is a float vector containing what the resulting output should be at the current sampling location


bool OptionEnabled(option);

Returns a boolean for if a boolean option is enabled or not This only works for the Option type configuration option Takes the OptionName that was defined in the configuration

  • Example
if (OptionEnabled(BEVEL)) { /* Do Stuff*/ }

genType GetOption(option);

Returns an options current setting genType is either int, float, float2/3/4, int2/3/4 Depending on what type your option is in the configuration it'll return the correct type here Takes the OptionName that was defined in the configuration

  • Example
float3 colour_boost = GetOption(SKY_COLOR);

genType mult(genType a, genType b);

Matrix multiplication operator, allow the multiplication of vectors and matrices. The output type depends on the type and dimensions of the inputs. gentype can be one of the following types: float2,3,4 float1x1 float2x2 float3x3 float4x4 float4x3 float3x4


float RandomSeedfloat(float2 seed);

Generates a random float using the supplied seed.


uint RandomSeeduint(float2 seed);

Generates a random uint using the supplied seed.


void Randomize();

Initializes the internal PRNG. Use this instruction at the start of the shader if you intent to use random numbers.


uint Rndint();
float Rndfloat();
float2 Rndfloat2();
float3 Rndfloat3();
float4 Rndfloat4()

Helper functions to generate diferent types of random values.


float4 SampleBicubic();
float4 SampleBicubic(float2 location);
float4 SampleBicubic(float2 location, float resolutionmultiplier);

float4 SamplePrevBicubic(float2 location, int idx, float resolutionmultiplier);
float4 SamplePrevBicubic(float2 location, int idx);
float4 SamplePrevBicubic(float2 location);

Color Sampling Functions using bicubic sample method.


Sample Shader


/*
[configuration]
[OptionBool]
GUIName = Skylight Bevel Shader
OptionName = BEVEL
DefaultValue = true

[OptionRangeFloat]
GUIName = Skylight Amount
OptionName = SKYLIGHT_AMOUNT
MinValue = 0.0
MaxValue = 1.0
StepAmount = 0.05,
DefaultValue = 0.5
DependentOption = BEVEL

[OptionRangeFloat]
GUIName = Sky Color
OptionName = SKY_COLOR
MinValue = 0.0, 0.0, 0.0
MaxValue = 1.0, 1.0, 1.0
StepAmount = 0.05, 0.05, 0.05
DefaultValue = 0.1, 0.1, 0.5
DependentOption = BEVEL

[OptionRangeFloat]
GUIName = Base Color
OptionName = BASE_COLOR
MinValue = 0.0, 0.0, 0.0
MaxValue = 1.0, 1.0, 1.0
StepAmount = 0.05, 0.05, 0.05
DefaultValue = 0.0, 0.0, 0.1
DependentOption = BEVEL

[OptionRangeFloat]
GUIName = Ground Color
OptionName = GROUND_COLOR
MinValue = 0.0, 0.0, 0.0
MaxValue = 1.0, 1.0, 1.0
StepAmount = 0.05, 0.05, 0.05
DefaultValue = 0.1, 0.4, 0.1
DependentOption = BEVEL

[OptionBool]
GUIName = Sharpen
OptionName = SHARPNESS
DefaultValue = true

[OptionRangeFloat]
GUIName = Sharpness Amount
OptionName = SHARPNESS_VALUE
MinValue = 0.0
MaxValue = 10.0
StepAmount = 0.5
DefaultValue = 1.5
DependentOption = SHARPNESS

[OptionBool]
GUIName = Color Boost
OptionName = COLOR_BOOST
DefaultValue = true

[OptionRangeFloat]
GUIName = Color Boost Red
OptionName = RED_BOOST
MinValue = 0.0
MaxValue = 5.0
StepAmount = 0.1
DefaultValue = 1.0
DependentOption = COLOR_BOOST

[OptionRangeFloat]
GUIName = Color Boost Green
OptionName = GREEN_BOOST
MinValue = 0.0
MaxValue = 5.0
StepAmount = 0.1
DefaultValue = 1.0
DependentOption = COLOR_BOOST

[OptionRangeFloat]
GUIName = Color Boost Blue
OptionName = BLUE_BOOST
MinValue = 0.0
MaxValue = 5.0
StepAmount = 0.1
DefaultValue = 1.0
DependentOption = COLOR_BOOST
[Pass]
EntryPoint = main
Input0=ColorBuffer
Input0Filter=Linear
Input0Mode=Clamp
[/configuration]
*/

void main() {
  float3 curr = Sample().xyz;

  if (OptionEnabled(SHARPNESS))
  {
	  float3 s0 = SampleOffset(int2(-1, -1)).xyz;
	  float3 s1 = SampleOffset(int2( 1, -1)).xyz;
	  float3 s2 = SampleOffset(int2(-1,  1)).xyz;
	  float3 s3 = SampleOffset(int2( 1,  1)).xyz;
	  float3 blur = (curr + s0 + s1 + s2 + s3) / 5;
	  curr = curr + (curr - blur) * GetOption(SHARPNESS_VALUE);
  }
  
  if (OptionEnabled(BEVEL))
  {
	  float mid = dot(curr, float3(0.33, 0.33, 0.33));
	  float top = dot(SampleOffset(int2(0,  1)).xyz, float3(0.33, 0.33, 0.33));
	  float bot = dot(SampleOffset(int2(0, -1)).xyz, float3(0.33, 0.33, 0.33));
	  float upw = ((top - mid) + (mid - bot)) / 4 + 0.5;
	  float3  col = mix(GetOption(SKY_COLOR), GetOption(GROUND_COLOR), upw);
	  float3 shde = mix(GetOption(BASE_COLOR), col, clamp(abs(upw * 2.0 - 1.0) * 1, 0.0, 1.0));
	  curr = curr + shde * GetOption(SKYLIGHT_AMOUNT);
  }

  if (OptionEnabled(COLOR_BOOST))
	  curr = curr * float3(GetOption(RED_BOOST), GetOption(GREEN_BOOST), GetOption(BLUE_BOOST));

  /* Done */
  SetOutput(float4(curr, 1.0));
}

Translations


TBD

Clone this wiki locally