找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 686|回复: 0

[经验] 修改适配renpy的Shader代码经验分享

[复制链接]
发表于 2024-1-11 10:20:10 | 显示全部楼层 |阅读模式

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

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

×
本帖最后由 birctreel 于 2024-1-11 10:26 编辑

昨天和西粥老师探讨了很多关于如何将shader应用于图层的尝试时聊到制作更多适用于renpy的shader程序,于是这里分享一下关于将shader代码修改成适配于renpy引擎的心得
*抱歉其实俺不懂shader,也不怎么懂renpy,甚至不会python(……)以下心得是基于对比了很多shader源码和renpy版本之间的差异(以及昨晚AxelKong老师修改我的shader代码后)总结出来的修改经验,不一定正确,但也许能跑,请抱着后图心态阅读->
*另外,shader源码是从shadertoy站上搜索的,有很多大神分享了自己制作的shader,真的非常感谢……!
Shadertoy站地址:https://www.shadertoy.com/

首先,需要熟悉renpy的shader代码结构。renpy的shader代码相关的教程在这里:
https://doc.renpy.cn/zh-CN/model.html#renpy.register_shader

基本分为三个部分:
[RenPy] 纯文本查看 复制代码
init python hide:
#shader需要定义在这个部分下面
    renpy.register_shader("shader.background",
    variables="""
    【第一部分:需要使用的变量】

    """,
    fragment_functions="""
   【第二部分:计算画面效果时需要的自定义函数】

    """,
    vertex_100="""
            v_tex_coord = a_tex_coord;
           【其实应该是第三部分但这个我也不太理解究竟是做什么功能的,但保持这么写就行不用动这一部分】
    """,

    fragment_300="""
    【第三部分:将上述第二部分的自定义函数,引入第一部分变量,应用为新画面效果的最终结算步骤】
    """)



这么一看结构应该清晰多了吧!


第一部分中,需要使用的变量,可以参考renpy页面(https://doc.renpy.cn/zh-CN/model.html#renpy.register_shader)学习。这些参数都有自己固定的变量类型,同时,renpy对于变量名称定义有一定的要求:

uniform变量开头必须为 u_,attribute变量开头必须为 a_,varying变量开头必须为 v_。 以 u_renpy_、 a_renpy 和 v_renpy 开头的变量都是Ren’Py预留变量名,不能用在自定义着色器中。




这一部分变量定义需要放在shader的第一部分,比如
[RenPy] 纯文本查看 复制代码
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;



#注意,我们的案例shader中其实不涉及iTime之类的变量,但保险起见你还是可以在第一部分把所有可以定义的都定义进去宁缺毋滥(吧(吧(吧

*坦白说,我也并不是完全明白每一个可定义变量的意义,我摸索得到的结果是
u_time是shader的运行时间,一般仅出现在一些会随时间变化的shader效果中。在shadertoy站的Shader源码中通常被定义为iTime
v_tex_coord和shader源码中的uv高度绑定,看到定义uv就可以直接用v_tex_coord
res0,似乎是纹理的每个点及其对应位置,在shader源码中通常写作iResolution. 也就是说,你如果在shader源码中看到iResolution.xy,可以改为res0.xy
tex0指的是输入shader的画面(=你想应用shader的图片或背景的纹理),一般相当于shader源码中的iChannel0
如果shader源码中出现了iChannel1,那还需要再定义一个新的输入shader纹理tex1。
[RenPy] 纯文本查看 复制代码
        uniform sampler2D tex1;


一般这种出现在你想给原本的画面上叠个图片滤镜之类的情况下

而这些变量的定义名称在shadertoy页面的shader中是不同的,我们在下面会说明如何修改一些不同的变量名

==============================================================

接下来,我们分析shadertoy站的shader基本结构。shadertoy站的每个shader根据作者的写作习惯会有很大区别,但基本上,都是需要应用在第二部分和第三部分的代码
为了解释如何修改,这里选用了一个shader作为范例
https://www.shadertoy.com/view/4tSyzy
在此感谢luluco250老师制作的shader
===================================================
shader范例中,你能看到shader源码中分为两个部分:

[RenPy] 纯文本查看 复制代码
const float pi = atan(1.0) * 4.0;
const int samples = 35;
const float sigma = float(samples) * 0.25;

float gaussian(vec2 i) {
    return 1.0 / (2.0 * pi * pow2(sigma)) * exp(-((pow2(i.x) + pow2(i.y)) / (2.0 * pow2(sigma))));
}

vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
    vec3 col = vec3(0.0);
    float accum = 0.0;
    float weight;
    vec2 offset;
    
    for (int x = -samples / 2; x < samples / 2; ++x) {
        for (int y = -samples / 2; y < samples / 2; ++y) {
            offset = vec2(x, y);
            weight = gaussian(offset);
            col += texture(sp, uv + scale * offset).rgb * weight;
            accum += weight;
        }
    }
    
    return col / accum;
}


类似这种 const、float、vec3、vec2等等开头的部件,其实是shader作者自己定义的计算函数(可以理解为python内的def块?),这部分就会被放在renpy的shader代码中的第二部分
而下面的

[RenPy] 纯文本查看 复制代码
void mainImage(out vec4 color, vec2 coord) {
    vec2 ps = vec2(1.0) / iResolution.xy;
    vec2 uv = coord * ps;
    
    color.rgb = blur(iChannel0, uv, ps);
    color.a = 1.0;
}



void mainImage开头的这部分,是引用上面定义的各种函数(float,vec2,vec3块)来计算最终的画面效果,也就是说,这一部分将要被放在renpy shader的第三部分中。

按照这个规律,我们可以把页面内的代码块粗略地改造成以下格式

[RenPy] 纯文本查看 复制代码
init python hide:
    renpy.register_shader("shader.sample",#随便给shader取一个名字
    variables="""//这部分是我们首先提到的定义变量的代码
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;
    """,
    fragment_functions="""//这部分粘贴的是定义的几个const常量、两个float和vec3格式的函数gaussian、blur部分的代码
    const float pi = atan(1.0) * 4.0;
    const int samples = 35;
    const float sigma = float(samples) * 0.25;

    float gaussian(vec2 i) {
        return 1.0 / (2.0 * pi * pow2(sigma)) * exp(-((pow2(i.x) + pow2(i.y)) / (2.0 * pow2(sigma))));
    }

    vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
        vec3 col = vec3(0.0);
        float accum = 0.0;
        float weight;
        vec2 offset;
        for (int x = -samples / 2; x < samples / 2; ++x) {
            for (int y = -samples / 2; y < samples / 2; ++y) {
                offset = vec2(x, y);
                weight = gaussian(offset);
                col += texture(sp, uv + scale * offset).rgb * weight;
                accum += weight;
            }
        }
        
        return col / accum;
    }
    
    """,
    vertex_100="""
            v_tex_coord = a_tex_coord;
    """,
        
    fragment_300="""//注意看!这部分粘贴的就是void mainimage内部的代码,去掉了voidimage的定义头部分,只有内部代码的纯净版(不

    vec2 ps = vec2(1.0) / iResolution.xy;
    vec2 uv = coord * ps;
    color.rgb = blur(iChannel0, uv, ps);
    color.a = 1.0;
    """)



那么,现在这样就可以跑了吗?
显然不行!O-zzzzzzzzzzzz
因为你会发现,shadertoy的shader代码中,出现了一些我们从来没有定义过的变量,包括
iResolution.xy,coord,iChannel0等等等等(以及包括本篇shader中没有出现的iTime)
这些变量有些是我们已经在上述的第一部分中定义过,有些是renpy的shader内置,但是和shadertoy站大家写的shader变量名称不同。请注意,renpy对于这些变量是有专门的定义名称的!所以我们需要将shadertoy站的变量,转化成renpy可以识别的变量。下面是一个我摸索出来的对应关系,有些已经在第一部分说过了,这里重新列一下。左侧为Shader源码,右侧为应当改成的renpy对应变量名
iTime -> u_time
iResolution.xy -> res0.xy
texture(...一般括号内会出现iChannel0) -> texture2D(....)
iChannel0 -> tex0
iChannel1 -> tex1

因此你需要把原本的shader代码中的对应变量名也统一改成renpy定义的变量名,结果如下:
[RenPy] 纯文本查看 复制代码
init python hide:
    renpy.register_shader("shader.sample",
    variables="""
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;
    """,
    fragment_functions="""
    const float pi = atan(1.0) * 4.0;
    const int samples = 35;
    const float sigma = float(samples) * 0.25;
    float gaussian(vec2 i) {
        return 1.0 / (2.0 * pi * pow2(sigma)) * exp(-((pow2(i.x) + pow2(i.y)) / (2.0 * pow2(sigma))));
    }

    vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
        vec3 col = vec3(0.0);
        float accum = 0.0;
        float weight;
        vec2 offset;
        for (int x = -samples / 2; x < samples / 2; ++x) {
            for (int y = -samples / 2; y < samples / 2; ++y) {
                offset = vec2(x, y);
                weight = gaussian(offset);
                col += texture2D(sp, uv + scale * offset).rgb * weight;//注意,把这里的texture改成了texture2D
                accum += weight;
            }
        }
        
        return col / accum;
    }
    
    """,
    vertex_100="""
            v_tex_coord = a_tex_coord;
    """,
        
    fragment_300="""


    vec2 ps = vec2(1.0) / res0.xy;//把iResolution替换成了res0
    vec2 uv = v_tex_coord;//不管它uv怎么写的,把uv定义成v_tex_coord一般是没问题的。在shader源码中uv常常被定义为fragCoord.xy/iResolution.xy,但如果你改成 gl_FragCoord.xy / res0.xy的话画面就会倒置过来………………不要问我为什么知道………………
    color.rgb = blur(tex0, uv, ps);
    color.a = 1.0;
    """)



