Skip to content

Commit 1905550

Browse files
committed
render_opengl: Use new shadowing algorithm in fragment shader fallback
Also lower minimum OpenGL requirement to 3.1.
1 parent e14b5d6 commit 1905550

12 files changed

Lines changed: 331 additions & 250 deletions

File tree

engine/modules/render/res/render/shader/lighting_frag.frag

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22

33
#extension GL_ARB_compute_shader : enable
44

5-
// fallback fragment shader currently uses atomics which don't support floats
6-
#ifdef GL_ARB_compute_shader
7-
#define IS_RAY_BUFFER_FLOAT 1
8-
#endif
9-
105
#define PI 3.14159
116
#define TWO_PI (PI * 2.0)
127

@@ -35,11 +30,7 @@ in vec2 TexCoord;
3530

3631
out vec4 out_Color;
3732

38-
#ifdef IS_RAY_BUFFER_FLOAT
39-
layout(binding = 0) uniform samplerBuffer u_RayBuffer;
40-
#else
41-
layout(binding = 0) uniform usamplerBuffer u_RayBuffer;
42-
#endif
33+
layout(binding = 0) uniform sampler2D u_ShadowMap;
4334

4435
layout(std140, binding = 0) uniform Scene {
4536
vec4 AmbientLightColor;
@@ -53,17 +44,6 @@ layout(std140, binding = 1) uniform Viewport {
5344
Light2D Lights[32];
5445
} viewport;
5546

56-
// annoying workaround bc our GLSL parser chokes on mid-function macros
57-
#ifdef IS_RAY_BUFFER_FLOAT
58-
float fetch_occl_dist(int ray_index) {
59-
return texelFetch(u_RayBuffer, ray_index).r;
60-
}
61-
#else
62-
float fetch_occl_dist(int ray_index) {
63-
return texelFetch(u_RayBuffer, ray_index).r / float(DIST_MULTIPLIER);
64-
}
65-
#endif
66-
6747
// Applies a power-like function with a non-integer exponent without doing an
6848
// expensive pow() operation.
6949
//
@@ -123,8 +103,7 @@ void main() {
123103
bool is_occluded = false;
124104
float occl_dist;
125105
if (light.is_occludable != 0U) {
126-
int ray_lookup_index = int(i * RAY_COUNT + ray_index);
127-
occl_dist = fetch_occl_dist(ray_lookup_index);
106+
occl_dist = texelFetch(u_ShadowMap, ivec2(ray_index, i), 0).r;
128107
is_occluded = dist >= occl_dist;
129108
}
130109

engine/modules/render/res/render/shader/shadowmap_comp.comp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ layout(std140, binding = 3) uniform Viewport {
3434
} viewport;
3535

3636
layout(binding = 0) uniform sampler2D u_OpacityMap;
37-
layout(binding = 0, r32f) uniform imageBuffer u_RayBuffer;
37+
layout(binding = 0, r32f) uniform image2D u_ShadowMap;
3838

3939
vec2 world_to_uv(vec2 world_pos) {
4040
return (viewport.ViewMatrix * vec4(world_pos, 0.0, 1.0)).xy * 0.5 + 0.5;
@@ -45,21 +45,21 @@ vec2 uv_to_world(vec2 uv) {
4545
}
4646

4747
void main() {
48-
ivec2 map_size = textureSize(u_OpacityMap, 0);
4948
uint global_index = gl_GlobalInvocationID.x;
5049
uint light_index = global_index / RAY_COUNT;
51-
uint ray_index = global_index % RAY_COUNT;
5250

5351
if (light_index >= viewport.LightCount) {
5452
return;
5553
}
5654

5755
Light2D light = viewport.Lights[light_index];
5856
if (light.is_occludable == 0U) {
59-
imageStore(u_RayBuffer, int(global_index), vec4(FLOAT_MAX, 0.0, 0.0, 0.0));
6057
return;
6158
}
6259

60+
uint ray_index = global_index % RAY_COUNT;
61+
ivec2 dest_coord = ivec2(ray_index, light_index);
62+
6363
vec2 light_pos_uv = world_to_uv(light.position.xy);
6464

6565
float theta = float(ray_index) * TWO_PI / float(RAY_COUNT) - PI;
@@ -82,14 +82,15 @@ void main() {
8282
float t_exit = min(t_max.x, t_max.y);
8383

8484
if (t_enter > t_exit) {
85-
imageStore(u_RayBuffer, int(global_index), vec4(FLOAT_MAX, 0.0, 0.0, 0.0));
85+
imageStore(u_ShadowMap, dest_coord, vec4(FLOAT_MAX, 0.0, 0.0, 0.0));
8686
return;
8787
}
8888

89+
ivec2 map_size = textureSize(u_OpacityMap, 0);
90+
8991
float nearest = FLOAT_MAX;
9092
ivec2 last_texel = ivec2(-1);
9193
float step_uv = 1.0 / float(max(map_size.x, map_size.y));
92-
bool did_sample_in_frame = false;
9394
float t_end = min(t_exit, max_uv_dist);
9495
for (float dist_uv = t_enter; dist_uv <= t_end; dist_uv += step_uv) {
9596
vec2 sample_uv = light_pos_uv + dir_uv_unit * dist_uv;
@@ -103,8 +104,6 @@ void main() {
103104
}
104105
last_texel = sample_texel;
105106

106-
did_sample_in_frame = true;
107-
108107
if (texelFetch(u_OpacityMap, sample_texel, 0).r > 0.0) {
109108
// pixel is opaque to light
110109
vec2 sample_world = uv_to_world(sample_uv);
@@ -116,5 +115,5 @@ void main() {
116115
// else pixel is transparent to light; continue marching
117116
}
118117

119-
imageStore(u_RayBuffer, int(global_index), vec4(nearest, 0.0, 0.0, 0.0));
118+
imageStore(u_ShadowMap, dest_coord, vec4(nearest, 0.0, 0.0, 0.0));
120119
}

engine/modules/render/res/render/shader/shadowmap_frag.frag

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#define PI 3.14159
44
#define TWO_PI (PI * 2.0)
5+
#define FLOAT_MAX 3.402823e38
56

67
#define LIGHTS_MAX 32
78
#define RAY_COUNT 360
@@ -29,7 +30,6 @@ in vec2 TexCoord;
2930
out vec4 out_Color;
3031

3132
layout(binding = 0) uniform sampler2D u_OpacityMap;
32-
layout(binding = 0, r32ui) uniform uimageBuffer u_RayBuffer;
3333

3434
layout(std140, binding = 2) uniform Scene {
3535
vec4 AmbientLightColor;
@@ -43,38 +43,87 @@ layout(std140, binding = 3) uniform Viewport {
4343
Light2D Lights[32];
4444
} viewport;
4545

46+
vec2 world_to_uv(vec2 world_pos) {
47+
return (viewport.ViewMatrix * vec4(world_pos, 0.0, 1.0)).xy * 0.5 + 0.5;
48+
}
49+
50+
vec2 uv_to_world(vec2 uv) {
51+
return (viewport.ViewMatrixInverse * vec4(uv * 2.0 - 1.0, 0.0, 1.0)).xy;
52+
}
53+
4654
void main() {
47-
if (viewport.LightCount == 0) {
55+
int buf_len = LIGHTS_MAX * RAY_COUNT;
56+
int global_index = int(TexCoord.x * buf_len);
57+
uint light_index = uint(TexCoord.y * LIGHTS_MAX);
58+
59+
if (light_index >= viewport.LightCount) {
4860
discard;
4961
}
5062

51-
if (texture(u_OpacityMap, TexCoord).r == 0.0) {
52-
// pixel is transparent to light
63+
Light2D light = viewport.Lights[light_index];
64+
if (light.is_occludable == 0U) {
5365
discard;
5466
}
5567

56-
for (int i = 0; i < LIGHTS_MAX; i++) {
57-
if (i >= viewport.LightCount) {
58-
break;
59-
}
68+
uint ray_index = uint(TexCoord.x * RAY_COUNT);
69+
70+
vec2 light_pos_uv = world_to_uv(light.position.xy);
71+
72+
float theta = float(ray_index) * TWO_PI / float(RAY_COUNT) - PI;
73+
vec2 dir_world = vec2(cos(theta), sin(theta));
74+
vec2 dir_uv = (viewport.ViewMatrix * vec4(dir_world, 0.0, 0.0)).xy;
75+
vec2 dir_uv_unit = normalize(dir_uv);
76+
77+
float max_world_dist = light.falloff_buffer + light.falloff_distance + light.shadow_falloff_distance;
78+
float max_uv_dist = max_world_dist * length(dir_uv) * 0.5;
79+
80+
vec2 inv_dir = 1.0 / dir_uv_unit;
81+
82+
vec2 t0 = (vec2(0.0) - light_pos_uv) * inv_dir;
83+
vec2 t1 = (vec2(1.0) - light_pos_uv) * inv_dir;
6084

61-
Light2D light = viewport.Lights[i];
62-
vec2 light_pos = light.position.xy;
63-
vec2 offset = WorldPos.xy - light_pos;
85+
vec2 t_min = min(t0, t1);
86+
vec2 t_max = max(t0, t1);
6487

65-
if (abs(offset.x) < 0.01 && abs(offset.y) < 0.01) {
66-
// light is fully occluded
67-
for (int j = 0; j < RAY_COUNT; j++) {
68-
imageAtomicExchange(u_RayBuffer, int(i * RAY_COUNT + j), 0);
69-
}
88+
float t_enter = max(max(t_min.x, t_min.y), 0.0);
89+
float t_exit = min(t_max.x, t_max.y);
90+
91+
if (t_enter > t_exit) {
92+
out_Color = vec4(FLOAT_MAX, 0.0, 0.0, 0.0);
93+
return;
94+
}
95+
96+
ivec2 map_size = textureSize(u_OpacityMap, 0);
97+
98+
float nearest = FLOAT_MAX;
99+
ivec2 last_texel = ivec2(-1);
100+
float step_uv = 1.0 / float(max(map_size.x, map_size.y));
101+
bool did_sample_in_frame = false;
102+
float t_end = min(t_exit, max_uv_dist);
103+
for (float dist_uv = t_enter; dist_uv <= t_end; dist_uv += step_uv) {
104+
vec2 sample_uv = light_pos_uv + dir_uv_unit * dist_uv;
105+
if (sample_uv.x <= 0.0 || sample_uv.y <= 0.0 || sample_uv.x >= 1.0 || sample_uv.y >= 1.0) {
70106
continue;
71107
}
72108

73-
float theta = atan(offset.y, offset.x) + PI;
74-
uint ray_index = uint(floor(float(RAY_COUNT) * theta / TWO_PI));
75-
uint dist = uint(distance(light_pos, WorldPos.xy) * DIST_MULTIPLIER);
76-
imageAtomicMin(u_RayBuffer, int(i * RAY_COUNT + ray_index), dist);
109+
ivec2 sample_texel = ivec2(sample_uv * vec2(map_size));
110+
if (sample_texel == last_texel) {
111+
continue;
112+
}
113+
last_texel = sample_texel;
114+
115+
did_sample_in_frame = true;
116+
117+
if (texelFetch(u_OpacityMap, sample_texel, 0).r > 0.0) {
118+
// pixel is opaque to light
119+
vec2 sample_world = uv_to_world(sample_uv);
120+
float dist_world = distance(sample_world, light.position.xy);
121+
nearest = dist_world;
122+
break;
123+
}
124+
125+
// else pixel is transparent to light; continue marching
77126
}
78127

79-
discard;
128+
out_Color = vec4(nearest, 0.0, 0.0, 0.0);
80129
}

engine/modules/render/src/constants.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub const SHADER_UNIFORM_GLOBAL_TIME: &str = "Time";
5454
pub const SHADER_UNIFORM_GLOBAL_TIME_OFF: u32 = 0;
5555

5656
pub const SHADER_UBO_SCENE: &str = "Scene";
57-
pub const SHADER_UBO_SCENE_LEN: u32 = 48 + (SHADER_STRUCT_LIGHT2D_LEN * LIGHTS_MAX);
57+
pub const SHADER_UBO_SCENE_LEN: u32 = 20;
5858
pub const SHADER_UNIFORM_SCENE_AL_COLOR: &str = "AmbientLightColor";
5959
pub const SHADER_UNIFORM_SCENE_AL_COLOR_OFF: u32 = 0;
6060
pub const SHADER_UNIFORM_SCENE_AL_LEVEL: &str = "AmbientLightLevel";

0 commit comments

Comments
 (0)