[Solved]Can we apply Shader on a whole layer?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
User avatar
birctreel
Regular
Posts: 52
Joined: Wed Dec 20, 2017 7:17 am
Projects: Knell of st.Godhrkar
Organization: -Andinomie-
Tumblr: birctreel
Location: Beijing, China
Contact:

[Solved]Can we apply Shader on a whole layer?

#1 Post by birctreel »

Hello guys! I've been diving into shader functionalities lately and encountered some issues during my research. I was wondering if anyone knows how to achieve the following goal: to overlay a CRT effect on the whole game screen (applying on the whole master layer).

Progress so far: I've managed to apply shader effects to individual images using Shader, but I'm unsure how to implement this across the entire layer.

Detailed research process:
After studying the insightful posts by user "章鱼" on Zhihu and delving into forum threads on shader research, I came up with an approach: modifying the shader to edit it into a Transform, and then applying it to the Layer.
Links to Zhihu posts:
https://zhuanlan.zhihu.com/p/348922729
https://zhuanlan.zhihu.com/p/349467983

Here are some Shader station codes I've referenced:

https://www.shadertoy.com/view/WdjfDy
https://www.shadertoy.com/view/wllBDM
Below is the Renpy-compatible Shader code I've adapted based on these references:

Code: Select all

init python:
    renpy.register_shader("shadertoy.test", variables="""
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        uniform sampler2D tex1;
    """,fragment_functions="""
        vec2 curve(vec2 uv)
        {
            uv = (uv - 0.5) * 2.0;
            uv *= 1.1;
            uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
            uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);
            uv  = (uv / 2.0) + 0.5;
            uv =  uv *0.92 + 0.04;
            return uv;
        }
    """,
        vertex_300="""
        //v_tex_coord = a_tex_coord;
    """,
 
        fragment_300="""
        //Curve
        vec2 uv = gl_FragCoord.xy / res0.xy;
        uv = curve( uv );
        vec3 col;
 
        // Chromatic
        col.r = texture2D(tex0,vec2(uv.x+0.003,(1-uv.y))).x;
        col.g = texture2D(tex0,vec2(uv.x+0.000,(1-uv.y))).y;
        col.b = texture2D(tex0,vec2(uv.x-0.003,(1-uv.y))).z;
 
        col *= step(0.0, uv.x) * step(0.0, uv.y);
        col *= 1.0 - step(1.0, uv.x) * 1.0 - step(1.0, uv.y);
 
        col *= 0.5 + 0.5*16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y);
        col *= vec3(0.95,1.05,0.95);
 
        //col *= 0.9+0.1*sin(10.0*u_time+uv.y*1000.0);
 
        col *= 0.99+0.01*sin(500.0*u_time);
 
        float comp = smoothstep( 0.2, 0.7, sin(u_time) );
        gl_FragColor = vec4(col,1.0);
    """)
 
init python:
    renpy.register_shader("test44.softlight", variables="""
        uniform sampler2D tex0;
        uniform sampler2D tex1;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;
    """, vertex_300="""
        v_tex_coord = a_tex_coord;
    """, fragment_300="""
        vec4 base = texture2D(tex0, v_tex_coord);
        vec4 blend = texture2D(tex1, v_tex_coord);
        vec4 ctemp = base * blend;
        gl_FragColor = mix(base, ctemp+(base*(1-((1-base)*(1-blend))-ctemp)), 0.5);
    """)
Then, proceed to convert this registered Shader into a Transform effect and apply to image or scene:

Code: Select all

transform shadercrt:
    shader "shadertoy.test"
    	
scene overlay at shadercrt
However, this Transform has a few issues:

1. It can only be applied to individual images and cannot be universally applied to layers or the entire game screen.
2. When applying this effect to images like character portraits with transparent backgrounds, the transparent background turns completely black and becomes unusable.
While going through the Ren'Py documentation, I found code for applying a transform to an entire layer:
https://doc.renpy.cn/zh-CN/displaying_i ... show-layer

But if I use either of those codes:

Code: Select all

camera master at shadercrt
show layer master at shadercrt
It would went wrong like:

Code: Select all

File "renpy/common/000window.rpy", line 114, in _window_auto_callback
    _window_show(auto=True)
  File "renpy/common/000window.rpy", line 69, in _window_show
    renpy.with_statement(trans)
Exception: Shader ('renpy.geometry', 'renpy.solid', 'shadertoy.test') has not been given uniform tex0.


The second approach I thought of involves utilizing Pygame to call shaders. Here, I found a Pygame project that uses shaders:
https://github.com/JingShing/ModernGL-S ... ith-pygame
While I'm not very familiar with Python and Pygame, after examining the code, I noticed that the visual effect defined in this Python code is called 'crt_shader.'
So, how can I apply this 'crt_shader' from Pygame to the entire Ren'Py game interface or a specific layer?

I'm unsure if there's a method to achieve my goal eventually. If it's not possible, I might just have to let go of this idea... (:3√ L)
Last edited by birctreel on Tue Nov 28, 2023 10:29 am, edited 1 time in total.

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2360
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Can we apply Shader on a whole layer?

#2 Post by Ocelot »

Did you check Layer displayables?
https://www.renpy.org/doc/html/displaya ... splayables

They might work for you.
< < insert Rick Cook quote here > >