——那么,这个shader就可以运行了吗?
——————————显然还是不行……!!!!!!!因为我们没有定义color!!还有pow2这种鬼玩意儿……!
(啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊真是太麻烦了………………!!!!!!)

下面这一部分,不属于通用修改shader源码的教程,而是关于这个范例shader中的修改内容哈(因为shader是不同作者写的,代码和变量命名习惯上会不太一样,但这里可以作为范例学习下)

首先,python里是没有pow2(XX)这玩意儿的……!搜了一圈发现这是C语言中的对2求XX次方的函数,在python里应当改成pow(2,XX)

其次,对比我们修改后的第三部分和源码中的void mainimage代码,会发现源码定义了一个类型为vec4,名称为color的变量,以下是范例中的void mainImage

[RenPy] 纯文本查看 复制代码
void mainImage(out vec4 color, vec2 coord) {
    vec2 ps = vec2(1.0) / iResolution.xy;
    vec2 uv = coord * ps;
    
    color.rgb = blur(iChannel0, uv, ps);
    color.a = 1.0;
}



经常修改shader的朋友都知道(……………………),这个变量实际上就是在定义最终的画面效果,但这么写是不够的,想要把这个画面效果赋给renpy页面,就需要在第三模块重新定义color(毕竟我们把void mainimage的表头删了,压根没地方定义color),再把它赋给gl_Fragcolor<-这个名称指的是画面经过shader一通魔改后真正返回的画面
比如:

[RenPy] 纯文本查看 复制代码
    fragment_300="""
    vec2 ps = vec2(1.0) / res0.xy;
    vec2 uv = v_tex_coord;
    vec4 color;//加了这句话
    color.rgb = blur(tex0, uv, ps);
    color.a = 1.0;
    gl_Fragcolor = color;//再把color的结果赋给最终输出参数gl_Fragcolor
    """)




所以最终魔改的shader为:

[RenPy] 纯文本查看 复制代码
init python hide:
    renpy.register_shader("shader.sample",
    variables="""
        uniform float u_time;
        uniform vec2 u_model_size;
        uniform vec2 res0;
        uniform sampler2D tex0;
        attribute vec2 a_tex_coord;
        varying vec2 v_tex_coord;

    """,
    fragment_functions="""
    const float pi = atan(1.0) * 4.0;
    const int samples = 35;
    const float sigma = float(samples) * 0.25;
    float gaussian(vec2 i) {
        return 1.0 / (2.0 * pi * pow(2,sigma)) * exp(-((pow(2,i.x) + pow(2,i.y)) / (2.0 * pow(2,sigma))));//注意看,这里的pow2()全部改成了pow(2,xxxx)
    }

    vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
        vec3 col = vec3(0.0);
        float accum = 0.0;
        float weight;
        vec2 offset;
        for (int x = -samples / 2; x < samples / 2; ++x) {
            for (int y = -samples / 2; y < samples / 2; ++y) {
                offset = vec2(x, y);
                weight = gaussian(offset);
                col += texture2D(sp, uv + scale * offset).rgb * weight;
                accum += weight;
            }
        }
        
        return col / accum;
    }
    
    """,
    vertex_100="""
            v_tex_coord = a_tex_coord;
    """,
        
    fragment_300="""

    vec2 ps = vec2(1.0) / res0.xy;
    vec2 uv = v_tex_coord;
    vec4 color;
    color.rgb = blur(tex0, uv, ps);
    color.a = 1.0;
    gl_FragColor = color;
    """)



我们可以把这个shader定义在transform里使用。如

[RenPy] 纯文本查看 复制代码
transform sample(child):#定义一个名叫sample的transform效果
    Model().shader("shader.sample").child(child, fit=True)



label start:
  show e001_01 at sample#把sample效果应用在图片上
  e "看看效果如何"





最终效果就是这样啦



不要对本篇经验分享抱有太多期待

不要对本篇经验分享抱有太多期待

基本就是这么个效果

基本就是这么个效果

评分

参与人数 1活力 +300 干货 +3 收起 理由
被诅咒的章鱼 + 300 + 3 我已经对本篇经验分享抱有太多期待了.

查看全部评分

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

本版积分规则

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

GMT+8, 2024-12-4 01:26 , Processed in 0.117940 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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