INTRODUCTION TO SHADERS

Introduction to shaders on iOS9+

Posted on Posted in Game Development

Intro

This article shows how to write basic shaders in the SpriteKit. It’s split into two parts: first we play, then we learn. It also contains information how to use SKAttribute / SKAttributeValue classes that were added in iOS SDK 10.0.

 

Prepare the project

Let’s get quick and dirty.

  • Open XCode 8 and create a new project from template: iOS > Game.
  • Open the GameScene.sks and remove the label in the center of the screen.
  • Download this and put it inside Assets.xcassets

  • Name it „Trees”
  • Open the GameScene.m
    • remove all instance variables
    • remove all methods

 

The Fragment Shader

Now we create an empty fragment shader In XCode:

  • In the Project Navigator select Supporting Files
  • Choose: File > New > File…
  • Select: Other > Empty
  • Name it „myShader.fsh” and press Create.
  • Put this inside:

Above fragment shader does nothing perceptible. Quick explanation:

  • void main() – this function gets called for each pixel of the sprite and outputs color for that pixel.
  • Gets input data from sourrounding globals and must set the gl_FragColor variable.
  • vec2, vec3, vec4 are the types similar to C’s: float array[2], float array[3], float array[4]
  • u_texture is a texture ID. Leave it alone 🙂
  • v_tex_coord is a vec2 which contains our current position in texture.
  • texture2D( tex , p ) is a function that returns color from texture tex in point p as vec4 (which contains rgba)
  • gl_FragColor is an output color, we must assign it a vec4

 

Loading code

What’s left is the loading code.

  • Open the GameScene.m
    • add method -didMoveToView:

Ensure that myShader.fsh figures in ProjectFile > Target > Build Phases > Copy Bundle Resources!

You may now run the project on the iOS device. There shall be no errors in the XCode’s console and you should see a screen similar to this below:

Let’s play a bit!

Now is the fun part. We’ll replace the shader’s main function.

Color with red with alpha preservation

Scale down by 2x

Swap colors after 1 second

Colorize over time

Waves

 

New Attributes

At WWDC 2016 Apple introduced an important update to SpriteKit – the SKAttribute / SKAttributeValue classes.

Before this SDK update, if we wanted to pass custom parameters into the shader program, we had to pass the data through a uniform value. This had two serious drawbacks:

  • every uniform change caused shader recompilation
  • shader program handled every sprite in the exact same way

For example: if we wanted to dye a group of sprites red, and one of them blue, we had two ways. First is to create two separate SKShader instances and change our custom „myColor” uniform. Second is to make one shader instance and change its uniform which causes a recompilation. Both ways cannot be drawn on same pass and the second one requires complex management code.

SDK 10.0 introduced the SKAttribute and SKAttributeValue classes. These two allow (finally!) passing data to the shader programs without recompilation. The usage algorithm is simple:

  • The shader part:
    1. Create a shader program (SKShader)
    2. Create an array of SKAttributes
    3. Assign array of attributes to the shader program
  • The sprite part:
    1. Assign the shader program to a sprite
    2. Assign a dictionary of SKAttributeValues

 

Example with attributes

In the last example, we’ll add two more sprites. Every one of them will have the same shader program and will differ only in attributes. Let’s modify the -didMoveToView: inGameScene.m:

… and the shader program:

… and see the parameterized result!

Caveats

  • The shader’s source code is typically loaded from a “.fsh” file to a plain NSString. This code must compile on the target device during the runtime (no buildtime checks!). Older devices may use different version of OpenGL ES so beware GLSL syntax differences! In Raft Challenge’s case there was the need to replace “__constant” (valid in OpenGL ES 3.0) to “const” for OpenGL ES 2.0.
  • It’s a good idea to keep a reference to SKShader object somewhere and reuse it as frequently as needed in order to avoid visible framerate drop. While allocation and shader compilation takes less than 1/60 sec, it may become a huge burden in render loop.
  • When using SpriteKit’s Texture Atlases be cautious of vtexcoord. XCode may rotate some textures which swap X and Y axis. Color modification is safe, geometry is not.

Summary

Summarizing, we learned by examples how to use fragment shaders in the Sprite Kit. Beyond basics, we added parameters to sprites themselves so our shader program can render every instance in a different way without any performance loss.

  • Complete project is available for a download here.

 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *