找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 58|回复: 5

[原创] 破旧电视机转场 Shader

[复制链接]
发表于 前天 04:14 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
某天晚上, 龙叔希望找一个破旧电视的转场, 后来找到了一个 CDD ( 那个 CDD 也有用到 shader, 但并没有完全使用 GLSL 编写, 而是调用了部分函数而已), 但可惜的是 CDD 只是 CDD, 强硬搬到 Transform 里效果并不好
然后我刚学 GLSL 四天, 正好练练手, 于是就有了以下的代码, 慢慢看下去吧


首先是单独作用在一个可视化组件上的代码
[RenPy] 纯文本查看 复制代码
    # 基础的 glitch 类型, 用于示范
    renpy.register_shader("glitch",
    variables = """
        uniform float u_time;
        uniform float u_lod_bias;
        uniform vec4 u_random;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;
        uniform sampler2D tex0;
    """,
    vertex_300="""
        v_tex_coord = a_tex_coord;
        """,
    fragment_functions="""
        float make_offset(float y, float time, float random=3.1415926){
        // 将原始随机数, y 坐标, 时间进行运算获得一个随机数种子
        float x = random*20.0 + y*3.0 + time*5.0;
        // 使用三角函数获取伪噪声, 这个伪噪声就为图像的偏移值
        float primer_random = (sin(cos(x)+x) + cos(x * 0.6 + 1.5) + cos(x * 1.8 + 2.5)) / 3.0;
        // 如果偏移的绝对值小于 0.5 直接不偏移
        if(abs(primer_random)<0.5) return 0.0;
        // 否则返回偏移值的 0.1 倍
        return (primer_random / 10.0) ;
        }
    """, fragment_300="""
        // 获取此像素点的偏移值
        float addin = make_offset(v_tex_coord[1], u_time, u_random[0]);
        if (addin != 0.0){
            // 图像 uv 的偏移
            vec2 uv = vec2(v_tex_coord[0]+addin, v_tex_coord[1]);
            // 将图像按照偏移的 uv 进行渲染
            gl_FragColor = texture2D(tex0, uv, u_lod_bias);
            // 这里再次偏移 uv, 并分别将再次渲染的图像的 r, b 通道提取出来作为色散
            gl_FragColor.r = texture2D(tex0, vec2(uv[0] + addin / 3.0, uv[1]), u_lod_bias).r;
            gl_FragColor.b = texture2D(tex0, vec2(uv[0] - addin / 3.0, uv[1]), u_lod_bias).b;
            }
        else{
            gl_FragColor = texture2D(tex0, v_tex_coord.st, u_lod_bias);
            }
    """)

评分

参与人数 1干货 +3 收起 理由
烈林凤 + 3 感谢分享!

查看全部评分

 楼主| 发表于 前天 04:16 | 显示全部楼层
接下来就是给 transform 使用的 shader 代码了

