找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4976|回复: 2

[原创] 使用shader绘制云层穿梭动画

[复制链接]
发表于 2021-7-20 07:42:47 | 显示全部楼层 |阅读模式

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

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

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

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

此贴中使用的shader代码来源为
CloudFlight

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

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

    renpy.register_shader("shadertoy.cloudflight", variables="""
        uniform float u_time;
        uniform vec4 a_position;
        varying vec2 v_tex_coord;
        uniform sampler2D tex1;
        attribute vec2 a_tex_coord;
    """,fragment_functions="""
        vec2 MatMultiplayVec(vec4 m, vec2 v)
        {
            return vec2(m.x * v.x + m.z * v.y, m.y * v.x + m.w * v.y);
        }
        vec4 r2(in float a)
        {
            float c = cos(a), s = sin(a); return vec4(c, s, -s, c);
        }
        float smax(float a, float b, float s)
        {
            float h = clamp(.5 + .5*(a - b)/s, 0., 1.);
            return mix(b, a, h) + h*(1. - h)*s;
        }
        vec3 hash33(vec3 p)
        { 
            float n = sin(dot(p, vec3(7, 157, 113)));    
            return fract(vec3(2097152, 262144, 32768)*n); 
        }
        float n3D( in vec3 p)
        {
            const vec3 s = vec3(7, 157, 113);
            vec3 ip = floor(p);
            vec4 h = vec4(0., s.yz, s.y + s.z) + dot(ip, s);
            p -= ip;
            p = p*p*(3. - 2.*p);
            h = mix(fract(sin(h)*43758.5453), fract(sin(h + s.x)*43758.5453), p.x);
            h.xy = mix(h.xz, h.yw, p.y);
            return mix(h.x, h.y, p.z);
        }
        vec2 path(in float z)
        {
            return vec2(sin(z*.075)*8., cos(z*.1)*.75*2.);
        }
        float map(vec3 p, float u_time)
        {
            vec3 t = vec3(1, .5, .25)*u_time;
            float mainLayer = n3D(p*vec3(.4, 1, .4))*.66 + n3D(p*vec3(.4, 1, .4)*2.*.8)*.34 - .0;    
            float detailLayer = n3D(p*3. + t)*.57 +  n3D(p*6.015 + t*2.)*.28 +  n3D(p*12.01 + t*4.)*.15 - .0;
            float clouds = mainLayer*.84 + detailLayer*.16;

            #if (ARRANGEMENT != 3)
            p.xy -= path(p.z);
            #endif

            #if (ARRANGEMENT == 0)
            return smax(clouds, -length(p.xy*vec2(1./32., 1.)) + 1.1 + (clouds - .5), .5) + (clouds - .5);
            #elif (ARRANGEMENT == 1)

            return smax((clouds - .25)*2., -smax(abs(p.x) - .5, abs(p.y) - .5, 1.), 2.);
            #elif (ARRANGEMENT == 2)

            return smax(clouds - .075, -length(p.xy*vec2(1./32., 1.)) + 1.1 + (clouds - .5), .5) + (clouds - .5)*.35;
            #else
            return (clouds - .25)*2.; 
            #endif
        }
    """,
        vertex_300="""
        v_tex_coord = vec2(a_tex_coord.s, 1.0 - a_tex_coord.t);
    """,
    
        fragment_300="""
        #define FAR 60.
        #define ARRANGEMENT 0
        vec2 uv = v_tex_coord;

        vec3 ro = vec3(0, 0, u_time*4.);
        vec3 lk = ro + vec3(0, 0, .25);

        ro.xy += path(ro.z);
        lk.xy += path(lk.z);

        float FOV = 3.14159/2.75;
        vec3 forward = normalize(lk-ro);
        vec3 right = normalize(vec3(forward.z, 0., -forward.x )); 
        vec3 up = cross(forward, right);

        vec3 rd = normalize(forward + FOV*uv.x*right + FOV*uv.y*up);

        vec2 sw = path(lk.z);
        //rd.xy *= r2(-sw.x/24.);
        rd.xy = MatMultiplayVec(r2(-sw.x/24.), rd.xy);
        //rd.yz *= r2(-sw.y/16.);
        rd.yz = MatMultiplayVec(r2(-sw.x/16.), rd.yz);

        vec3 rnd = hash33(rd.yzx + fract(u_time));

        float lDen = 0., td = 0., w = 0.;

        float d = 1., d2 = 0., t = dot(rnd, vec3(.333));

        const float h = .5;

        vec3 col = vec3(0), sp;

        vec3 ld = normalize(vec3(-.2, .3, .8));

        vec3 sky = mix(vec3(1, 1, .9), vec3(.19, .35, .56), rd.y*0.5 + 0.5);

        vec3 fakeLd = normalize(vec3(-.2, .3, .8*1.5));
        float sun = clamp(dot(fakeLd, rd), 0.0, 1.0);

        sky += vec3(1, .3, .05)*pow(sun, 5.)*.25; 
        sky += vec3(1, .4, .05)*pow(sun, 8.)*.35; 
        sky += vec3(1, .9, .7)*pow(sun, 128.)*.5; 

        sky *= sqrt(sky); 

        vec3 cloudCol = mix(sky, vec3(1, .9, .8), .66);

        for (int i=0; i<64; i++) {


            sp = ro + rd*t; // Current ray position.
            d = map(sp, u_time); // Closest distance to the surface... particle.

            if(d<.001*(1. + t*.125) || td>1. || t>FAR) break;

            w = d<h? (1. - td)*(h - d) : 0.;   

            td += w + 1./64.; // Looks cleaner, but a little washed out.

            d2 = map(sp + ld*.1, u_time);

            float diff = max(d2 - d, 0.)*20.; 

            col += w*max(d*d*(1. - d2)*3. - .05, 0.)*(diff*cloudCol*2. + vec3(.95, 1, 1.05))*2.5; // Darker, brooding.

            col *= .98 + fract(rnd*289. + t*41.13)*.04;

            t += max(d*.5, .05); //
        }

        col = max(col, 0.);

        col *= mix(vec3(1), sky, .25);

        col = mix(col, sky, smoothstep(0., .85, t/FAR));
        col = mix(col, sky*sky*2., 1. - 1./(1.+ t*t*.001));
        vec3 fCol = mix(pow(vec3(1.3, 1, 1)*col, vec3(1, 2, 10)), sky, .5);
        col = mix(fCol, col, dot(cos(rd*6. +sin(rd.yzx*6.)), vec3(.333))*.1 + .9);

        uv = v_tex_coord;

        col = mix(pow(min(vec3(1.5, 1, 1).zyx*col, 1.), vec3(1, 3, 16)), col, 
                  pow(16.*uv.x*uv.y*(1. - uv.x)*(1. - uv.y) , .125)*.5 + .5);

        gl_FragColor = vec4(sqrt(min(col, 1.)), 1.0);

    """
    )

