# Text Shaders

Ren'Py contains a text shader system that makes it possible to control how Ren'Py displays text. When enabled, the text shader system uses Model-Based Rendering to render two triangles for each unicode character. Shader parts, either specified from the creator or generated by Ren'Py, are applied to the model, and can change how text is displayed.

The text shader documentation is in three parts:

1. How to use text shaders

2. What text shaders are included in Ren'Py

3. How to create new text shaders

Note that while text shaders are intended to be easily used by game creators, making your own text shaders requires some knowledge of GLSL, the OpenGL Shading Language, as well as Model-Based Rendering, and hence is more advanced than most Ren'Py features.

## Using Text Shaders

There are three ways to use text shaders:

Default Text Shader The first is to set the default text shader, using `config.default_textshader`.

    define config.default_textshader = "wave:10"

When set this way, the text shader will be used for all text that does not specify a text shader. It will also be combined with text shaders that include the default text shader, which is most of them.

Generally, the default textshader should take care of slow text and shouldn't add more complicated effects.

Styles The second way to use text shaders is to set the `textshader` style property, either directly or in one of the many ways provided by Ren'Py to set styles.

    style default:
        textshader "dissolve"

    define goldfinger = Character("Goldfinger", what_textshader="linetexture:gold.png")

    screen purchase():
        vbox:
            text "Buy the DLC for more!" textshader "wave"
            textbutton "Buy Now":
                text_textshader "wave" action iap.Purchase("dlc")

Text Tags The third way to use text shaders is to use the `appropriate text tag` to change the look of a portion of text.

    "What's this? A letter from {shader=zoom}out of nowhere{/shader}?"

Note A text block should either use text shaders or not - mixing is not supported. For example, you should set `config.default_textshader` or `textshader` style property if you use the text tag like above.

### Specifying Text Shaders

Text shaders are specified as strings like:

    "dissolve"
    "jitter:u__jitter=1.0, 3.0"
    "texture:gold.png"

The first part of the string, before the first colon, is the name of the text shader. The rest of the string is a series of uniforms that are passed to the shader, separated by colons. (Uniforms are parameters that are passed to the shader, that can be used to control how the shader works.)

