找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 5757|回复: 2

[原创] 使用shader绘制动态云层

[复制链接]
发表于 2021-7-16 11:32:53 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 被诅咒的章鱼 于 2021-7-23 16:35 编辑

最近在研究shader,此贴为中间副产品。需Ren'Py 7.4.5以上版本。

此贴中使用的shader代码来源为
ShaderToy-2D Clouds

我只是做了一点修改和移植到Ren'Py上。

首先是主体的shader代码(不要问我是怎么来的,问就是我也不懂):
[RenPy] 纯文本查看 复制代码
init python:

    renpy.register_shader("shadertoy.clouds", variables="""
        uniform float u_cloudscale;
        uniform float u_speed;
        uniform float u_clouddark;
        uniform float u_cloudlight;
        uniform float u_cloudcover;
        uniform float u_cloudalpha;
        uniform float u_skytint;
        uniform vec3 u_skycolour1;
        uniform vec3 u_skycolour2;
        uniform vec4 u_m;
        uniform vec2 u_model_size;
        uniform float u_time;
        uniform vec4 u_random;
        uniform vec4 a_position;
        varying vec2 v_tex_coord;
        uniform sampler2D tex0;
        attribute vec2 a_tex_coord;
    """,
        fragment_functions="""
        vec2 hash(vec2 p)
        {
            p = vec2(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)));
            return -1.0 + 2.0*fract(sin(p)*43758.5453123);
        }
        vec2 MatMultiplayVec(vec4 m, vec2 v)
        {
            return vec2(m.x * v.x + m.z * v.y, m.y * v.x + m.w * v.y);
        }
        float noise(in vec2 p)
        {
            const float K1 = 0.366025404; // (sqrt(3)-1)/2;
            const float K2 = 0.211324865; // (3-sqrt(3))/6;
            vec2 i = floor(p + (p.x+p.y)*K1);        
            vec2 a = p - i + (i.x+i.y)*K2;
            vec2 o = (a.x>a.y) ? vec2(1.0,0.0) : vec2(0.0,1.0); //vec2 of = 0.5 + 0.5*vec2(sign(a.x-a.y), sign(a.y-a.x));
            vec2 b = a - o + K2;
            vec2 c = a - 1.0 + 2.0*K2;
            vec3 h = max(0.5-vec3(dot(a,a), dot(b,b), dot(c,c)), 0.0);
            vec3 n = h*h*h*h*vec3(dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0)));
            return dot(n, vec3(70.0));        
        }
        float fbm(vec2 n, vec4 m)
        {
            float total = 0.0, amplitude = 0.1;
            for (int i = 0; i < 7; i++)
            {
                total += noise(n) * amplitude;
                //n = m * n;
                n = MatMultiplayVec(m, n);
                amplitude *= 0.4;
            }
            return total;
        }
        
    """,
        vertex_300="""
        v_tex_coord = a_tex_coord;
    """,
    
        fragment_300="""

        vec2 p = v_tex_coord.xy;
        vec2 uv = p * vec2(u_model_size.x/u_model_size.y, 1.0);
        float time = u_time * u_speed;
        
        float q = fbm(uv * u_cloudscale * 0.5, u_m);

        //ridged noise shape
        float r = 0.0;
        uv *= u_cloudscale;
        uv -= q - time;
        float weight = 0.8;
        for (int i=0; i<8; i++)
        {
            r += abs(weight*noise( uv ));
            uv = MatMultiplayVec(u_m, uv) + time;
            weight *= 0.7;
        }

        //noise shape
        float f = 0.0;
        uv = p * vec2(u_model_size.x/u_model_size.y, 1.0);
        uv *= u_cloudscale;
        uv -= q - time;
        weight = 0.7;
        for (int i=0; i<8; i++){
            f += weight * noise(uv);
            uv = MatMultiplayVec(u_m, uv) + time;
            weight *= 0.6;
        }
        f *= r + f;

        //noise colour
        float c = 0.0;
        time = u_time * u_speed * 2.0;
        uv = p * vec2(u_model_size.x/u_model_size.y, 1.0);
        uv *= u_cloudscale * 2.0;
        uv -= q - time;
        weight = 0.4;
        for (int i=0; i<7; i++)
        {
            c += weight*noise( uv );
            uv = MatMultiplayVec(u_m, uv) + time;
            weight *= 0.6;
        }

        //noise ridge colour
        float c1 = 0.0;
        time = u_time * u_speed * 3.0;
        uv = p * vec2(u_model_size.x/u_model_size.y, 1.0);
        uv *= u_cloudscale * 3.0;
        uv -= q - time;
        weight = 0.4;
        for (int i=0; i<7; i++){
            c1 += abs(weight*noise( uv ));
            uv = MatMultiplayVec(u_m, uv) + time;
            weight *= 0.6;
        }
        c += c1;

        vec3 skycolour = mix(u_skycolour2, u_skycolour1, p.y);
        vec3 cloudcolour = vec3(1.1, 1.1, 0.9) * clamp((u_clouddark + u_cloudlight*c), 0.0, 1.0);
        f = u_cloudcover + u_cloudalpha * f * r;
        vec3 result = mix(skycolour, clamp(u_skytint * skycolour + cloudcolour, 0.0, 1.0), clamp(f + c, 0.0, 1.0));
        gl_FragColor = vec4(result, 1.0);
    """)

然后是在自定义可视组件中应用这个shader:
[RenPy] 纯文本查看 复制代码
init python:

    class CloudAnim(renpy.Displayable):
    
        def __init__(self, child, width, height, **kwargs):
            super(CloudAnim, self).__init__(**kwargs)
            
            self.child = renpy.displayable(child)
            self.width = width
            self.height = height

        def render(self, width, height, st, at):
            render = renpy.Render(self.width, self.height)
            render.place(self.child)
            render.add_shader("shadertoy.clouds")
            render.add_uniform("u_time", st)
            render.add_uniform("u_speed", 0.01)
            render.add_uniform("u_cloudscale", 1.1)
            render.add_uniform("u_clouddark", 0.7)
            render.add_uniform("u_cloudlight", 0.4)
            render.add_uniform("u_cloudcover", 0.2)
            render.add_uniform("u_cloudalpha", 8.0)
            render.add_uniform("u_skytint", 0.6)
            render.add_uniform("u_skycolour1", (0.2, 0.4, 0.6))
            render.add_uniform("u_skycolour2", (0.4, 0.7, 1.0))
            render.add_uniform("u_m", (1.6, 1.2, -1.2, 1.6))
            render.add_uniform("u_model_size", (1280, 720))
            renpy.redraw(self, 0)
            return render


script.rpy中使用图像:
[RenPy] 纯文本查看 复制代码
image clouds = CloudAnim("texture", width = 1280, height = 720)

label main_menu:
    return


label start:

    show clouds
    
    pause


注意这里使用一张名为“texture”的图片,图片内容是什么不重要,尺寸需要为1280×720(或者根据需求修改)。预览就不放了。在自定义可视组件的render部分中的所有使用add_uniform添加的参数都可以修改,请根据自己设备和喜好试着调整。

最后说明一下使用CDD而不是transform的原因:
我试过了,程序能跑,效果不行。
在transform中无法指定shader中的u_time,只能让Ren'Py内部的调用周期增加u_time的值,运行效果就是卡成ppt。



发表于 2021-7-23 14:48:35 | 显示全部楼层
7.5.4?
回复

使用道具 举报

 楼主| 发表于 2021-7-23 16:35:56 | 显示全部楼层
7.4.5……已修改……
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-19 07:09 , Processed in 0.059857 second(s), 13 queries , File On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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