-
Notifications
You must be signed in to change notification settings - Fork 30
Shaders
This tutorial will describe how shaders are used in JSFML, but not how they work or how to write them, since there are entire books about the topic.
Shaders are programs that are typically executed on the GPU during the rendering process, immediately before something is drawn. Thus, they can be used to post-process the scene for advanced effects. A much more detailed description is available in the Wikipedia article about shaders.
JSFML supports vertex and pixel shaders written in GLSL. The supported GLSL version depends on the OpenGL version used to create the rendering context.
Shaders are represented by the Shader class. A shader consists of a vertex shader, a fragment shader or both. It offers methods to load shaders from a file or stream like the Texture class, but also for loading them directly from a string that contains the GLSL code.
After loading, JSFML directly attempts to compile the shader on the GPU. This only succeeds if shaders are supported by the current hardware, which can be tested using the static method [isAvailable](http://jsfml.org/javadoc/org/jsfml/graphics/Shader.html#isAvailable(\)), the GLSL version of the shader is supported in the current OpenGL context and - of course - if the program is syntactically correct.
A shader can be applied when drawing a Drawable by attaching it to the render states. The RenderTarget interface also provides a convenience overload of the [draw](http://jsfml.org/javadoc/org/jsfml/graphics/RenderTarget.html#draw(org.jsfml.graphics.Drawable, org.jsfml.graphics.RenderStates)) method, which will create the default render states with the given shader.
The Shader class also provides write access to uniform variables within the shader program with the [setParameter](http://jsfml.org/javadoc/org/jsfml/graphics/Shader.html#setParameter(java.lang.String, float)) methods. This method has numerous overloads that can be used for the respective uniform variable types.
To present a common technique to apply post effects to a scene in JSFML, we will create a simple grayscale fragment shader and apply it to a scene.
The idea is simple: instead of rendering everything to the render target directly, we render it to a RenderTexture. We then create a sprite for the resulting texture and draw it to the render target using the shader.
The following GLSL program implements a simple NTSC grayscale fragment shader:
void main()
{
//Compute level of gray
float gray = dot(gl_Color.rgb, vec3(0.299, 0.587, 0.114));
//Write destination color
gl_FragColor = vec4(gray, gray, gray, gl_Color.a);
}
We'll assume that we save this program into a file called grayscale.frag
.
In this example, we draw the whole scene to a render texture and apply the grayscale shader on the resulting texture using a sprite:
// ... (create window)
//Ensure that shaders are available on this system
if(!Shader.isAvailable()) {
System.err.println("Sorry, your hardware does not support shaders!");
return;
}
//Create the shader and attempt to load the program
Shader grayscale = new Shader();
try {
grayscale.loadFromFile(new File("grayscale.frag"), Shader.Type.FRAGMENT);
} catch(IOException|ShaderSourceException ex) {
ex.printStackTrace();
return;
}
//Attempt to create the render texture
RenderTexture rtex = new RenderTexture();
try {
rtex.create(window.getSize().x, window.getSize().y);
} catch(TextureCreationException ex) {
ex.printStackTrace();
return;
}
//Create a sprite with the result texture
Sprite resultSprite = new Sprite(rtex.getTexture());
//Main loop
while(window.isOpen()) {
//Draw the scene to the render texture
rtex.clear();
// .... (draw scene to rtex, not the window)
rtex.display();
//Draw the result sprite on the window using the grayscale shader
window.clear();
window.draw(resultSprite, grayscale);
window.display();
// ... (event handling)
}