Uniforms can be specified by name followed by =, or the name can be omitted to set each uniform in order. (Omitting the name is not supported in Ren'Py 7.) While internally all uniforms begin with u_, the u_ can be omitted for brevity.

The value of a uniform can be:

- Between 1 and 4 numbers, separated by commas. These can be used with the the float, vec2, vec3, or vec4 types.

- A color, beginning with #. (For example, #f00 or #ff0000 for red.) This creates a vec4 corresponding to that color. This color will be premultiplied by its alpha channel.

- A displayable that will be used as a texture. This creates a sampler2D that can be used to sample the texture.

Uniform values can't be expressions or access variables, though it is possible to use text interpolation to create a string that can be evaluated as a textshader tag or its parameter.

Finally, text shaders can be combined with each other using the | operator. For example:

    "jitter:1.0, 3.0|wave"

This will apply both the jitter and wave shaders to the text. This only works if the shaders are compatible with each other, and do not use the same uniforms (or use the uniform in a way that is compatible with each other, in which case it takes the value from the last shader in the list).

Unless a textshader has include_default set to False, the default textshader will be combined with the textshader specified in the style or tag.

### Text Shader Callbacks

The `config.textshader_callbacks` variable can be used to set a callback that is run when a text shader is applied. This can be used to customize the text shader based on a preference.

    default persistent.dissolve_text = True

    init python:
        def get_default_textshader():
            if persistent.dissolve_text:
                return "dissolve"
            else:
                return "typewriter"

    define config.default_textshader = "default"
    define config.textshader_callbacks["default"] = get_default_textshader

## Built-In Text Shaders

Ren'Py includes a number of built-in text shaders. These are:

## Creating Text Shaders

Text shaders are GLSL programs that are run on the GPU. These shaders are registered using the renpy.register_text_shader function.

### Variables in Text Shaders

In additions to the uniforms you provided to the text shader (generally beginning with `u__`), Ren'Py makes the following variables available to text shaders. To use a variable in a text shader, it needs to be declared in the variables argument to renpy.register_text_shader.

In addition to these, the model uniforms and attributes are available, with a_position, a_tex_coord, u_time and u_random being particularly useful.

#### Uniforms

`float u_text_depth`

  The depth of the text from the top. The topmost text has a depth of 0.0, the first outline or shadow has a depth of 1.0, the second outline or shadow has a depth of 2.0, and so on.

`float u_text_main`

  If this is 1.0, the text is the main text. If this is 0.0, the text is the outline or shadow of the main text.

`float u_text_max_depth`

  The maximum value of u_text_depth. This is the number of outlines and shadows that will be drawn. When u_text_depth is equal to this value, the texct is the last outline or shadow, which may be useful for drawing backgrounds.

`vec2 u_text_offset`

  The offset of the text from the center of the character. This is in drawable pixels in x, y order.

`float u_text_outline`

  The width of the outline around the text. This is in drawable pixels, and is the distance from the edge of the text to the edge of the outline.

`float u_text_slow_duration`

  The duration of a single slow character, when showing slow text. 0.0 if not showing slow text.

`float u_text_slow_time`

  The time in seconds since the start of the slow text effect. This will only increase until the end of slow text, when it will max out. If the user clicks to terminate slow text, this will max out. It should only be used for slow text - use

`float u_text_to_drawable`

  The ratio of virtual pixels to drawable pixels. This is used to convert from virtual pixels to drawable pixels.

`float u_text_to_virtual`

  The ratio of drawable pixels to virtual pixels. This is used to convert from drawable pixels to virtual pixels.

`sampler2D tex0`

  This texture contains the rendered text at the current depth.

`vec2 res0`

  The resolution of tex0, in drawable pixels.

#### Attributes

When drawing text, each vertex corresponds to a single glyph. Multiple glyphs may have vertices that share locations, but these are passed to the shader as different vertices.

`float a_text_ascent`

  The ascent of the current glyph's font above the baseline, in drawable pixels.

`vec2 a_text_center`

  The position of the center of the glyphs's baseline, in drawable pixels. This is not the center of the rectangle, it's a point on the baseline and around the center of the character.

`float a_text_descent`

  The descent of the current glyph below the baseline, in drawable pixels.

`float a_text_index`

  The index of the glyph being drawn. This is 0 for the first vertex and goes up by one for each vertex.

`vec2 a_text_min_time`

  The minimum time at which any vertex of the glyph should be shown. When showing from left-to-right, this is the time the leftmost vertices should be shown. When the text is meant to be shown instantly but `u_text_slow_duration` is not 0.0, this will be -3600.0.

`vec2 a_text_max_time`

  The maximum time at which any vertex of the glyph should be shown. When showing from left-to-right, this is the time the rightmost vertices should be shown. When the text is meant to be shown instantly but `u_text_slow_duration` is not 0.0, this will be -3600.0.

`float a_text_time`

  The time at which this vertex should be shown. When the text is meant to be shown instantly but `u_text_slow_duration` is not 0.0, this will be -3600.0.

`vec4 a_text_pos_rect`

  The rectangle containing the glyph, in drawable pixels. This is a vec4 with the x, y, width, and height of the rectangle, in drawable pixels. This can be converted to texture coordinates by dividing it by `res0`.

### Pseudo-Glyphs

When drawing outlined text, Ren'Py will creates pseudo-glyphs that cover the start and end of each line. If a line is blank, one pseudo-glyph is created covering the whole line. These pseudo-glyphs are necessary to cover areas where outlines may extend into a line above or below the current line.

### Example

This is an example of a text shader that spins text when shown.

    init python:

        def adjust_extra_slow_time(ts, u__delay, **kwargs):
            """
            Adjusts a text shader's extra slow time to support the spinning text shader.
            """
            ts.extra_slow_time = u__delay

        renpy.register_textshader(
            "spin",
            adjust_function = adjust_extra_slow_time,

            variables = """
            uniform float u__delay;
            uniform float u__offset;
            uniform float u_text_slow_time;
            attribute vec2 a_text_center;
            attribute float a_text_min_time;
            """,

            vertex_50 = """
            float l__angle = clamp((u_text_slow_time - a_text_min_time) / u__delay, 0.0, 1.0) * 2.0 * 3.1415926536;
            float l__sin = sin(l__angle);
            float l__cos = cos(l__angle);

            gl_Position.y -= u__offset;
            gl_Position.xy -= a_text_center;
            gl_Position = vec4(
                gl_Position.x * l__cos - gl_Position.y * l__sin,
                gl_Position.x * l__sin + gl_Position.y * l__cos,
                gl_Position.z,
                gl_Position.w
                );
            gl_Position.xy += a_text_center;
            gl_Position.y += u__offset;
            """,

            u__delay = 1.0,
            u__offset = 0,
        )

It can be used witt the following script:

    define config.default_textshader = "typewriter"

    label start:

        "This is a test of the {shader=spin:0.5:-5}spin{/shader} text shader."