How to attach brightness shader to root viewport?

jmiguyjmiguy Posts: 23Member
edited November 20 in Shaders

I’ve played games that have an option that let you adjust the screen brightness from within the game. I’ve found a shader that allows you to set brightness, but I cannot figure out how to apply it the the root viewport so it applies to the entire game. Does anyone know how to do this?

shader_type canvas_item;

uniform float brightness = 1.0;
uniform float contrast = 1.0;
uniform float saturation = 1.0;

void fragment() {
        vec3 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0).rgb;

        c.rgb = mix(vec3(0.0), c.rgb, brightness);
        c.rgb = mix(vec3(0.5), c.rgb, contrast);
        c.rgb = mix(vec3(dot(vec3(1.0), c.rgb)*0.33333), c.rgb, saturation);

        COLOR.rgb = c;
}

Thanks.

Answers

  • MegalomaniakMegalomaniak Posts: 1,001Admin
    Answer ✓

    It's part of the screen-space shaders demo project. From the official demo projects collection. In the demo the shader is applied to a TextureRect node but I suspect it would work just as well on a ViewportContainer node. First you have to give the node a shader material, then in that material you create/attach a shader file where you can paste that code.

    ...

    Ok, just tested and I had to edit the shader:

    shader_type canvas_item;
    
    uniform float brightness = 1.0;
    uniform float contrast = 1.0;
    uniform float saturation = 1.0;
    
    uniform sampler2D vp;
    
    void fragment() {
        vec3 c = textureLod(vp, SCREEN_UV, 0.0).rgb;
    
            c.rgb = mix(vec3(0.0), c.rgb, brightness);
            c.rgb = mix(vec3(0.5), c.rgb, contrast);
            c.rgb = mix(vec3(dot(vec3(1.0), c.rgb)*0.33333), c.rgb, saturation);
    
            COLOR.rgb = c;
        }
    

    And then in the material parameters I had to create a new ViewportTexture for the vp uniforms parameter input. Hope this helps, let me know if you need further instruction.

  • jmiguyjmiguy Posts: 23Member

    Thanks for the info. I have not tried using a viewport texture. I will give it a try and let you know if I can get it to work. Also, you’re exactly right regarding the shader. The demo you mentioned was where I found it. Thanks for all of your help.

  • jmiguyjmiguy Posts: 23Member

    I'm still not able to get this to work, as I'm not sure what I'm doing (I just started with Godot a few weeks ago). I have a global singleton scene that I'm trying to add this shader to. I added a ViewportContainer under the root node, then when adding the ViewportTexture in materials parameter, I was prompted to select a Viewport, but none of my nodes were Viewports. I tried adding a Viewport node and setting ViewportContainer as its child. None of this worked for me.

    I'd like to make a brightness slider just like the volume slider to add to an options menu.

    Thanks.

  • MegalomaniakMegalomaniak Posts: 1,001Admin

    No, no, no - the viewport has to be the containers child, and then the rest of your game would be under that viewport. That viewport needs to have a camera under it to capture. I'm on my laptop right now but I'll post a simple example project for you later from my desktop.

  • MegalomaniakMegalomaniak Posts: 1,001Admin
    edited November 21 Answer ✓
  • jmiguyjmiguy Posts: 23Member

    Thank you so much for taking the time to make this demo. This is very helpful. You have been more helpful than all the docs and tutorials I've gone through so far. Since I'm new to Godot, I'm still struggling with the basics. It seems the more I read the documentation, the more confused I get.

    I added a button to your demo under the sliders to change to a new scene. I created a new scene using a ColorRect to cover the entire viewport. When I configure the button to use get_tree().change_scene("res://Scenes/ColorRect.tscn"), as the documentation says to do for switching scenes, the new scene does appear, but the changes made in BCS are lost.

    How do I make these settings persist through scene changes? I've tried using the code below, also found in Godot docs to try to load the new scene into the master tree, but that didn't work correctly either. I've only posted part of the code I used, since the code example was kind of long.

    If you can help me learn how to do proper scene changes with the shader being applied to all scenes, I'd be truly grateful.

    func _deferred_goto_scene(path):
    # Immediately free the current scene,
    # there is no risk here.
    current_scene.free()

    # Load new scene.
    var s = ResourceLoader.load(path)
    
    # Instance the new scene.
    current_scene = s.instance()
    
    # Add it to the active scene, as child of root.
    get_tree().get_root().add_child(current_scene)
    
    # Optional, to make it compatible with the SceneTree.change_scene() API.
    get_tree().set_current_scene(current_scene)
    

    Thanks again.

  • MegalomaniakMegalomaniak Posts: 1,001Admin
    edited November 21

    @jmiguy said:
    When I configure the button to use get_tree().change_scene("res://Scenes/ColorRect.tscn"), as the documentation says to do for switching scenes, the new scene does appear, but the changes made in BCS are lost.

    How do I make these settings persist through scene changes?

    Well, the "simple" answer, as you already might suspect, is not to change scene but to instance a scene under that Viewport(and remove any no longer needed). Note that the 3D material test object is instanced as a scene(though I did that through the editors scene outliner rather than code).

    @jmiguy said:
    I've tried using the code below, also found in Godot docs to try to load the new scene into the master tree, but that didn't work correctly either. I've only posted part of the code I used, since the code example was kind of long.

    func _deferred_goto_scene(path):
        # Immediately free the current scene,
        # there is no risk here.
        current_scene.free()
    
        # Load new scene.
        var s = ResourceLoader.load(path)
    
        # Instance the new scene.
        current_scene = s.instance()
    
        # Add it to the active scene, as child of root.
        get_tree().get_root().add_child(current_scene)
    
        # Optional, to make it compatible with the SceneTree.change_scene() API.
        get_tree().set_current_scene(current_scene)
    

    This code is just a part of a bigger chain/concept - I recommend that you fully follow through that tutorial on it's own first to better understand what it's about and how it deals with things.

    edit: realized I didn't link the most up to date version of the tutorial, so fixed it.

  • jmiguyjmiguy Posts: 23Member

    Thanks again. I've read that documentation multiple times and have tried every combination I can think up, but nothing works for me. I don't understand how to instance the new scene under the Viewport and remove the current instance. That's where I'm stuck.

  • jmiguyjmiguy Posts: 23Member

    I made some progress. I found a tutorial that showed how to add an instance. Now I need to figure out how to remove the old scene.

    func _on_Button_pressed():
    var new_res = load("res://Scenes/ColorRect.tscn")
    var new_scene = new_res.instance()
    $ViewportContainer/Viewport.add_child(new_scene)

  • jmiguyjmiguy Posts: 23Member
    edited November 22 Answer ✓

    I got it to work!!! Can you please let me know if you agree with this, or if there's a better way? I added a global autoload script to your demo. In the Master.gd script I added the following function, then below that is the code I added to Global.gd.

    func _on_Button_pressed():
        Global.goto_scene("res://Scenes/ColorRect.tscn")
    
    
    extends Node
    
    var current_scene = null
    
    func _ready():
        current_scene = get_node("/root/Master/ViewportContainer/Viewport/Scene Root") 
    
    func goto_scene(path):
        call_deferred("_deferred_goto_scene", path)
    
    func _deferred_goto_scene(path):
        current_scene.free()
        var s = ResourceLoader.load(path)
        current_scene = s.instance()
        get_node("/root/Master/ViewportContainer/Viewport").add_child(current_scene)
        get_tree().set_current_scene(current_scene)
    
  • MegalomaniakMegalomaniak Posts: 1,001Admin
    edited November 22 Answer ✓

    At a glance certainly looks about right. Ultimately you'll want to take a look at using the interactive resource loader though.

  • jmiguyjmiguy Posts: 23Member

    Thank you. I was reading up on the interactive loader in the background loading section of the docs. I will definitely try to get it to work as well.

    https://docs.godotengine.org/en/3.0/tutorials/io/background_loading.html?highlight=Interactive resource loader

  • CalinouCalinou Posts: 193Admin Godot Developer
    edited November 24

    I think you can add brightness control to your game without writing your own shader. The Environment resource used in WorldEnvironment already offers an Adjustments option which can be used to tweak brightness, contrast and saturation of the final image (with optional color grading).

  • jmiguyjmiguy Posts: 23Member

    Thanks for this info. (More for me to learn.) :) Do you know of a good tutorial or demo that showcases this functionality?

Sign In or Register to comment.