[RenPy] 纯文本查看 复制代码
    renpy.register_shader("glitch_transform",
        variables = """
            uniform float u_time;
            uniform float u_lod_bias;
            uniform float u_renpy_dissolve;
            uniform vec4 u_random;
            attribute vec2 a_tex_coord;
            varying vec2 v_tex_coord;
            uniform sampler2D tex0;
            uniform sampler2D tex1;
        """,
        vertex_300="""
        v_tex_coord = a_tex_coord;
        """,
        fragment_functions="""
        float make_offset(float y, float time, float random=3.1415926, float offset=0.0){
            // 将原始随机数, y 坐标, 时间进行运算获得一个随机数种子
            float x = random*20.0 + y*3.0 + time*5.0;
            // 使用三角函数获取伪噪声, 这个伪噪声就为图像的偏移值
            float primer_random = (sin(cos(x)+x) + cos(x * 0.6 + 1.5) + cos(x * 1.8 + 2.5)) / 3.0;
            // 对 dissolve 的适配
            primer_random += offset * (primer_random/abs(primer_random)) * 2.0;
            // 如果偏移的绝对值小于 0.5 直接不偏移
            // if(abs(primer_random)<0.5) return 0.0;

            // 否则返回偏移值的 0.1 倍
            return primer_random;
        }
        """,
        fragment_300="""
        // 偏移的缩放
        float pos_dispersion = 0.2;
        // 色散的缩放
        float color_dispersion = 0.05;
        // 触发变化的临界值
        float dispersion_limit = 0.25;

        // 获取此像素点的偏移值
        float addin = make_offset(v_tex_coord[1], u_time, u_random[0], u_renpy_dissolve);
        // 偏移值的绝对值
        float addin_abs = abs(addin);

        // 未达到临界值, 使用原本的图像
        if(addin_abs < 0.5+dispersion_limit){
            gl_FragColor = texture2D(tex0, v_tex_coord.st, u_lod_bias);
        }
        // 达到临界值, 渲染图像
        else if(0.5+dispersion_limit <= addin_abs && addin_abs < 1.5-dispersion_limit){

            // 因为 addin 会进行修改, 新旧图像 mix 的值就提前存储了
            float mix_val = addin - 0.5;

            // 将 [0.5~1.0], [1.0~1.5] 映射为 [0.0~1.0], [1.0~0.0], 对应渐变渐出效果
            if(addin > 1.0) addin = (1.5 - addin_abs) * (addin/addin_abs);
        else addin = (addin_abs - 0.5) * (addin/addin_abs);

        // 共用一个 uv
        vec2 uv = vec2(v_tex_coord[0]+ addin * pos_dispersion, v_tex_coord[1]);
        vec2 uv_r = vec2(uv[0] + addin * color_dispersion, uv[1]);
        vec2 uv_b = vec2(uv[0] - addin * color_dispersion, uv[1]);
        
        // 渲染旧图
        vec4 bg_color = texture2D(tex0, uv, u_lod_bias);
        bg_color.r = texture2D(tex0, uv_r, u_lod_bias).r;
        bg_color.b = texture2D(tex0, uv_r, u_lod_bias).b;

        // 渲染新图
        vec4 co_color = texture2D(tex1, uv, u_lod_bias);
        co_color.r = texture2D(tex1, uv_b, u_lod_bias).r;
        co_color.b = texture2D(tex1, uv_b, u_lod_bias).b;

        // 使用 mix 函数做 dissolve
        gl_FragColor = mix(bg_color, co_color, mix_val);

    }
    // 超出临界值, 使用新图像
    else if(1.5-dispersion_limit <= addin_abs){
        gl_FragColor = texture2D(tex1, v_tex_coord.st, u_lod_bias);
    }
    """
)
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 前天 04:21 | 显示全部楼层
最后就是调用他们的逻辑了

单个可视化组件: 使用 python 函数传入一个可视化组件
[RenPy] 纯文本查看 复制代码
    
def get_glitch_displayable(obj, render_size=None):
    model = Model(render_size)
    model.shader("glitch")
    model.texture(renpy.displayable(obj))
    return model


transform: 创建一个新 transform 来调用 shader
[RenPy] 纯文本查看 复制代码
transform glitch(duration=1.0, new_widget=None, old_widget=None):
    
    delay duration
    Model().texture(old_widget).child(new_widget)

    shader ['glitch_transform']

    u_renpy_dissolve 0.0
    linear duration u_renpy_dissolve 1.0
回复 支持 抱歉

使用道具 举报

发表于 前天 13:51 | 显示全部楼层
这个效果真的很棒!!谢谢分享!!
回复 支持 抱歉

使用道具 举报

 楼主| 发表于 前天 22:32 | 显示全部楼层
今天龙叔跟我说代码在他的 mac 电脑上报错了, 经过测试后发现了一个非常....不符合直觉的现象:
在 fragment_function 里定义的函数的传参不可以有默认值, 否则会报错 ( 仅发生在 mac 上 )
解决方案就是把默认传参给删了, 虽然本身参数就是动态的不需要默认值, 但这个报错真的是莫名其妙得不行 (

如果你用的是 mac 并发生报错的话希望这句话能解决问题

回复 支持 抱歉

使用道具 举报

发表于 昨天 14:17 | 显示全部楼层
好棒,谢谢大佬分享!
回复 支持 抱歉

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|RenPy中文空间 ( 苏ICP备17067825号|苏公网安备 32092302000068号 )

GMT+8, 2025-4-2 16:28 , Processed in 0.052560 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表