Parallax Camera and "Drunken" Blur

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
midgethetree
Regular
Posts: 39
Joined: Wed Dec 30, 2020 3:51 pm
Completed: Back When, The Mother of Pearls, various jam games
Projects: When Everyone's Watching, Deliberation
Github: midgethetree
itch: midge-the-tree
Discord: rainafc#3353
Contact:

Parallax Camera and "Drunken" Blur

#1 Post by midgethetree »

I've been asked about how I accomplished the visual effects in Back When, more specifically the parallax and blur effects. Here's the answer!

Parallax

The parallax effect used the following transform:

Code: Select all

transform parallax:
    perspective True
    subpixel True
    function moving_camera
Which I used when displaying the master layer (where the background and sprite are) by adding the following line to the before_main_menu and start labels:

Code: Select all

camera at parallax
(Edited to replace "show layer master at parallax" with "camera at parallax",which as of the introduction of the camera statement with 7.4.6 is the best way to apply 3D stage effects you want to keep from scene to scene.)

Perspective True turns on Ren'Py's 3D Stage, which means I can specify how close to the "camera" the background and sprite are relative to each other. Subpixel True makes the master layer drawn with subpixel accuracy, so small nudges of the cursor don't make the images jump pixel by pixel. Then there's the last line, the moving_camera function, which is what actually, well, moves the camera.

Code: Select all

def moving_camera(trans, st, at):
    if persistent.parallax:
        x, y = renpy.display.draw.get_mouse_pos()
        trans.xoffset = (x - config.screen_width / 2) * .05
        trans.yoffset = (y - config.screen_height / 2) * .05
    else:
        trans.xoffset = 0
        trans.yoffset = 0
    return 0
This function checks the position of the mouse, and changes the offset of the transformed displayable (in this case, the master layer) based on that. You'll notice it only does so if persistent.parallax is True, which I included so I could provide readers an option in the settings menu to turn off parallax.

The last ingredient was taking advantage of that 3D Stage feature to set the background at a different distance from the camera. I took a 1975x1111 background and displayed it at the transform:

Code: Select all

transform bg:
    truecenter()
    zzoom true
    zpos -1000
Truecenter centers the background, zpos sets the image "further back", and since that shrinks the image zzoom true scales it back up. With that, I had a working parallax camera!

Blur

There were actually two blurs I used. One was the Ren'Py blur which can be used in transforms to give a kind of motion blur effect. I used this to blur Brooke's sprite when it first appeared, like so:

Code: Select all

show brooke at default:
    linear .25 blur 5
    linear .25 blur 0
This is also the blur I used on the screens layer when the confirm screen was shown, so the rest of the game menu would appear out-of-focus. But if used to blur the master layer it didn't play well with the background's transform (specifically the negative zzoom seemed to be the issue...), and I wanted a somewhat different effect anyway, so I also wrote a custom shader:

Code: Select all

renpy.register_shader('drunk', variables="""
    uniform sampler2D tex0;
    uniform vec2 u_model_size;
    varying vec2 v_uv;
    uniform float u_blur_radius;
    uniform float u_dark_radius;
""", vertex_300="""
    v_uv = a_position.xy / u_model_size;
""", fragment_300="""
    vec4 col = texture2D(tex0, v_uv);
    col += texture2D(tex0, v_uv + vec2(.0, -u_blur_radius) / u_model_size);
    col += texture2D(tex0, v_uv + vec2(.0, u_blur_radius) / u_model_size);
    col += texture2D(tex0, v_uv + vec2(-u_blur_radius, .0) / u_model_size);
    col += texture2D(tex0, v_uv + vec2(u_blur_radius, .0) / u_model_size);
    col /= 5.0;
    gl_FragColor = vec4(mix(col.rgb, vec3(0,0,0), max(distance(v_uv, vec2(.5,.5)) / sqrt(.5) - u_dark_radius, .0)), col.a);
""")
This shader isn't anything too crazy for those familiar with shaders to begin with. It takes the existing texture (tex0) and layers it over itself for a total of five layers, four of those slightly offset in different directions, and then divides by 5 to prevent everything from becoming super bright. This gives more of a double-vision kind of blur, which was perfect for what I had in mind. Then I mixed the result with black depending on the distance from the center of the screen, so it'd be black around the edges. To use this shader I wrote the following transform to show the master layer at:

Code: Select all

transform drunk:
    parallel:
        parallax()
    parallel:
        shader 'drunk'
        u_blur_radius .0 u_dark_radius 1.0
        easein .5 u_blur_radius persistent.blur u_dark_radius .5
        .1
        easeout .5 u_blur_radius .0 u_dark_radius 1.0
The previous parallax transform is still being run, but now in parallel to that is the drunk shader. The blur radius goes from .0 to the persistent.blur variable (7.0 by default, .0 if the player turned off blur) and back, and the dark_radius goes from 1.0 to .5 and back (arguably I did a poor job naming this variable - values of 1.0 and greater mean no black around the edges, -1.0 would make for a completely black screen, which is not necessarily intuitive but ah well).

Hope this explanation helps y'all make some cool VNs!
Last edited by midgethetree on Mon Nov 29, 2021 1:57 am, edited 1 time in total.

User avatar
Alex
Lemma-Class Veteran
Posts: 3090
Joined: Fri Dec 11, 2009 5:25 pm
Contact:

Re: Parallax Camera and "Drunken" Blur

#2 Post by Alex »

Thanks for sharing this :)

User avatar
Syrale
Regular
Posts: 100
Joined: Sun Oct 25, 2015 10:28 am
Completed: Robot Daycare, Deep Sea Valentine, Locke(d)
Projects: Artificial Selection, rei_carnation
Github: kigyo
itch: kigyo
Discord: kigyo#2564
Contact:

Re: Parallax Camera and "Drunken" Blur

#3 Post by Syrale »

This is exactly what I was searching for. Thank you so much!

Lubnewski
Newbie
Posts: 3
Joined: Sat Nov 27, 2021 2:53 am
Contact:

Re: Parallax Camera and "Drunken" Blur

#4 Post by Lubnewski »

Hey! For some reason, the parallax effect doesn't seem to work for me? I have no clue what I'm doing wrong. There's no error or anything, the background's just completely still. I'm using Ren'Py 7.4.10 btw.

User avatar
midgethetree
Regular
Posts: 39
Joined: Wed Dec 30, 2020 3:51 pm
Completed: Back When, The Mother of Pearls, various jam games
Projects: When Everyone's Watching, Deliberation
Github: midgethetree
itch: midge-the-tree
Discord: rainafc#3353
Contact:

Re: Parallax Camera and "Drunken" Blur

#5 Post by midgethetree »

Lubnewski wrote: Sat Nov 27, 2021 3:00 am Hey! For some reason, the parallax effect doesn't seem to work for me? I have no clue what I'm doing wrong. There's no error or anything, the background's just completely still. I'm using Ren'Py 7.4.10 btw.
One thing that might be causing this is that 7.4.6 and later introduced a camera statement and thus made it so "show layer" statements are cleared on scene statements, so if you're using "show layer master at parallax" before a scene statement it's possible the parallax effect is getting cleared immediately after being applied. Switching to "camera at parallax" should work (or moving the show layer statement to after every scene statement, but the camera statement is generally more convenient since it won't get cleared).

Post Reply

Who is online

Users browsing this forum: No registered users