User avatar
birctreel
Regular
Posts: 52
Joined: Wed Dec 20, 2017 7:17 am
Projects: Knell of st.Godhrkar
Organization: -Andinomie-
Tumblr: birctreel
Location: Beijing, China
Contact:

Re: Can we apply Shader on a whole layer?

#3 Post by birctreel »

Ocelot wrote: Mon Nov 27, 2023 1:32 pm Did you check Layer displayables?
https://www.renpy.org/doc/html/displaya ... splayables

They might work for you.
Hello! Thank you very much for this quick reply! This is really helpful!

So here is my solution:

1. Regarding the issue of image inversion caused by shader code, after investigating thoroughly, I found that the problem lies in:

Code: Select all

vec2 uv = fragCoord.xy / iResolution.xy;//The original code from the Shader site which can be found almost everywhere on the shader site
Usually I turned it into

Code: Select all

vec2 uv = gl_FragCoord.xy / res0.xy;
And this causes the image to invert. The solution is to delete this line and change the 'uv' function to 'v_tex_coord' (remember to define 'v_tex_coord = a_tex_coord' beforehand)."

2. Regarding the issue of character portraits turning black around the edges due to the Shader. I finally investigated the shader code and found that it concerns the 'col' variable used for rendering 'tex0'. I had defined it as a 3-dimensional object (vec3), representing its RGB color space, which resulted in the inability to recognize 'a' (alpha/transparency). The solution involves redefining 'col' as 'vec4' and adding the necessary statements to define 'col.a':"

Code: Select all

    vec4 col;
    col.r = texture2D(tex0, crt_uv + vec2(0.002, 0)).r;
    col.g = texture2D(tex0, crt_uv).g;
    col.b = texture2D(tex0, crt_uv + vec2(0., -0.002)).b;
    col.a = texture2D(tex0, crt_uv).a;
    col *= step(0.0, v_tex_coord.x) * step(0.0, v_tex_coord.y);
    col *= 1.0 - step(1.0, v_tex_coord.x) * 1.0 - step(1.0,v_tex_coord.y);
    col *= vec4(0.95,1.02,0.95,1.0);
    col *= 0.99+0.01*sin(1200.0*u_time);
    gl_FragColor = col;
""")

3. From your advice, here is my solution to apply the shader on a whole layer:

Code: Select all

define config.detached_layers += [ "crt" ]
image tv = Layer("crt")
 
label start:
    show tv at shadercrt#show the tv layer with the shader
    scene checkmate onlayer crt with dissolve:
            zoom 2.0 align(.0,.0)
            linear 60 xalign 1.0           
    show eileen happy onlayer crt
Then, eileen happy and checkmate would all be with the shader!
Thank you very much!!

User avatar
plastiekk
Regular
Posts: 109
Joined: Wed Sep 29, 2021 4:08 am
Contact:

Re: Can we apply Shader on a whole layer?

#4 Post by plastiekk »

birctreel wrote: Tue Nov 28, 2023 10:29 am ...
Amazing! Could you post a code example here because I have no clue about shaders but would like to try it out?
Why on earth did I put the bread in the fridge?

User avatar
birctreel
Regular
Posts: 52
Joined: Wed Dec 20, 2017 7:17 am
Projects: Knell of st.Godhrkar
Organization: -Andinomie-
Tumblr: birctreel
Location: Beijing, China
Contact:

Re: Can we apply Shader on a whole layer?

#5 Post by birctreel »

plastiekk wrote: Fri Dec 01, 2023 11:03 am
birctreel wrote: Tue Nov 28, 2023 10:29 am ...
Amazing! Could you post a code example here because I have no clue about shaders but would like to try it out?
Sure! The code example I've already posted above. But if you need shader example, here it is:

Code: Select all

init python:
    renpy.register_shader("shader.background", variables="""
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        uniform sampler2D tex1;
        varying vec2 v_tex_coord;
    """,fragment_functions="""

    vec2 crt_coords(vec2 uv, float bend)
    {
        uv -= 0.5;
        uv *= 2.;
        uv.x *= 1. + pow(abs(uv.y)/bend, 2.);
        uv.y *= 1. + pow(abs(uv.x)/bend, 2.);
        
        uv /= 2.5;
        return uv + .5;
    }
    float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
    }

    float rand(float c){
        return rand(vec2(c,1.0));
    }
    """,
        vertex_300="""
        v_tex_coord = a_tex_coord;
    """,

        fragment_300="""
        float t = float(int(u_time * 15));
        vec2 crt_uv = crt_coords(v_tex_coord, 4.);
        vec4 col;
        col.r = texture2D(tex0, crt_uv + vec2(0.002, 0)).r;
        col.g = texture2D(tex0, crt_uv).g;
        col.b = texture2D(tex0, crt_uv + vec2(0., -0.002)).b;
        col.a = texture2D(tex0, crt_uv).a;
        col *= step(0.0, v_tex_coord.x) * step(0.0, v_tex_coord.y);
        col *= 1.0 - step(1.0, v_tex_coord.x) * 1.0 - step(1.0,v_tex_coord.y);
        col *= vec4(0.98,1.01,0.95,1.0);
        col *= 0.99+0.01*sin(1200.0*u_time);
        col *= (1.0+(rand(v_tex_coord+t*.01)-.2)*.15);
        gl_FragColor = col;
    """)


transform shadercrt:
    shader "shader.background"

Post Reply

Who is online

Users browsing this forum: Google [Bot], Imperf3kt