需要说明的是:原链接中提供了两种 n3D 函数,默认是使用RGBA噪声纹理贴图的方式,注释了使用代码生成噪声的方式;由于Ren'Py中使用RGBA噪声纹理贴图会报错“全局纹理需要 #130以上版本才支持”,应该是Ren'Py使用的OpenGL版本比较低的原因,所以不得不改成使用代码生成噪声(对性能有影响)。

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

    class CloudFlight(renpy.Displayable):
    
        def __init__(self, child, width, height, **kwargs):
            super(CloudFlight, 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.cloudflight")
            render.add_uniform("u_time", st)
            renpy.redraw(self, 0)
            return render


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

label start:
 
    show cloudflight     
    pause

注意这里使用一张名为“texture”的图片,图片内容是什么不重要,尺寸需要为1280×720(或者根据需求修改)。
没有什么参数可以调。如果要修改的话,可以试试shader中两个预编译的 FARARRANGEMENT

发表于 2021-7-30 12:16:32 | 显示全部楼层
报错哦
[RenPy] 纯文本查看 复制代码
I'm sorry, but errors were detected in your script. Please correct the
errors listed below, and try again.


File "game/screens.rpy", line 1491: python block expects a non-empty block.
    init python:
                ^

File "game/screens.rpy", line 1493: expected statement.
(Perhaps you left out a " at the end of the first line.)
    renpy.register_shader("shadertoy.cloudflight", variables="""
            uniform float u_time;
            uniform vec4 a_position;
            varying vec2 v_tex_coord;
            uniform sampler2D tex1;
            attribute vec2 a_tex_coord;
        """,fragment_functions="""
            vec2 MatMultiplayVec(vec4 m, vec2 v)
            {
                return vec2(m.x * v.x + m.z * v.y, m.y * v.x + m.w * v.y);
            }
            vec4 r2(in float a)
            {
                float c = cos(a), s = sin(a); return vec4(c, s, -s, c);
            }
            float smax(float a, float b, float s)
            {
                float h = clamp(.5 + .5*(a - b)/s, 0., 1.);
                return mix(b, a, h) + h*(1. - h)*s;
            }
            vec3 hash33(vec3 p)
            { 
                float n = sin(dot(p, vec3(7, 157, 113)));    
                return fract(vec3(2097152, 262144, 32768)*n); 
            }
            float n3D( in vec3 p)
            {
                const vec3 s = vec3(7, 157, 113);
                vec3 ip = floor(p);
                vec4 h = vec4(0., s.yz, s.y + s.z) + dot(ip, s);
                p -= ip;
                p = p*p*(3. - 2.*p);
                h = mix(fract(sin(h)*43758.5453), fract(sin(h + s.x)*43758.5453), p.x);
                h.xy = mix(h.xz, h.yw, p.y);
                return mix(h.x, h.y, p.z);
            }
            vec2 path(in float z)
            {
                return vec2(sin(z*.075)*8., cos(z*.1)*.75*2.);
            }
            float map(vec3 p, float u_time)
            {
                vec3 t = vec3(1, .5, .25)*u_time;
                float mainLayer = n3D(p*vec3(.4, 1, .4))*.66 + n3D(p*vec3(.4, 1, .4)*2.*.8)*.34 - .0;    
                float detailLayer = n3D(p*3. + t)*.57 +  n3D(p*6.015 + t*2.)*.28 +  n3D(p*12.01 + t*4.)*.15 - .0;
                float clouds = mainLayer*.84 + detailLayer*.16;
     
                #if (ARRANGEMENT != 3)
                p.xy -= path(p.z);
                #endif
     
                #if (ARRANGEMENT == 0)
                return smax(clouds, -length(p.xy*vec2(1./32., 1.)) + 1.1 + (clouds - .5), .5) + (clouds - .5);
                #elif (ARRANGEMENT == 1)
     
                return smax((clouds - .25)*2., -smax(abs(p.x) - .5, abs(p.y) - .5, 1.), 2.);
                #elif (ARRANGEMENT == 2)
     
                return smax(clouds - .075, -length(p.xy*vec2(1./32., 1.)) + 1.1 + (clouds - .5), .5) + (clouds - .5)*.35;
                #else
                return (clouds - .25)*2.; 
                #endif
            }
        """,
            vertex_300="""
            v_tex_coord = vec2(a_tex_coord.s, 1.0 - a_tex_coord.t);
        """,
         
            fragment_300="""
            #define FAR 60.
            #define ARRANGEMENT 0
            vec2 uv = v_tex_coord;
     
            vec3 ro = vec3(0, 0, u_time*4.);
            vec3 lk = ro + vec3(0, 0, .25);
     
            ro.xy += path(ro.z);
            lk.xy += path(lk.z);
     
            float FOV = 3.14159/2.75;
            vec3 forward = normalize(lk-ro);
            vec3 right = normalize(vec3(forward.z, 0., -forward.x )); 
            vec3 up = cross(forward, right);
     
            vec3 rd = normalize(forward + FOV*uv.x*right + FOV*uv.y*up);
     
            vec2 sw = path(lk.z);
            //rd.xy *= r2(-sw.x/24.);
            rd.xy = MatMultiplayVec(r2(-sw.x/24.), rd.xy);
            //rd.yz *= r2(-sw.y/16.);
            rd.yz = MatMultiplayVec(r2(-sw.x/16.), rd.yz);
     
            vec3 rnd = hash33(rd.yzx + fract(u_time));
     
            float lDen = 0., td = 0., w = 0.;
     
            float d = 1., d2 = 0., t = dot(rnd, vec3(.333));
     
            const float h = .5;
     
            vec3 col = vec3(0), sp;
     
            vec3 ld = normalize(vec3(-.2, .3, .8));
     
            vec3 sky = mix(vec3(1, 1, .9), vec3(.19, .35, .56), rd.y*0.5 + 0.5);
     
            vec3 fakeLd = normalize(vec3(-.2, .3, .8*1.5));
            float sun = clamp(dot(fakeLd, rd), 0.0, 1.0);
     
            sky += vec3(1, .3, .05)*pow(sun, 5.)*.25; 
            sky += vec3(1, .4, .05)*pow(sun, 8.)*.35; 
            sky += vec3(1, .9, .7)*pow(sun, 128.)*.5; 
     
            sky *= sqrt(sky); 
     
            vec3 cloudCol = mix(sky, vec3(1, .9, .8), .66);
     
            for (int i=0; i<64; i++) {
     
     
                sp = ro + rd*t; // Current ray position.
                d = map(sp, u_time); // Closest distance to the surface... particle.
     
                if(d<.001*(1. + t*.125) || td>1. || t>FAR) break;
     
                w = d<h? (1. - td)*(h - d) : 0.;   
     
                td += w + 1./64.; // Looks cleaner, but a little washed out.
     
                d2 = map(sp + ld*.1, u_time);
     
                float diff = max(d2 - d, 0.)*20.; 
     
                col += w*max(d*d*(1. - d2)*3. - .05, 0.)*(diff*cloudCol*2. + vec3(.95, 1, 1.05))*2.5; // Darker, brooding.
     
                col *= .98 + fract(rnd*289. + t*41.13)*.04;
     
                t += max(d*.5, .05); //
            }
     
            col = max(col, 0.);
     
            col *= mix(vec3(1), sky, .25);
     
            col = mix(col, sky, smoothstep(0., .85, t/FAR));
            col = mix(col, sky*sky*2., 1. - 1./(1.+ t*t*.001));
            vec3 fCol = mix(pow(vec3(1.3, 1, 1)*col, vec3(1, 2, 10)), sky, .5);
            col = mix(fCol, col, dot(cos(rd*6. +sin(rd.yzx*6.)), vec3(.333))*.1 + .9);
     
            uv = v_tex_coord;
     
            col = mix(pow(min(vec3(1.5, 1, 1).zyx*col, 1.), vec3(1, 3, 16)), col, 
                      pow(16.*uv.x*uv.y*(1. - uv.x)*(1. - uv.y) , .125)*.5 + .5);
     
            gl_FragColor = vec4(sqrt(min(col, 1.)), 1.0);
     
        """
        )
回复 支持 抱歉

使用道具 举报

发表于 2021-7-30 14:31:43 | 显示全部楼层
qwe3147969113 发表于 2021-7-30 12:16
报错哦
[RenPy] 纯文本查看 复制代码
I'm sorry, but errors were detected in your script. Please correct th ...[/quote]

[mw_shl_code=renpy,true]        renpy.register_shader("shadertoy.cloudflight", variables="""


好吧,其实是这段代码前面要加个tab
回复 支持 抱歉

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-22 19:47 , Processed in 0.124005 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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