mirror of
https://github.com/libretro/docs.git
synced 2024-11-23 16:59:40 +00:00
8a8f9b31d2
In #526 I accidentally lost some of my edits trying to selectively stage lines with typo fixes, while deleting hunks that were only whitespace changes. In hopes nobody else falls into this trap, I ran the following command to find all the files to edit, and apply emacs's delete-trailing-whitespace function to each file. ```sh find * -type f -print0 | xargs -0 -P$(nproc) -n1 -I{} sh -c 'if file -b "{}" | grep -qFi text; then emacs -batch "{}" -f delete-trailing-whitespace -f save-buffer; fi' ``` Note: `git add -u` will add only known paths to the stage (index), even if `tidy.sh` exists in the git work directory.
2193 lines
36 KiB
Plaintext
2193 lines
36 KiB
Plaintext
#LyX 2.0 created this file. For more info see http://www.lyx.org/
|
||
\lyxformat 413
|
||
\begin_document
|
||
\begin_header
|
||
\textclass article
|
||
\use_default_options true
|
||
\maintain_unincluded_children false
|
||
\language english
|
||
\language_package default
|
||
\inputencoding auto
|
||
\fontencoding global
|
||
\font_roman default
|
||
\font_sans default
|
||
\font_typewriter default
|
||
\font_default_family default
|
||
\use_non_tex_fonts false
|
||
\font_sc false
|
||
\font_osf false
|
||
\font_sf_scale 100
|
||
\font_tt_scale 100
|
||
|
||
\graphics default
|
||
\default_output_format default
|
||
\output_sync 0
|
||
\bibtex_command default
|
||
\index_command default
|
||
\paperfontsize default
|
||
\spacing single
|
||
\use_hyperref true
|
||
\pdf_title "Cg/HLSL Libretro shader tutorial"
|
||
\pdf_author "Hans-Kristian Antzen, Daniel De Matteis"
|
||
\pdf_bookmarks true
|
||
\pdf_bookmarksnumbered false
|
||
\pdf_bookmarksopen false
|
||
\pdf_bookmarksopenlevel 1
|
||
\pdf_breaklinks false
|
||
\pdf_pdfborder false
|
||
\pdf_colorlinks true
|
||
\pdf_backref false
|
||
\pdf_pdfusetitle true
|
||
\papersize default
|
||
\use_geometry false
|
||
\use_amsmath 1
|
||
\use_esint 1
|
||
\use_mhchem 1
|
||
\use_mathdots 1
|
||
\cite_engine basic
|
||
\use_bibtopic false
|
||
\use_indices false
|
||
\paperorientation portrait
|
||
\suppress_date false
|
||
\use_refstyle 1
|
||
\index Index
|
||
\shortcut idx
|
||
\color #008000
|
||
\end_index
|
||
\secnumdepth 3
|
||
\tocdepth 3
|
||
\paragraph_separation indent
|
||
\paragraph_indentation default
|
||
\quotes_language english
|
||
\papercolumns 1
|
||
\papersides 1
|
||
\paperpagestyle default
|
||
\tracking_changes false
|
||
\output_changes false
|
||
\html_math_output 0
|
||
\html_css_as_file 0
|
||
\html_be_strict false
|
||
\end_header
|
||
|
||
\begin_body
|
||
|
||
\begin_layout Title
|
||
Cg/HLSL libretro shader tutorial
|
||
\end_layout
|
||
|
||
\begin_layout Author
|
||
Hans-Kristian Arntzen, Daniel De Matteis
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset CommandInset toc
|
||
LatexCommand tableofcontents
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Section
|
||
Introduction
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
This document is for a (fresh) shader developer that wants to develop shader
|
||
programs for use in various emulators/games.
|
||
Shader programs run on your GPU, and thus enables very sophisticated effects
|
||
to be performed on the picture which might not be possible in real-time
|
||
on the CPU.
|
||
Some introduction to shader programming in general is given, so more experience
|
||
d developers that only need reference for the specification may just skip
|
||
ahead.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Current emulators that support the specification explained here to a certain
|
||
degree are:
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
RetroArch
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
RetroArch
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
SNES9x
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
SNES9x Win32
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
There are three popular shader languages in use today:
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
HLSL
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
HLSL (High-Level Shading Language, Direct3D)
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
GLSL
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
GLSL (GL Shading Language, OpenGL)
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
Cg
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Cg (HLSL/GLSL, nVidia)
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The spec is for the
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
Cg
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Cg shading language developed by nVidia.
|
||
It
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
wraps
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
around
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
OpenGL
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
OpenGL and
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
HLSL
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
HLSL to make shaders written in Cg quite portable.
|
||
It is also the shading language implemented on the
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
PlayStation3
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
PlayStation3, thus increasing the popularity of it.
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
The rendering pipeline
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
With shaders you are able to take control over a large chunk of the GPUs
|
||
inner workings by writing your own programs that are uploaded and run on
|
||
the GPU.
|
||
In the old days, GPUs were a big black box that was highly configurable
|
||
using endless amount of API calls.
|
||
In more modern times, rather than giving you endless amounts of buttons,
|
||
you are expected to implement the few «buttons» you actually need, and
|
||
have a streamlined API.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The rendering pipeline is somewhat complex, but we can in general simplify
|
||
it to:
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
Vertex processing
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
Rasterization
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
Fragment processing
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
Framebuffer blend
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We are allowed to take control of what happens during vertex processing,
|
||
and fragment processing.
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
A Cg/HLSL program
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
If you were to process an image on a CPU, you would most likely do something
|
||
like this:
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
for (unsigned y = 0; y < height; y++) {
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
for (unsigned x = 0; x < width; x++)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_pixel[y][x] = process_pixel(in_pixel[y][x], y, x);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
We quickly realize that this is highly serial and slow.
|
||
We see that out_pixel[y][x] isn't dependent on out_pixel[y + k][x + k],
|
||
so we see that we can parallelize quite a bit.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Essentially, we only need to implement process_pixel() as a single function,
|
||
which is called thousands, even millions of time every frame.
|
||
The only purpose in life for process_pixel() is to process an input, and
|
||
produce an output.
|
||
No state is needed, thus, a
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
pure
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
function in computer science terms.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
For the Cg program, we need to implement two different functions.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
main_vertex() takes care of transforming every incoming vertex from camera
|
||
space down to clip space.
|
||
This essentially means projection of 3D (coordinates on GPU) down to 2D
|
||
(your screen)
|
||
\begin_inset Foot
|
||
status collapsed
|
||
|
||
\begin_layout Plain Layout
|
||
Since we're dealing with old school emulators here, which are already 2D,
|
||
the vertex shading is very trivial.
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
.
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Vertex shaders get various coordinates as input, and uniforms.
|
||
Every vertex emitted by the emulator is run through main_vertex which calculate
|
||
s the final output position
|
||
\begin_inset Foot
|
||
status collapsed
|
||
|
||
\begin_layout Plain Layout
|
||
For our emulators this is just 4 times, since we're rendering a quad on
|
||
the screen.
|
||
3D games would obviously have a lot more vertices.
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
.
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
While coordinates differ for each invocation, uniforms are constant throughout
|
||
every call.
|
||
Think of it as a global variable that you're not allowed to change.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Vertex shading can almost be ignored altogether, but since the vertex shader
|
||
is run only 4 times, and the fragment shader is run millions of times per
|
||
frame, it is a good idea to precalculate values in vertex shader that can
|
||
later be used in fragment shader.
|
||
There are some limitiations to this which will be mentioned later.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
main_fragment() takes care of calculating a pixel color for every single
|
||
output pixel on the screen.
|
||
If you're playing at 1080p, the fragment shader will have to be run 1920
|
||
* 1080 times! This is obviously straining on the GPU unless the shader
|
||
is written efficiently.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Obviously, main_fragment is where the real action happens.
|
||
For many shaders we can stick with a
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
dummy
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
vertex shader which does some very simple stuff.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The fragment shader receives a handle to a texture (the game frame itself),
|
||
and the texture coordinate for the current pixel, and a bunch of uniforms.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
A fragment shader's final output is a color, simple as that.
|
||
Processing ends here.
|
||
\end_layout
|
||
|
||
\begin_layout Section
|
||
Hello World
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We'll start off with the basic vertex shader.
|
||
No fancy things are being done.
|
||
You'll see a similiar vertex shader in most of the Cg programs out there
|
||
in the wild.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
void main_vertex(
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform float4x4 modelViewProj,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float2 out_tex : TEXCOORD
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_pos = mul(modelViewProj, pos);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_color = color;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_tex = tex;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
This looks vaguely familiar to C, and it is.
|
||
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
Cg
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Cg stands for
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
C for graphics
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
after all.
|
||
We notice some things are happening, notable some new types.
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Cg types
|
||
\end_layout
|
||
|
||
\begin_layout Subsubsection
|
||
Float4
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
float4 is a vector type.
|
||
It contains 4 elements.
|
||
It could be colors, positions, whatever.
|
||
It's used for vector processing which the GPUs are extremely efficient
|
||
at.
|
||
\end_layout
|
||
|
||
\begin_layout Subsubsection
|
||
Semantics
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We see various semantics.
|
||
The POSITION semantic means that the variable is tied to vertex coordinates.
|
||
We see that we have an input POSITION, and an output (out) POSITION.
|
||
We thus transform the input to the output with a matrix multiply with the
|
||
current model-view projection.
|
||
Since this matrix is the same for every vertex, it is a uniform.
|
||
Remember that the variable names DO matter.
|
||
modelViewProj has to be called exactly that, as the emulator will pass
|
||
the MVP to this uniform.
|
||
It is in the specification.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Since we have semantics for the POSITION, etc, we can call them whatever
|
||
we want, as the Cg environment figures out what the variables mean.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The transformation happens here:
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_pos = mul(modelViewProj, pos);
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The COLOR semantic isn't very interesting for us, but the example code in
|
||
nVidias Cg documentation includes it, so we just follow along.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
TEXCOORD is the texture coordinate we get from the emulator, and generally
|
||
we just pass it to the fragment shader directly.
|
||
The coordinate will then be
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
linearly interpolated
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
across the fragments.
|
||
More complex shaders can output (almost) as many variables they want, that
|
||
will be linearily interpolated for free to the fragment shader.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We also need a fragment shader to go along with the vertex shader, and here's
|
||
a basic shader that only outputs the pixel as-is.
|
||
This is pretty much the result you'd get if you didn't run any shader (fixed-fu
|
||
nction) at all.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 main_fragment(uniform sampler2D s0 : TEXUNIT0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD) : COLOR
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return tex2D(s0, tex);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
This is arguably simpler than the vertex shader.
|
||
Important to notice are:
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
sampler2D is a handle to a texture in Cg.
|
||
The semantic here is TEXUNIT0, which means that it refers to the texture
|
||
in texture unit 0.
|
||
This is also part of the specification.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
float2 tex : TEXCOORD is the interpolated coordinate we received from the
|
||
vertex shader.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
tex2D(s0, tex); simply does texture lookup and returns a COLOR, which is
|
||
emitted to the framebuffer.
|
||
Simple enough.
|
||
Practically every fragment does more than one texture lookup.
|
||
For example, classic pixel shaders look at the neighbor pixels as well
|
||
to determine the output.
|
||
But where is the neighbor pixel? We'll revise the fragment shader and try
|
||
to make a really blurry shader to demonstrate.
|
||
We now need to pull up some uniforms.
|
||
We need to know how to modify our tex coordinates so that it points to
|
||
a neighbor pixel.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
struct input
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 video_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 texture_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 output_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float frame_count;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
};
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 main_fragment(uniform sampler2D s0 : TEXUNIT0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform input IN, float2 tex : TEXCOORD) : COLOR
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 result = float4(0.0);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float dx = 1.0 / IN.texture_size.x;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float dy = 1.0 / IN.texture_size.y;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Grab some of the neighboring pixels and
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// blend together for a very mushy blur.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(-dx, -dy));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(dx, -dy));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(0.0, 0.0));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(-dx, 0.0));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return result / 4.0;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Here we use IN.texture_size to determine the the size of the texture.
|
||
Since GL maps the whole texture to the interval [0.0, 1.0], 1.0 / IN.texture_size
|
||
means we get the offset for a single pixel, simple enough.
|
||
Almost every shader uses this.
|
||
We can calculate these offsets in vertex shader to improve performance
|
||
since the coordinates are linearily interpolated anyways, but that is for
|
||
another time ...
|
||
;)
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Putting it together
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The final runnable product is a single .cg file with the main_vertex and
|
||
main_fragment functions added together.
|
||
Not very complicated.
|
||
For the icing on the cake, you should add a license header.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
/* Stupid blur shader.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
Author: Your friendly neighbor.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
License: We don't have those things!
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
*/
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
struct input
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 video_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 texture_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 output_size;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float frame_count;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
};
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
void main_vertex(
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform float4x4 modelViewProj,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float2 out_tex : TEXCOORD
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_pos = mul(modelViewProj, pos);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_color = color; out_tex = tex;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 main_fragment(uniform sampler2D s0 : TEXUNIT0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform input IN, float2 tex : TEXCOORD) : COLOR
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 result = float4(0.0);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float dx = 1.0 / IN.texture_size.x;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float dy = 1.0 / IN.texture_size.y;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Grab some of the neighboring pixels and blend
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// together for a very mushy blur.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(-dx, -dy));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(dx, -dy));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(0.0, 0.0));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
result += tex2D(s0, tex + float2(-dx, 0.0));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return result / 4.0;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Result
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
begin{figure}
|
||
\backslash
|
||
begin{centering}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset Graphics
|
||
filename supermetroid.jpg
|
||
lyxscale 50
|
||
scale 30
|
||
clip
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
caption{The result of the shader code.}
|
||
\backslash
|
||
end{centering}
|
||
\backslash
|
||
end{figure}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
As you can see, it's not a practical shader, but it shows the blurring effect
|
||
to the extreme.
|
||
\end_layout
|
||
|
||
\begin_layout Section
|
||
Expanding further
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Lookup textures
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We'll first mention a very popular feature among RetroArch users the ability
|
||
to access external textures.
|
||
This means we have several samplers available for use.
|
||
In the config file, we define the textures as so:
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
textures = "foo;bar"
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
foo = path_foo.png
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
bar = bar_foo.png
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
foo_linear = true # Linear filtering for foo.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
bar_linear = true # Linear filtering for bar.
|
||
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
RetroArch PS3 uses PNG as the main format, RetroArch can use whatever if
|
||
Imlib2 support is compiled in.
|
||
If not, it's restricted to lop-left ordered, non-RLE TGA.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
From here on,
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
foo
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
and
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
bar
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
can be found as uniforms in the shaders.
|
||
The texture coordinates for the lookup texture will be found in TEXCOORD1.
|
||
This can simply be passed along with TEXCOORD0 in the vertex shader as
|
||
we did with TEXCOORD0.
|
||
Here we make a fragment shader that blends in two background picture at
|
||
a reduced opacity.
|
||
Do NOT assign lookup textures to a certain TEXUNIT, Cg will assign a fitting
|
||
texture unit to the sampler.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 main_fragment(uniform sampler2D s0 : TEXUNIT0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform sampler2D foo, uniform sampler2D bar,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD0, float2 tex_lut : TEXCOORD1) : COLOR
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 bg_sum = (tex2D(foo, tex_lut) + tex2D(bar, tex_lut)) * 0.15;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return lerp(tex2D(s0, tex), bg_sum, bg_sum.a); // Alpha blending.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Here's an example of what can be achieved using borders (which are just
|
||
a simple lookup texture):
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
begin{figure}
|
||
\backslash
|
||
begin{centering}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset Graphics
|
||
filename shader-rarch-2.jpg
|
||
lyxscale 50
|
||
scale 50
|
||
clip
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
caption{A shader making use of a lookup texture for the purpose of drawing
|
||
a background border.}
|
||
\backslash
|
||
end{centering}
|
||
\backslash
|
||
end{figure}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Multipass
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
It is sometimes feasible to process an effect in several steps.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
shaders = 2
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
shader0 = pass1.cg
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
shader1 = pass2.cg
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
scale_type0 = source
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
scale0 = 2.0
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
filter_linear0 = true
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
filter_linear1 = false
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Subsection
|
||
Game-aware shaders
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
This is a new and exciting feature.
|
||
It allows shaders to grab data from the emulator state itself, such as
|
||
RAM data.
|
||
This is only implemented for SNES so far, but the idea is quite extendable
|
||
and portable.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The basic idea is that we capture RAM data in a certain way (semantic if
|
||
you will) from the SNES, and pass it as a uniform to the shader.
|
||
The shader can thus act on game state in interesting ways.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
As a tool to show this feature, we'll focus on replicating the simple tech
|
||
demo shown on YouTube:
|
||
\begin_inset CommandInset href
|
||
LatexCommand href
|
||
target "http://www.youtube.com/watch?v=4VzaE9q735k"
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
What happens is that when Mario jumps in the water, the screen gets a
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
watery
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
effect applied to it, with a rain lookup texture, and a wavy effect.
|
||
When he jumps out of the water, the water effect slowly fades away.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We thus need to know two things:
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
Is Mario currently in water or not?
|
||
\end_layout
|
||
|
||
\begin_layout Itemize
|
||
If not, how long time was it since he jumped out?
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Since shaders do not have state associated with it, we have to let the environme
|
||
nt provide the state we need in a certain way.
|
||
We'll call this concept a semantic.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
To capture a RAM value directly, we can use the
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
capture
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
semantic.
|
||
To record the time when the RAM value last changed, we can use the
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
transition
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
semantic.
|
||
We obviously also need to know where in RAM we can find this information.
|
||
Luckily, the guys over at SMW Central know the answer:
|
||
\begin_inset CommandInset href
|
||
LatexCommand href
|
||
target "http://www.smwcentral.net/?p=map&type=ram"
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
We see:
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
$7E:0075, byte, Flag, Player is in water flag.
|
||
#$00 = No; #$01 = Yes.
|
||
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Bank $7E and $7F are mapped to WRAM $0000-$FFFF and $10000-$1FFFF respectively.
|
||
Thus, our WRAM address is $0075.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
In the config file, we can now set up the uniforms we'll want to be captured
|
||
in the config file.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
imports = "mario_water;mario_water_time"
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_semantic = capture
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
# Capture the RAM value as-is.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_wram = 0075
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
# This value is hex!
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_time_semantic = transition
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
# Capture the frame count when this variable last changed.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
# Use with IN.frame_count, to create a fade-out effect.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_time_wram = 0075
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
The amount of possible
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
semantics
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
are practically endless.
|
||
It might be worthwhile to attempt some possibility to run custom code that
|
||
keeps track of the shader uniforms in more sophisticated ways later on.
|
||
Do note that there is also a %s_mask value which will let you bitmask the
|
||
RAM value to check for bit-flags more easily.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Now that we got that part down, let's work on the shader design.
|
||
In the fragment shader we simply render both the full water effect, and
|
||
the «normal» texture, and let a
|
||
\begin_inset Quotes eld
|
||
\end_inset
|
||
|
||
blend
|
||
\begin_inset Quotes erd
|
||
\end_inset
|
||
|
||
variable decide.
|
||
We can say that 1.0 is full water effect, 0.0 is no effect.
|
||
We can start working on our vertex shader.
|
||
We will do something useful here for once.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
struct input
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float frame_count;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
};
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
void main_vertex(
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_pos : POSITION,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform float4x4 modelViewProj,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float4 out_color : COLOR,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float2 out_tex : TEXCOORD0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex1 : TEXCOORD1,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float2 out_tex1 : TEXCOORD1,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Even if the data should have been int,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Cg doesn't seem to
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform float mario_water,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// support integer uniforms
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform float mario_water_time,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform input IN,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Blend factor is passed to fragment shader.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// We'll output the same value in every vertex,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// so every fragment will get the same value
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// for blend_factor since there is nothing to interpolate.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out float blend_factor )
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_pos = mul(modelViewProj, pos);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_color = color;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_tex = tex;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
out_tex1 = tex1;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float transition_time = 0.5 *
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
(IN.frame_count – mario_water_time) / 60.0;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// If Mario is in the water ($0075 != 0),
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// it's always 1 ...
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
if (mario_water > 0.0)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
blend_factor = 1.0;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Fade out from 1.0 towards 0.0 as
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// transition_time grows larger.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
else
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
blend_factor = exp(-transition_time);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
All fine and dandy so far, now we just need to use this blend_factor in
|
||
our fragment shader somehow ...
|
||
Let's move on to the fragment shader where we blend.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float apply_wave(float2 pos, float2 src, float cnt)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 diff = pos - src;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float dist = 300.0 * sqrt(dot(diff, diff));
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
dist -= 0.15 * cnt;
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return sin(dist);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// Fancy shizz to create a wave.
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 water_texture(float4 output, float2 scale, float cnt)
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float res = apply_wave(scale, src0, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src1, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src2, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src3, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src4, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src5, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
res += apply_wave(scale, src6, cnt);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return output * (0.95 + 0.012 * res);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 main_fragment
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
(
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform input IN,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float2 tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
uniform sampler2D rain, float2 tex1 : TEXCOORD1,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
in float blend_factor // Passed from vertex
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
) : COLOR
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
{
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 water_tex = water_texture(tex2D(s0, tex), tex1, IN.frame_count);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 normal_tex = tex2D(s0, tex);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
float4 rain_tex = tex2D(rain, tex1);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// First, blend normal and water texture together,
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
// then add the rain texture with alpha blending on top
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
return lerp(lerp(normal_tex, water_tex, blend_factor),
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
rain_tex, rain_tex.a * blend_factor * 0.5);
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Subsubsection
|
||
RetroArch config file
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset listings
|
||
inline false
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
shaders = 1
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
shader0 = mario.cg
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
filter_linear0 = true
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
imports = "mario_water;mario_water_time"
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_semantic = capture
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_time_semantic = transition
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_wram = 0075
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
mario_water_time_wram = 0075
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
textures = rain
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
rain = rain.tga
|
||
\end_layout
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
rain_linear = true
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Subsubsection
|
||
How to test when developing for RetroArch?
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
To develop these kinds of shaders, I'd recommend using
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
RetroArch
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
RetroArch w/
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
Cg
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
Cg support, and a debugging tool for your emulator of choice to peek at
|
||
RAM values (build it for
|
||
\begin_inset Index idx
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
bSNES
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
bSNES yourself with options=debugger).
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
After written, the shader should translate nicely over to RetroArch with
|
||
some slight changes to the config.
|
||
\end_layout
|
||
|
||
\begin_layout Subsubsection
|
||
Results
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
Here are some screenshots of the mario effect (in Super Mario World SNES)
|
||
we developed.
|
||
Obviously this is a very simple example showing what can be done.
|
||
It's not confined to overlays.
|
||
The imagination is the limit here.
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
begin{figure}
|
||
\backslash
|
||
begin{centering}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset Graphics
|
||
filename mario-water1.jpg
|
||
lyxscale 50
|
||
scale 50
|
||
clip
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
caption{Super Mario World prior to Mario jumping in water.}
|
||
\backslash
|
||
end{centering}
|
||
\backslash
|
||
end{figure}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
begin{figure}
|
||
\backslash
|
||
begin{centering}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset Graphics
|
||
filename mario-water2.jpg
|
||
lyxscale 50
|
||
scale 50
|
||
clip
|
||
|
||
\end_inset
|
||
|
||
|
||
\begin_inset ERT
|
||
status open
|
||
|
||
\begin_layout Plain Layout
|
||
|
||
|
||
\backslash
|
||
caption{Super Mario World with a game aware shader applying a LUT texture
|
||
as soon as Mario jumps into the water.}
|
||
\backslash
|
||
end{centering}
|
||
\backslash
|
||
end{figure}
|
||
\end_layout
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\begin_layout Standard
|
||
\begin_inset CommandInset index_print
|
||
LatexCommand printindex
|
||
type "idx"
|
||
|
||
\end_inset
|
||
|
||
|
||
\end_layout
|
||
|
||
\end_body
|
||
\end_document
|