반응형

오래된 옛날 CRT 모니터나 VHS 비디오 테이프를 재생하는 효과를 표현해 보자.

 

아래와 같이 세팅한다.

Sprite2D - 캐릭터 스프라이트를 추가하고 화면 중앙에 위치시킨다.

ColorRect - Layout - Transform - Size를 뷰포트와 동일하게 조절한다.

                Material에 Shader Material을 추가한다.

                Shader Material을 추가하면 나타나는 Shader에 아래 셰이더 코드를 추가한다.

                셰이더 코드를 추가하면 나타나는 Shader Parameters - Overlay 체크 박스를 체크한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/*
Shader from Godot Shaders - the free shader library.
godotshaders.com/shader/VHS-and-CRT-monitor-effect
 
This shader is under CC0 license. Feel free to use, improve and 
change this shader according to your needs and consider sharing 
the modified result to godotshaders.com.
*/
 
shader_type canvas_item;
 
//*** IMPORTANT! ***/ 
// - If you are using this shader to affect the node it is applied to set 'overlay' to false (unchecked in the instepctor).
// - If you are using this shader as an overlay, and want the shader to affect the nodes below in the Scene hierarchy,
//   set 'overlay' to true (checked in the inspector).
// On Mac there is potentially a bug causing this to not work properly. If that is the case and you want to use the shader as an overlay
// change all "overlay ? SCREEN_TEXTURE : TEXTURE" to only "SCREEN_TEXTURE" on lines 129-140, and "vec2 uv = overlay ? warp(SCREEN_UV) : warp(UV);"
// to "vec2 uv = warp(SCREEN_UV);" on line 98.
uniform bool overlay = false;
 
