Ren'Py color gradient in screen

maximusleroy

Member
Aug 26, 2016
164
803
Is there a better way to apply a color gradient shader to a screen?
What I tried works, but it's not pretty. It also only correctly works when all elements are the same size.

I tried giving the transform to the vbox, but it repeated the gradient on each child element instead.
Python:
init:
    transform shader_menu(step, max):
        shader "example.gradient"
        u_color_top (0.984+0.016/max*step, 0.565-0.102/max*step, 0.208+0.694/max*step, 1.0)
        u_color_bottom (0.984+0.016/max*(step+1), 0.565-0.102/max*(step+1), 0.208+0.694/max*(step+1), 1.0)

init python:
    renpy.register_shader("example.gradient", variables="""
        uniform vec4 u_color_top;
        uniform vec4 u_color_bottom;
        uniform vec2 u_model_size;
        varying float v_gradient_done;
        attribute vec4 a_position;
    """, vertex_300="""
        v_gradient_done = a_position.y / u_model_size.y;
    """, fragment_300="""
        float gradient_done = v_gradient_done;
        gl_FragColor *= mix(u_color_top, u_color_bottom, gradient_done);
    """)

screen navigation():
    vbox:
        spacing 5
        style_prefix "navigation"
        imagebutton auto "start_%s" background "buttons_bg" action Start() at shader_menu(0,7)
        imagebutton auto "load_%s" background "buttons_bg" action ShowMenu("load") at shader_menu(1,7)
        imagebutton auto "prefs_%s" background "buttons_bg" action ShowMenu("preferences") at shader_menu(2,7)
        imagebutton auto "about_%s" background "buttons_bg" action ShowMenu("about") at shader_menu(3,7)
        imagebutton auto "help_%s" background "buttons_bg" action ShowMenu("help") at shader_menu(4,7)
        imagebutton auto "creds_%s" background "buttons_bg" action ShowMenu("credits") at shader_menu(5,7)
        imagebutton auto "quit_%s" background "buttons_bg" action Quit(confirm=not main_menu) at shader_menu(6,7)
 
Last edited:

maximusleroy

Member
Aug 26, 2016
164
803
I was able to generalize the shader some more, but I still haven't found a way to apply it to a whole screen or a block of displayables without the gradient repeating on each child element.
Python:
init:
    transform shader_menu(cur_step=0, max_step=1, color1=Color("#fc8e1d"), color2=Color("#ffa2d7")):
        shader "color.gradient"
        u_step cur_step
        u_max max_step
        u_color_top color1.rgba
        u_color_bottom color2.rgba

init python:
    renpy.register_shader("color.gradient", variables="""
        uniform vec4 u_color_top;
        uniform vec4 u_color_bottom;
        uniform vec2 u_model_size;
        uniform float u_step;
        uniform float u_max;
        varying vec4 v_color_top;
        varying vec4 v_color_bottom;
        varying float v_gradient_done;
        attribute vec4 a_position;
    """, vertex_300="""
        vec4 color_offset = (u_color_bottom - u_color_top) / u_max;

        v_color_top = u_color_top + color_offset * u_step;
        v_color_bottom = v_color_top + color_offset;
        v_gradient_done = a_position.y / u_model_size.y;
    """, fragment_300="""
        gl_FragColor *= mix(v_color_top, v_color_bottom, v_gradient_done);
    """)
 
Last edited:

osanaiko

Engaged Member
Modder
Jul 4, 2017
2,547
4,631
Shaders? In *my* Renpy? Literally shaking my head right now.

Anyway, to be honest this is one of the first times I've seen shaders even mentioned in these forums. And I'm not really sure what this specific shader is supposed to do... If you're looking to have a smooth color gradient background for a screen frame then you can achieve the same effect using 2d image backgrounds.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,964
16,204
[...] but I still haven't found a way to apply it to a whole screen or a block of displayables without the gradient repeating on each child element.
What version of Ren'Py are you using and what changes have you done with the configuration ?

I tried to replicate the error, in order to understand the issue, and then possibly find why it happen and how to fix it, but if I try your code with a 8.1.0, and a fresh new project, I get this error:
"Exception: Shader ('color.gradient', 'renpy.geometry', 'renpy.texture') has not been given uniform u_max."
And if I try with a 7.5.3, again with a fresh new project, I get this one:
Exception: Shader (u'color.gradient', u'renpy.geometry', 'renpy.texture') has not been given uniform u_step.
 

maximusleroy

Member
Aug 26, 2016
164
803
What version of Ren'Py are you using and what changes have you done with the configuration ?

I tried to replicate the error, in order to understand the issue, and then possibly find why it happen and how to fix it, but if I try your code with a 8.1.0, and a fresh new project, I get this error:
"Exception: Shader ('color.gradient', 'renpy.geometry', 'renpy.texture') has not been given uniform u_max."
And if I try with a 7.5.3, again with a fresh new project, I get this one:
Exception: Shader (u'color.gradient', u'renpy.geometry', 'renpy.texture') has not been given uniform u_step.
I'm using 8.1.1

I found a way to fix my problem, if you add a mesh all the child elements are rendered as one

Python:
init:
    transform gradient_menu(color1=Color("#fc8e1d"), color2=Color("#ffa2d7")):
        mesh(2,2)
        shader "gradient.vertical_down"
        u_color1 color1.rgba
        u_color2 color2.rgba

init python:
    renpy.register_shader("gradient.vertical_down", variables="""
        uniform vec4 u_color1;
        uniform vec4 u_color2;
        uniform vec2 u_model_size;
        varying float v_position;
        attribute vec4 a_position;
    """, vertex_300="""
        v_position = a_position.y;
    """, fragment_300="""
        gl_FragColor *= mix(u_color1, u_color2, v_position / u_model_size.y);
    """)
Then you just need to add the transform to the outer block like so
Python:
screen navigation():

    vbox at gradient_menu():
        ##buttons go here
Next step it do figure out how to make animations with them.
 
Last edited:

maximusleroy

Member
Aug 26, 2016
164
803
There is 2 ways to make an animation with a shader.
First is by using u_time inside the shader
Second is inside the transform where the shader is called by utilizing

Here would be the shaders in those 2 scenarios.
You don't have permission to view the spoiler content. Log in or register now.

The main differences will be in the transform.
If the shader uses u_time then the following lines will need to be added, where x is 1/FPS or 0 for as fast as Ren'Py is capable of.
Otherwise the shader will refresh at 5 FPS when no other displayables needs to be redrawn.
Python:
pause x
repeat
Below are the transform using u_time and using warpers.
In both cases a gradient should be moving right to left (or left to right if speed is negative)
You don't have permission to view the spoiler content. Log in or register now.

The main limitation of using u_time seems to be that there is no way to stop the shader animation (other than removing it at an other point in the transform. Be it by using shader "-shader.name" or using another shader instead)
 
Last edited:
  • Wow
Reactions: gojira667