Experience modifying the shader on Renpy from a comrade from China
birctreel
https://www.renpy.cn/thread-1487-1-1.html
Yesterday, I discussed a lot with Mr. Xizhu about how to apply shaders to layers. When we talked about making more shader programs suitable for renpy, I would like to share my experience on modifying the shader code to adapt to the renpy engine
*Sorry In fact, I don’t know shaders, I don’t know much about renpy, and I don’t even know python (…) The following experience is based on comparing the differences between many shader source codes and renpy versions (and after teacher AxelKong modified my shader code last night). The modification experience may not be correct, but it may work. Please read it with the mentality of following the picture ->
*In addition, the shader source code was searched from the shadertoy website. Many masters have shared their own shaders. Thank you very much. ...!
Shadertoy website address:
https://www.shadertoy.com/
First of all, you need to be familiar with the shader code structure of renpy. Tutorials related to renpy's shader code are here:
https://doc.renpy.cn/zh-CN/model.html#r ... ter_shader
Code: Select all
init python hide.
#shader needs to be defined under this section
renpy.register_shader("shader.background",
variables="""
[Part 1: Variables to use
""",
fragment_functions="""
[Part II: Custom Functions Needed to Calculate Screen Effects]
""",
vertex_100="""
v_tex_coord = a_tex_coord;
[actually it should be the third part but I don't really understand what this does, but just keep it that way and don't move this part]
""","
fragment_300="""
[Part III: the custom function of the second part of the above, the introduction of the first part of the variable, applied to the final settlement of the new screen effect step]
""")
The structure should be much clearer now!
In the first part, you can refer to the renpy page (
https://doc.renpy.cn/zh-CN/model.html#r ... ter_shader) to learn the variables that need to be used. These parameters have their own fixed variable types. At the same time, renpy has certain requirements for variable name definition:
The uniform variable must start with u_, the attribute variable must start with a_, and the varying variable must start with v_. Variables starting with u_renpy_, a_renpy and v_renpy are Ren'Py reserved variable names and cannot be used in custom shaders.
This part of the variable definition needs to be placed in the first part of the shader, such as
Code: Select all
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;
Note that our case shader does not actually involve variables such as iTime, but to be on the safe side, you can still define everything that can be defined in the first part
. Understand the meaning of each definable variable, the result I got is that
u_time is the running time of the shader, which generally only appears in some shader effects that change with time. In the Shader source code of the shadertoy station, it is usually defined as iTime
v_tex_coord and The uv height in the shader source code is bound. When you see the definition of uv, you can directly use v_tex_coord
res0, which seems to be each point of the texture and its corresponding position. It is usually written as iResolution in the shader source code. In other words, if you are in the shader source code When you see iResolution.xy, you can change it to res0.xy.
tex0 refers to the input shader screen (= the picture or background texture you want to apply the shader to), which is generally equivalent to iChannel0 in the shader source code
. If iChannel1 appears in the shader source code, Then you need to define a new input shader texture tex1.
Generally this happens when you want to add a picture filter to the original screen.
The definition names of these variables are different in the shader on the shadertoy page. We will explain how to modify some different variable names below.
================================================== ============
Next, we analyze the basic structure of the shader of the shadertoy station. Each shader on the shadertoy site will be very different according to the author's writing habits, but basically, they are all codes that need to be applied in the second and third parts.
In order to explain how to modify, a shader is selected as an example
https://www.shadertoy.com/view/4tSyzy
would like to thank Mr. luluco250 for the shader
================================== ===================
In the shader example, you can see that the shader source code is divided into two parts:
Code: Select all
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;
}
Components starting with const, float, vec3, vec2, etc. are actually calculation functions defined by the shader author (can be understood as a def block in python?). This part will be placed in the shader code of renpy. Part Two
And the following
Code: Select all
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;
}
The part at the beginning of void mainImage refers to the various functions (float, vec2, vec3 blocks) defined above to calculate the final picture effect. In other words, this part will be placed in the third part of the renpy shader.
According to this rule, we can roughly transform the code blocks in the page into the following format
Code: Select all
init python hide.
renpy.register_shader("shader.sample", # give the shader a random name
variables="""//This is the part of the code that defines the variables we mentioned first.
uniform float u_time.
uniform vec2 u_model_size.
uniform vec2 res0; uniform sampler2D tex0
uniform sampler2D tex0; uniform
attribute vec2 a_tex_coord; varying vec2 v_tex_coord; uniform vec2 v_tex_coord
varying vec2 v_tex_coord; uniform vec2 v_tex_coord
""",
fragment_functions="""//this part of the paste is the definition of several const constants, two float and vec3 format functions gaussian, blur part of the code
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); vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
float accum = 0.0;
float weight; vec2 offset; vec2 scale
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); ++y) {
weight = gaussian(offset); col += texture(sp, sp, y); ++y
accum += weight; }
}
}
return col / accum; }
}
""",
vertex_100="""
v_tex_coord = a_tex_coord;
""", v_tex_coord = a_tex_coord; """,
fragment_300=""""// Pay attention! This part of the paste is the code inside the void mainimage, removing the definition header part of the voidimage, only the pure version of the internal code (not the
vec2 ps = vec2(1.0) / iResolution.xy;
vec2 uv = coord * ps;
color.rgb = blur(iChannel0, uv, ps);
color.a = 1.0;
""")
So, can I run away now?
Obviously not! O-zzzzzzzzzzzz
Because you will find that in the shadertoy shader code, there are some variables that we have never defined, including
iResolution.xy, coord, iChannel0, etc. (and including iTime that does not appear in this shader)
. Some of the variables have been defined in the first part above, and some are built-in to the renpy shader, but their names are different from the shader variables written by everyone on the shadertoy site. Please note that renpy has specially defined names for these variables! So we need to convert the variables of the shadertoy station into variables that renpy can recognize. The following is a corresponding relationship that I have figured out. Some of them have already been mentioned in the first part, so I will list them again here. The left side is the Shader source code, and the right side is the renpy corresponding variable name that should be changed to
iTime -> u_time
iResolution.xy -> res0.xy
texture(...generally iChannel0 will appear in parentheses) -> texture2D(....)
iChannel0 -> tex0
iChannel1 -> tex1
Therefore, you need to change the corresponding variable names in the original shader code to the variable names defined by renpy. The results are as follows:
Code: Select all
init python hide.
renpy.register_shader("shader.sample",
variables="""
uniform float u_time; uniform vec2 u_model_size; uniform
uniform vec2 u_model_size; uniform vec2 res0; uniform
uniform vec2 res0; uniform sampler2D tex0
uniform sampler2D tex0; uniform
attribute vec2 a_tex_coord; varied vec2 v_tex_coord; varied vec2 v_tex_coord; uniform
varying vec2 v_tex_coord; uniform vec2 res0; uniform sampler2D tex0; attribute vec2 a_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); vec3 blur(sampler2D sp, vec2 uv, vec2 scale) {
float accum = 0.0;
float weight; vec2 offset; vec2 scale
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); ++y) {
weight = gaussian(offset).
col += texture2D(sp, uv + scale * offset).rgb * weight; //note, change texture here to texture2D
accum += weight; }
}
}
return col / accum; }
}
""",
vertex_100="""
v_tex_coord = a_tex_coord;
""", fragment_300=""
fragment_300="""
vec2 ps = vec2(1.0) / res0.xy; // replace iResolution with res0
vec2 uv = v_tex_coord;//regardless of how it uv is written, defining uv as v_tex_coord is generally fine. In shader source code uv is often defined as fragCoord.xy / iResolution.xy, but if you change it to gl_FragCoord.xy / res0.xy then the image will be inverted ......... ......... don't ask me why I know ..................
color.rgb = blur(tex0, uv, ps);
color.a = 1.0;
""")
So, can this shader be run?
——————————Obviously it still doesn’t work…! ! ! ! ! ! ! Because we did not define color! ! And there are ghost things like pow2...!
(Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa That is so troublesome...!!!!!!)
The following part is not a general tutorial for modifying shader source code, but It’s about the modifications in this example shader (because the shader is written by different authors, the code and variable naming habits will be different, but it can be used as an example to learn from). First of all
, there is no such thing as pow2(XX) in Python. Damn...! After searching around, I found that this is a function in C language that raises 2 to the power of XX. In python, it should be changed to pow(2,XX).
Secondly, compare our modified third part with the void mainimage code in the source code. You will find that the source code defines a variable of type vec4 and name color. The following is the void mainImage in the example.
Code: Select all
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;
}
riends who often modify shaders know (……………………) that this variable actually defines the final picture effect, but writing it like this is not enough. If you want to assign this picture effect to the renpy page, you need to Redefine color in the third module (after all, we deleted the header of void mainimage, there is no place to define color), and then assign it to gl_Fragcolor <- this name refers to the picture that is actually returned after the shader changes it.
for example:
Code: Select all
fragment_300="""
vec2 ps = vec2(1.0) / res0.xy;
vec2 uv = v_tex_coord;
vec4 color; //added this line
color.rgb = blur(tex0, uv, ps);
color.a = 1.0;
gl_Fragcolor = color;// then assign the result of color to the final output parameter gl_Fragcolor
""")
So the final modified shader is:
Code: Select all
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;
""")
We can define this shader and use it in transform. like
Code: Select all
transform sample(child):# Define a transform effect called sample
Model().shader("shader.sample").child(child, fit=True)
label start.
show e001_01 at sample#Apply sample effect to image
e "See how it works"