uniform float scanlines_opacity : hint_range(0.01.0= 0.4;
uniform float scanlines_width : hint_range(0.00.5= 0.25;
uniform float grille_opacity : hint_range(0.01.0= 0.3;
uniform vec2 resolution = vec2(640.0480.0); // Set the number of rows and columns the texture will be divided in. Scanlines and grille will make
                                              // a square based on these values
 
uniform bool pixelate = true// Fill each square ("pixel") with a sampled color, creating a pixel look and a more accurate representation of how
                              // a CRT monitor would work.
 
uniform bool roll = true;
uniform float roll_speed = 8.0// Positive values are down, negative are up
uniform float roll_size : hint_range(0.0100.0= 15.0;
uniform float roll_variation : hint_range(0.15.0= 1.8// This valie is not an exact science. You have to play around with the value to find
                                                           // a look you like. How this works is explained in the code below.
uniform float distort_intensity : hint_range(0.00.2= 0.05// The distortion created by the rolling effect.
 
uniform float noise_opacity : hint_range(0.01.0= 0.4;
uniform float noise_speed = 5.0// There is a movement in the noise pattern that can be hard to see first. This sets the speed of that movement.
 
uniform float static_noise_intensity : hint_range(0.01.0= 0.06;
 
uniform float aberration : hint_range(-1.01.0= 0.03// Chromatic aberration, a distortion on each color channel.
uniform float brightness = 1.4// When adding scanline gaps and grille the image can get very dark. Brightness tries to compensate for that.
uniform bool discolor = true// Add a discolor effect simulating a VHS
 
uniform float warp_amount :hint_range(0.05.0= 1.0// Warp the texture edges simulating the curved glass of a CRT monitor or old TV.
uniform bool clip_warp = false;
 
uniform float vignette_intensity = 0.4// Size of the vignette, how far towards the middle it should go.
uniform float vignette_opacity : hint_range(0.01.0= 0.5;
 
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
 
// Used by the noise functin to generate a pseudo random value between 0.0 and 1.0
vec2 random(vec2 uv){
    uv = vec2( dot(uv, vec2(127.1,311.7) ),
               dot(uv, vec2(269.5,183.3) ) );
    return -1.0 + 2.0 * fract(sin(uv) * 43758.5453123);
}
 
// Generate a Perlin noise used by the distortion effects
float noise(vec2 uv) {
    vec2 uv_index = floor(uv);
    vec2 uv_fract = fract(uv);
 
    vec2 blur = smoothstep(0.01.0, uv_fract);
 
    return mix( mix( dot( random(uv_index + vec2(0.0,0.0) ), uv_fract - vec2(0.0,0.0) ),
                     dot( random(uv_index + vec2(1.0,0.0) ), uv_fract - vec2(1.0,0.0) ), blur.x),
                mix( dot( random(uv_index + vec2(0.0,1.0) ), uv_fract - vec2(0.0,1.0) ),
                     dot( random(uv_index + vec2(1.0,1.0) ), uv_fract - vec2(1.0,1.0) ), blur.x), blur.y) * 0.5 + 0.5;
}
 
// Takes in the UV and warps the edges, creating the spherized effect
vec2 warp(vec2 uv){
    vec2 delta = uv - 0.5;
    float delta2 = dot(delta.xy, delta.xy);
    float delta4 = delta2 * delta2;
    float delta_offset = delta4 * warp_amount;
    
    return uv + delta * delta_offset;
}
 
// Adds a black border to hide stretched pixel created by the warp effect
float border (vec2 uv){
    float radius = min(warp_amount, 0.08);
    radius = max(min(min(abs(radius * 2.0), abs(1.0)), abs(1.0)), 1e-5);
    vec2 abs_uv = abs(uv * 2.0 - 1.0- vec2(1.01.0+ radius;
    float dist = length(max(vec2(0.0), abs_uv)) / radius;
    float square = smoothstep(0.961.0, dist);
    return clamp(1.0 - square, 0.01.0);
}
 
// Adds a vignette shadow to the edges of the image
float vignette(vec2 uv){
    uv *= 1.0 - uv.xy;
    float vignette = uv.x * uv.y * 15.0;
    return pow(vignette, vignette_intensity * vignette_opacity);
}
 
void fragment()
{
    vec2 uv = overlay ? warp(SCREEN_UV) : warp(UV); // Warp the uv. uv will be used in most cases instead of UV to keep the warping
    vec2 text_uv = uv;
    vec2 roll_uv = vec2(0.0);
    float time = roll ? TIME : 0.0;
    
 
    // Pixelate the texture based on the given resolution.
    if (pixelate)
    {
        text_uv = ceil(uv * resolution) / resolution;
    }
    
    // Create the rolling effect. We need roll_line a bit later to make the noise effect.
    // That is why this runs if roll is true OR noise_opacity is over 0.
    float roll_line = 0.0;
    if (roll || noise_opacity > 0.0)
    {
        // Create the areas/lines where the texture will be distorted.
        roll_line = smoothstep(0.30.9, sin(uv.y * roll_size - (time * roll_speed) ) );
        // Create more lines of a different size and apply to the first set of lines. This creates a bit of variation.
        roll_line *= roll_line * smoothstep(0.30.9, sin(uv.y * roll_size * roll_variation - (time * roll_speed * roll_variation) ) );
        // Distort the UV where where the lines are
        roll_uv = vec2(( roll_line * distort_intensity * (1.-UV.x)), 0.0);
    }
    
    vec4 text;
    if (roll)
    {
        // If roll is true distort the texture with roll_uv. The texture is split up into RGB to 
        // make some chromatic aberration. We apply the aberration to the red and green channels accorging to the aberration parameter
        // and intensify it a bit in the roll distortion.
        text.r = texture(SCREEN_TEXTURE, text_uv + roll_uv * 0.8 + vec2(aberration, 0.0* .1).r;
        text.g = texture(SCREEN_TEXTURE, text_uv + roll_uv * 1.2 - vec2(aberration, 0.0* .1 ).g;
        text.b = texture(SCREEN_TEXTURE, text_uv + roll_uv).b;
        text.a = 1.0;
    }
    else
    {
        // If roll is false only apply the aberration without any distorion. The aberration values are very small so the .1 is only 
        // to make the slider in the Inspector less sensitive.
        text.r = texture(SCREEN_TEXTURE, text_uv + vec2(aberration, 0.0* .1).r;
        text.g = texture(SCREEN_TEXTURE, text_uv - vec2(aberration, 0.0* .1).g;
        text.b = texture(SCREEN_TEXTURE, text_uv).b;
        text.a = 1.0;
    }
    
    float r = text.r;
    float g = text.g;
    float b = text.b;
    
    uv = warp(UV);
    
    // CRT monitors don't have pixels but groups of red, green and blue dots or lines, called grille. We isolate the texture's color channels 
    // and divide it up in 3 offsetted lines to show the red, green and blue colors next to each other, with a small black gap between.
    if (grille_opacity > 0.0){
        
        float g_r = smoothstep(0.850.95, abs(sin(uv.x * (resolution.x * 3.14159265))));
        r = mix(r, r * g_r, grille_opacity);
        
        float g_g = smoothstep(0.850.95, abs(sin(1.05 + uv.x * (resolution.x * 3.14159265))));
        g = mix(g, g * g_g, grille_opacity);
        
        float b_b = smoothstep(0.850.95, abs(sin(2.1 + uv.x * (resolution.x * 3.14159265))));
        b = mix(b, b * b_b, grille_opacity);
        
    }
    
    // Apply the grille to the texture's color channels and apply Brightness. Since the grille and the scanlines (below) make the image very dark you
    // can compensate by increasing the brightness.
    text.r = clamp(r * brightness, 0.01.0);
    text.g = clamp(g * brightness, 0.01.0);
    text.b = clamp(b * brightness, 0.01.0);
    
    // Scanlines are the horizontal lines that make up the image on a CRT monitor. 
    // Here we are actual setting the black gap between each line, which I guess is not the right definition of the word, but you get the idea  
    float scanlines = 0.5;
    if (scanlines_opacity > 0.0)
    {
        // Same technique as above, create lines with sine and applying it to the texture. Smoothstep to allow setting the line size.
        scanlines = smoothstep(scanlines_width, scanlines_width + 0.5, abs(sin(uv.y * (resolution.y * 3.14159265))));
        text.rgb = mix(text.rgb, text.rgb * vec3(scanlines), scanlines_opacity);
    }
    
    // Apply the banded noise.
    if (noise_opacity > 0.0)
    {
        // Generate a noise pattern that is very stretched horizontally, and animate it with noise_speed
        float noise = smoothstep(0.40.5, noise(uv * vec2(2.0200.0+ vec2(10.0, (TIME * (noise_speed))) ) );
        
        // We use roll_line (set above) to define how big the noise should be vertically (multiplying cuts off all black parts).
        // We also add in some basic noise with random() to break up the noise pattern above. The noise is sized according to 
        // the resolution value set in the inspector. If you don't like this look you can 
        // change "ceil(uv * resolution) / resolution" to only "uv" to make it less pixelated. Or multiply resolution with som value
        // greater than 1.0 to make them smaller.
        roll_line *= noise * scanlines * clamp(random((ceil(uv * resolution) / resolution) + vec2(TIME * 0.80.0)).x + 0.80.01.0);
        // Add it to the texture based on noise_opacity
        text.rgb = clamp(mix(text.rgb, text.rgb + roll_line, noise_opacity), vec3(0.0), vec3(1.0));
    }
    
    // Apply static noise by generating it over the whole screen in the same way as above
    if (static_noise_intensity > 0.0)
    {
        text.rgb += clamp(random((ceil(uv * resolution) / resolution) + fract(TIME)).x, 0.01.0* static_noise_intensity;
    }
    
    // Apply a black border to hide imperfections caused by the warping.
    // Also apply the vignette
    text.rgb *= border(uv);
    text.rgb *= vignette(uv);
    // Hides the black border and make that area transparent. Good if you want to add the the texture on top an image of a TV or monitor.
    if (clip_warp)
    {
        text.a = border(uv);
    }
    
    // Apply discoloration to get a VHS look (lower saturation and higher contrast)
    // You can play with the values below or expose them in the Inspector.
    float saturation = 0.5;
    float contrast = 1.2;
    if (discolor)
    {
        // Saturation
        vec3 greyscale = vec3(text.r + text.g + text.b) / 3.;
        text.rgb = mix(text.rgb, greyscale, saturation);
        
        // Contrast
        float midpoint = pow(0.52.2);
        text.rgb = (text.rgb - vec3(midpoint)) * contrast + midpoint;
    }
    
    COLOR = text;
}
 

 

실행하면 위와 같은 효과가 표현된다.

 

Shader Parameters의 옵션을 바꿔가며 여러가지 효과를 적용해 보자.

 

※ 참고

VHS and CRT monitor effect

 

반응형
Posted by J-sean
:
반응형

2D 안개 효과를 만들어 보자.

 

ParallaxBackground를 추가하고 Layer를 높은 숫자로 바꾼다.

 

ParallaxLayer를 추가하고 Mirroring을 뷰포트와 동일하게 설정한다.

 

ColorRect를 추가하고 Size를 뷰포트와 동일하게 설정한다.

 

ColorRect에 ShaderMaterial을 추가하고 Shader를 생성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
shader_type canvas_item;
//render_mode unshaded; // optional
 
// Noise texture
uniform sampler2D noise_texture: repeat_enable, filter_nearest;
// Fog density
uniform float density: hint_range(0.01.0= 0.25;
// Fog speed
uniform vec2 speed = vec2(0.020.01);
 
// Called for every pixel the material is visible on
void fragment() {
    // Make the fog slowly move
    vec2 uv = UV + speed * TIME;
    // Sample the noise texture
    float noise = texture(noise_texture, uv).r;
    // Convert the noise from the (0.0, 1.0) range to the (-1.0, 1.0) range
    // and clamp it between 0.0 and 1.0 again
    float fog = clamp(noise * 2.0 - 1.00.01.0);
    // Apply the fog effect
    COLOR.a *= fog * density;
}
 

 

셰이더는 위와 같이 작성한다.

 

Shader Parameters - Noise Texture에 Noise Texture 2D를 추가한다.

 

사이즈는 뷰포트와 동일하게, Seamless 옵션 체크, Noise - FastNoiseLite를 추가한다.

 

적당한 주인공 스프라이트를 추가한다.

 

여러가지 옵션을 변경하며 적당한 안개 효과를 찾는다.

 

float fog = clamp(noise * 2.0 - 1.0, 0.0, 1.0);

필요하다면 셰이더 코드에서 2.0 이라는 수치를 적당히 변경해 보자.

 

※ 참고

2D fog overlay

 

반응형
Posted by J-sean
:
반응형

윈도우 사이즈 변경 시 적용되는 뷰포트 화면 비율 모드를 설정해 보자.

 

스프라이트를 하나 추가한다.

 

실행하면 스프라이트가 나타난다.

 

윈도우 사이즈를 변경하면 화면이 잘린다.

 

Project Settings... - Display - Window - Stretch - Mode를 viewport로 변경한다.

 

 

사이즈를 변경하면 뷰포트의 비율이 유지된 상태로 늘어나거나 줄어든다.

 

Aspect를 expand로 변경한다.

 

뷰포트의 비율이 유지되지 않은 상태로 늘어나거나 줄어든다.

 

반응형
Posted by J-sean
:
반응형

셰이더를 이용해 멋진 빛 효과를 만들어 보자.

 

Sprite2D를 추가하고 Texture를 지정한다. 아무 이미지나 지정해도 상관 없지만 여기서는 Canvas Texture를 지정하고 Scale을 조절했다.

 

Material에 Shader Material을 지정하고 Shader 파일(GodRay.gdshader)을 생성했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
Shader from Godot Shaders - the free shader library.
godotshaders.com/shader/god-rays
 
Feel free to use, improve and change this shader according to your needs
and consider sharing the modified result on godotshaders.com.
*/
 
shader_type canvas_item;
 
uniform float angle = -0.3;
uniform float position = -0.2;
uniform float spread : hint_range(0.01.0= 0.5;
uniform float cutoff : hint_range(-1.01.0= 0.1;
uniform float falloff : hint_range(0.01.0= 0.2;
uniform float edge_fade : hint_range(0.01.0= 0.15;
 
uniform float speed = 1.0;
uniform float ray1_density = 8.0;
uniform float ray2_density = 30.0;
uniform float ray2_intensity : hint_range(0.01.0= 0.3;
 
uniform vec4 color : source_color = vec4(1.00.90.650.8);
 
uniform bool hdr = false;
uniform float seed = 5.0;
 
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
 
// Random and noise functions from Book of Shader's chapter on Noise.
float random(vec2 _uv) {
    return fract(sin(dot(_uv.xy, vec2(12.989878.233))) * 43758.5453123);
}
 
float noise (in vec2 uv) {
    vec2 i = floor(uv);
    vec2 f = fract(uv);
 
    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.00.0));
    float c = random(i + vec2(0.01.0));
    float d = random(i + vec2(1.01.0));
 
    // Smooth Interpolation
 
    // Cubic Hermine Curve. Same as SmoothStep()
    vec2 u = f * f * (3.0-2.0 * f);
 
    // Mix 4 coorners percentages
    return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}
 
mat2 rotate(float _angle){
    return mat2(vec2(cos(_angle), -sin(_angle)), vec2(sin(_angle), cos(_angle)));
}
 
vec4 screen(vec4 base, vec4 blend){
    return 1.0 - (1.0 - base* (1.0 - blend);
}
 
void fragment()
{    
    // Rotate, skew and move the UVs
    vec2 transformed_uv = (rotate(angle) * (UV - position))  / ((UV.y + spread) - (UV.y * spread));
    
    // Animate the ray according the the new transformed UVs
    vec2 ray1 = vec2(transformed_uv.x * ray1_density + sin(TIME * 0.1 * speed) * (ray1_density * 0.2+ seed, 1.0);
    vec2 ray2 = vec2(transformed_uv.x * ray2_density + sin(TIME * 0.2 * speed) * (ray1_density * 0.2+ seed, 1.0);
    
    // Cut off the ray's edges
    float cut = step(cutoff, transformed_uv.x) * step(cutoff, 1.0 - transformed_uv.x);
    ray1 *= cut;
    ray2 *= cut;
    
    // Apply the noise pattern (i.e. create the rays)
    float rays;
    
    if (hdr){
        // This is not really HDR, but check this to not clamp the two merged rays making 
        // their values go over 1.0. Can make for some nice effect
        rays = noise(ray1) + (noise(ray2) * ray2_intensity);
    }
    else{
         rays = clamp(noise(ray1) + (noise(ray2) * ray2_intensity), 0.01.0);
    }
    
    // Fade out edges
    rays *= smoothstep(0.0, falloff, (1.0 - UV.y)); // Bottom
    rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, transformed_uv.x); // Left
    rays *= smoothstep(0.0 + cutoff, edge_fade + cutoff, 1.0 - transformed_uv.x); // Right
    
    // Color to the rays
    vec3 shine = vec3(rays) * color.rgb;
 
    // Try different blending modes for a nicer effect. "Screen" is included in the code,
    // but take a look at https://godotshaders.com/snippet/blending-modes/ for more.
    // With "Screen" blend mode:
    shine = screen(texture(SCREEN_TEXTURE, SCREEN_UV), vec4(color)).rgb;
    
    COLOR = vec4(shine, rays * color.a);
}
 

 

Shader 파일(GodRay.gdshader)에 위와 같은 코드를 작성한다.

 

스프라이트에 셰이더가 적용된다.

 

 

Shader Parameters - Speed를 조절해 보자.

 

멋진 빛 효과가 표현된다.

 

Material - 우클릭 - Save As... 를 선택한다.

 

Material을 저장하면(GodRay.tres) 나중에 간단히 다시 사용 할 수 있다.

 

 

코드에서 Shader Parameters에 실시간 접근해야 한다면 스크립트(Control.cs)를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using Godot;
 
public partial class Control : Sprite2D
{
    //[Export] public ShaderMaterial myShaderMaterial;
    // 위 주석을 해제하고 빌드하면 Editor에 My Shader Material이 표시된다.
    // 여기에 사용하는 Material(GodRay.tres)을 넣고 직접 접근할 수도 있다.
 
    public int speed;
 
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        speed = 1;
    }
 
    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
        if (Input.IsActionJustPressed("ui_up"))
            (Material as ShaderMaterial).SetShaderParameter("speed"++speed);
        if (Input.IsActionJustPressed("ui_down"))
            (Material as ShaderMaterial).SetShaderParameter("speed"--speed);
    }
}
 

 

위와 같이 코드를 작성하면 Shader Material의 파라미터를 언제든 변경할 수 있다.

 

※ 참고

God rays

 

 

반응형
Posted by J-sean
:

[Godot] Path2D PathFollow2D

Godot 2024. 2. 22. 22:10 |
반응형

정해진 길을 따라 움직이는 오브젝트를 만들어 보자.

 

Path2D를 추가하고 툴 버튼을 이용해 path를 그린다.

 

PathFollow2D, Sprite2D를 추가한다. PathFollow2D는 Path2D의 자식 노드이어야 한다.

 

Node2D에 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using Godot;
 
public partial class Control : Node2D
{
    public PathFollow2D follower;
 
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        follower = GetChild<Path2D>(0).GetChild<PathFollow2D>(0);
    }
 
    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
        follower.ProgressRatio += (float)delta;
    }
}
 

 

위와 같이 코드를 작성한다.

 

실행하면 스프라이트가 Path를 따라 움직인다.

 

반응형
Posted by J-sean
:
반응형

2D 씬에 3D 오브젝트를 표현해 보자.

 

2D 씬에 스프라이트를 생성하고 Texture는 ViewportTexture를 지정한다.

 

SubViewport를 생성한다.

 

3D 씬에 Torus, Camera, Light를 생성하고 적당히 배치해 저장한다.

 

저장한 3D 씬을 SubViewport 자식 노드로 추가한다.

 

스프라이트 Texture - Viewport Path에 SubviewPort를 지정한다.

 

위와 같이 3D 카메라에 보이는 화면이 텍스쳐로 표현된다.

 

Transparent BG 옵션을 선택하면 배경이 투명해진다.

 

3D 오브젝트에 스크립트를 추가해 움직임을 적용하면 스프라이트에 반영된다.

 

반응형
Posted by J-sean
:

[Godot] Line2D 선 그리기

Godot 2024. 2. 21. 18:02 |
반응형

간단한 선을 그려보자.

 

Line2D 노드 두 개와 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using Godot;
using System.Collections.Generic;
 
public partial class Control : Node2D
{
    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        DrawCircle();
        DrawPentagram();
    }
 
    // 원 그리기
    public async void DrawCircle()
    {
        Line2D line = GetNode<Line2D>("Line2D");
        //line.Closed = true;
        // Closed를 여기서 변경하면 원이 그려지는 방식이 달라진다.
        line.Width = 2;
        line.DefaultColor = Colors.Red;
        line.Position = new Vector2(200200);
 
        float radius = 100;
        float angle;
 
        for (int i = 0; i < 360; i++)
        {
            angle = Mathf.DegToRad(i);
            line.AddPoint(CalcCirclePoint(radius, angle));
            await ToSignal(GetTree().CreateTimer(0.01), SceneTreeTimer.SignalName.Timeout);
        }
 
        line.Closed = true;
    }
 
    // 원 포인트 계산
    public Vector2 CalcCirclePoint(float radius, float angle)
    {
        float x = Mathf.Cos(angle);
        float y = Mathf.Sin(angle);
 
        return new Vector2(x * radius, y * radius);
    }
 
    // 별 그리기
    public async void DrawPentagram()
    {
        Line2D line = GetNode<Line2D>("Line2D2");
        line.Width = 2;
        line.DefaultColor = Colors.Blue;
        line.Position = new Vector2(200200);
 
        List<Vector2> points = new List<Vector2>();
        points.Add(new Vector2(6080));
        points.Add(new Vector2(0-100));
        points.Add(new Vector2(-6080));
        points.Add(new Vector2(95-25));
        points.Add(new Vector2(-95-25));
        //points.Add(new Vector2(60, 80));
        // 마지막에 Closed 프로퍼티에 true를 대입하기 때문에 별의 시작점을 다시 추가할
        // 필요는 없다.
 
        for (int i = 0; i < points.Count; i++)
        {
            line.AddPoint(points[i]);
            await ToSignal(GetTree().CreateTimer(1), SceneTreeTimer.SignalName.Timeout);
        }
 
        line.Closed = true;
    }
}
 

 

위와 같이 코드를 작성한다.

 

 

※ 참고

Line2D

Custom drawing in 2D

 

반응형
Posted by J-sean
:
반응형

Godot Sprite에는 Colorkey를 설정하는 옵션이 없다.PNG 이미지는 알파 채널을 통해 투명 배경을 설정할 수 있어 상관 없지만 JPG, BMP같은 이미지 파일은 투명 배경이 없어 곤란하다. 하지만 셰이더를 이용해 같은 효과를 만들 수 있다.

 

스프라이트를 추가하고 텍스쳐를 지정한다.

 

Material - New Shader Material을 선택한다.

 

Shader - New Shader를 선택한다.

 

적당한 이름을 지정하고 셰이더 파일을 생성한다.

 

 

Shader Editor를 활성화 한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
shader_type canvas_item;
// Get texture and a key that we want as transparent
uniform sampler2D Diffuse;
uniform vec4 ChromaKey : source_color;
 
void vertex() {
    // Called for every vertex the material is visible on.
}
 
void fragment() {
    // Called for every pixel the material is visible on.
    
    // The color value is the sampled pixel color
    COLOR = texture(Diffuse, UV);
 
    // If the color is the same as the chroma key, make it transparent.
    if (COLOR == ChromaKey) {
        COLOR.a = 0.0;
    }
}
 
//void light() {
    // Called for every pixel for every light affecting the CanvasItem.
    // Uncomment to replace the default light processing function with this one.
//}
 

 

위와 같은 코드를 작성한다.

 

Shader Parameters를 아래와 같이 지정한다.

ChromaKey: R:54 G:61 B:82 A:255 (Godot 이미지의 배경색)

Diffuse: 텍스쳐 이미지와 동일하게 지정

※ Diffuse에 이미지 지정시 Texture 파라미터의 filter를 선택할 수 없어 이미지가 흐리게 보일 수 있다. 필요하다면 셰이더 코드에서 Diffuse 선언을 아래와 같이 수정한다.

uniform sampler2D Diffuse : filter_nearest;

 

Godot 이미지의 배경이 투명해졌다.

 

 

하지만 게임을 실행해 보면 배경이 투명하지 않다.

 

1
2
3
4
// If the color is the same as the chroma key, make it transparent.
if (distance(COLOR, ChromaKey) < 0.001) {
    COLOR.a = 0.0;
}
 

 

vec4 타입의 ChromaKey와 COLOR 데이터 타입의 비교에서 약간의 오차가 발생하는거 같다.

if()을 위와 같이 수정한다.

 

원하는 색이 정상적으로 투명하게 변한다.

 

※ 참고

이렇게 하나의 스프라이트에 셰이더를 적용하면 같은 씬의 다른 스프라이트에도 모두 같은 방식으로 셰이더를 적용해야 아래와 같은 에러가 생기지 않는 경우가 발생할 수 있다.

E 0:00:01:0199   swap_buffers: Vulkan: Cannot submit graphics queue. Error code: VK_ERROR_DEVICE_LOST
  <C++ Error>    Condition "err" is true. Returning: ERR_CANT_CREATE
  <C++ Source>   drivers/vulkan/vulkan_context.cpp:2536 @ swap_buffers()
E 0:00:01:0208   prepare_buffers: Vulkan: Did not create swapchain successfully. Error code: VK_NOT_READY
  <C++ Error>    Condition "err != VK_SUCCESS" is true. Breaking.
  <C++ Source>   drivers/vulkan/vulkan_context.cpp:2459 @ prepare_buffers()

 

반응형
Posted by J-sean
: