mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add bloom passes in character select screen, use proper source image for bloom after clouds are applied, add blurred and less blurred stages of bloom together (experimental could remove), add filtering downsample pass that is not yet used
This commit is contained in:
parent
74e01f10e3
commit
29bfee26c0
73
assets/voxygen/shaders/dual-downsample-filtered-frag.glsl
Normal file
73
assets/voxygen/shaders/dual-downsample-filtered-frag.glsl
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#version 420 core
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0)
|
||||||
|
uniform texture2D t_src_color;
|
||||||
|
layout(set = 0, binding = 1)
|
||||||
|
uniform sampler s_src_color;
|
||||||
|
layout(set = 0, binding = 2)
|
||||||
|
// TODO: refactor in rust
|
||||||
|
uniform u_locals {
|
||||||
|
vec2 halfpixel;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 tgt_color;
|
||||||
|
|
||||||
|
vec4 simplesample(vec2 uv) {
|
||||||
|
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check whether the texel color is higher than threshold, if so output as brightness color
|
||||||
|
vec4 filterDim(vec4 color) {
|
||||||
|
// TODO: note where this constant came from if we keep it
|
||||||
|
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
||||||
|
if(brightness > 1.0)
|
||||||
|
return vec4(color.rgb, 1.0);
|
||||||
|
else
|
||||||
|
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 filteredFetch(ivec2 uv) {
|
||||||
|
return filterDim(simplesample(uv));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derived from: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||||
|
vec4 filteredDownsample(vec2 uv, vec2 halfpixel) {
|
||||||
|
vec2 tex_res = 2.0 / halfpixel;
|
||||||
|
// coordinate of the top left texel
|
||||||
|
// _ _ _ _
|
||||||
|
// |x|_|_|_|
|
||||||
|
// |_|_|_|_|
|
||||||
|
// |_|_|_|_|
|
||||||
|
// |_|_|_|_|
|
||||||
|
//
|
||||||
|
ivec2 tl_coord = ivec2(uv * tex_res + vec2(-1.5, 1.5));
|
||||||
|
|
||||||
|
// Fetch inner square
|
||||||
|
vec4 sum = filteredFetch(tl_coord + ivec2(1, 1));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(2, 1));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(1, 2));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(2, 2));
|
||||||
|
// Weight inner square
|
||||||
|
sum *= 5.0;
|
||||||
|
// Fetch border
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(0, 0));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(1, 0));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(2, 0));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(3, 0));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(0, 1));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(3, 1));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(0, 2));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(3, 2));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(0, 3));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(1, 3));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(2, 3));
|
||||||
|
sum += filteredFetch(tl_coord + ivec2(3, 3));
|
||||||
|
|
||||||
|
return sum / 32.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
tgt_color = filteredDownsample(uv, halfpixel);
|
||||||
|
}
|
@ -185,8 +185,10 @@ void main() {
|
|||||||
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
|
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
|
||||||
|
|
||||||
// Bloom
|
// Bloom
|
||||||
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) * 0.75;
|
// divide by 4.0 to account for adding blurred layers together
|
||||||
aa_color += bloom;
|
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) / 4.0;
|
||||||
|
float bloom_factor = 0.10;
|
||||||
|
aa_color = aa_color * (1.0 - bloom_factor) + bloom * bloom_factor;
|
||||||
|
|
||||||
// Tonemapping
|
// Tonemapping
|
||||||
float exposure_offset = 1.0;
|
float exposure_offset = 1.0;
|
||||||
|
@ -257,6 +257,11 @@ impl PlayState for CharSelectionState {
|
|||||||
if let Some(mut second_pass) = drawer.second_pass() {
|
if let Some(mut second_pass) = drawer.second_pass() {
|
||||||
second_pass.draw_clouds();
|
second_pass.draw_clouds();
|
||||||
}
|
}
|
||||||
|
// Bloom
|
||||||
|
// TODO: make optional
|
||||||
|
{
|
||||||
|
drawer.run_bloom_passes()
|
||||||
|
}
|
||||||
// PostProcess and UI
|
// PostProcess and UI
|
||||||
let mut third_pass = drawer.third_pass();
|
let mut third_pass = drawer.third_pass();
|
||||||
third_pass.draw_postprocess();
|
third_pass.draw_postprocess();
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
//! Based on: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||||
|
|
||||||
use super::super::Consts;
|
use super::super::Consts;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
|
// TODO: auto-tune the number of passes to maintain roughly constant blur per
|
||||||
|
// unit of FOV so changing resolution / FOV doesn't change the blur appearance
|
||||||
|
// significantly
|
||||||
/// Each level is a multiple of 2 smaller in both dimensions.
|
/// Each level is a multiple of 2 smaller in both dimensions.
|
||||||
/// For a total of 8 passes from the largest to the smallest to the largest
|
/// For a total of 8 passes from the largest to the smallest to the largest
|
||||||
/// again.
|
/// again.
|
||||||
@ -98,7 +103,7 @@ impl BloomLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct BloomPipelines {
|
pub struct BloomPipelines {
|
||||||
//pub downsample_filtered: wgpu::RenderPipeline,
|
pub downsample_filtered: wgpu::RenderPipeline,
|
||||||
pub downsample: wgpu::RenderPipeline,
|
pub downsample: wgpu::RenderPipeline,
|
||||||
pub upsample: wgpu::RenderPipeline,
|
pub upsample: wgpu::RenderPipeline,
|
||||||
}
|
}
|
||||||
@ -107,7 +112,7 @@ impl BloomPipelines {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
vs_module: &wgpu::ShaderModule,
|
vs_module: &wgpu::ShaderModule,
|
||||||
//downsample_filtered_fs_module: &wgpu::ShaderModule,
|
downsample_filtered_fs_module: &wgpu::ShaderModule,
|
||||||
downsample_fs_module: &wgpu::ShaderModule,
|
downsample_fs_module: &wgpu::ShaderModule,
|
||||||
upsample_fs_module: &wgpu::ShaderModule,
|
upsample_fs_module: &wgpu::ShaderModule,
|
||||||
target_format: wgpu::TextureFormat,
|
target_format: wgpu::TextureFormat,
|
||||||
@ -121,75 +126,69 @@ impl BloomPipelines {
|
|||||||
bind_group_layouts: &[&layout.layout],
|
bind_group_layouts: &[&layout.layout],
|
||||||
});
|
});
|
||||||
|
|
||||||
let downsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
let create_pipeline = |label, fs_module, blend| {
|
||||||
label: Some("Bloom downsample pipeline"),
|
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
layout: Some(&render_pipeline_layout),
|
label: Some(label),
|
||||||
vertex: wgpu::VertexState {
|
layout: Some(&render_pipeline_layout),
|
||||||
module: vs_module,
|
vertex: wgpu::VertexState {
|
||||||
entry_point: "main",
|
module: vs_module,
|
||||||
buffers: &[],
|
entry_point: "main",
|
||||||
},
|
buffers: &[],
|
||||||
primitive: wgpu::PrimitiveState {
|
},
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
primitive: wgpu::PrimitiveState {
|
||||||
strip_index_format: None,
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
strip_index_format: None,
|
||||||
cull_mode: None,
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
clamp_depth: false,
|
cull_mode: None,
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
clamp_depth: false,
|
||||||
conservative: false,
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
},
|
conservative: false,
|
||||||
depth_stencil: None,
|
},
|
||||||
multisample: wgpu::MultisampleState {
|
depth_stencil: None,
|
||||||
count: 1,
|
multisample: wgpu::MultisampleState {
|
||||||
mask: !0,
|
count: 1,
|
||||||
alpha_to_coverage_enabled: false,
|
mask: !0,
|
||||||
},
|
alpha_to_coverage_enabled: false,
|
||||||
fragment: Some(wgpu::FragmentState {
|
},
|
||||||
module: downsample_fs_module,
|
fragment: Some(wgpu::FragmentState {
|
||||||
entry_point: "main",
|
module: fs_module,
|
||||||
targets: &[wgpu::ColorTargetState {
|
entry_point: "main",
|
||||||
format: target_format,
|
targets: &[wgpu::ColorTargetState {
|
||||||
blend: None,
|
format: target_format,
|
||||||
write_mask: wgpu::ColorWrite::ALL,
|
blend,
|
||||||
}],
|
write_mask: wgpu::ColorWrite::ALL,
|
||||||
}),
|
}],
|
||||||
});
|
}),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
let upsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
let downsample_filtered_pipeline = create_pipeline(
|
||||||
label: Some("Bloom upsample pipeline"),
|
"Bloom downsample filtered pipeline",
|
||||||
layout: Some(&render_pipeline_layout),
|
downsample_filtered_fs_module,
|
||||||
vertex: wgpu::VertexState {
|
None,
|
||||||
module: vs_module,
|
);
|
||||||
entry_point: "main",
|
let downsample_pipeline =
|
||||||
buffers: &[],
|
create_pipeline("Bloom downsample pipeline", downsample_fs_module, None);
|
||||||
},
|
let upsample_pipeline = create_pipeline(
|
||||||
primitive: wgpu::PrimitiveState {
|
"Bloom upsample pipeline",
|
||||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
upsample_fs_module,
|
||||||
strip_index_format: None,
|
Some(wgpu::BlendState {
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
color: wgpu::BlendComponent {
|
||||||
cull_mode: None,
|
src_factor: wgpu::BlendFactor::One,
|
||||||
clamp_depth: false,
|
dst_factor: wgpu::BlendFactor::One,
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
operation: wgpu::BlendOperation::Add,
|
||||||
conservative: false,
|
},
|
||||||
},
|
// we don't really use this, but... we need something here
|
||||||
depth_stencil: None,
|
alpha: wgpu::BlendComponent {
|
||||||
multisample: wgpu::MultisampleState {
|
src_factor: wgpu::BlendFactor::One,
|
||||||
count: 1,
|
dst_factor: wgpu::BlendFactor::One,
|
||||||
mask: !0,
|
operation: wgpu::BlendOperation::Add,
|
||||||
alpha_to_coverage_enabled: false,
|
},
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: upsample_fs_module,
|
|
||||||
entry_point: "main",
|
|
||||||
targets: &[wgpu::ColorTargetState {
|
|
||||||
format: target_format,
|
|
||||||
blend: None,
|
|
||||||
write_mask: wgpu::ColorWrite::ALL,
|
|
||||||
}],
|
|
||||||
}),
|
}),
|
||||||
});
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
downsample_filtered: downsample_filtered_pipeline,
|
||||||
downsample: downsample_pipeline,
|
downsample: downsample_pipeline,
|
||||||
upsample: upsample_pipeline,
|
upsample: upsample_pipeline,
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,7 @@ impl Renderer {
|
|||||||
&views.tgt_color,
|
&views.tgt_color,
|
||||||
&views.tgt_depth,
|
&views.tgt_depth,
|
||||||
[
|
[
|
||||||
&views.tgt_color,
|
&views.tgt_color_pp,
|
||||||
&views.bloom_tgts[1],
|
&views.bloom_tgts[1],
|
||||||
&views.bloom_tgts[2],
|
&views.bloom_tgts[2],
|
||||||
&views.bloom_tgts[3],
|
&views.bloom_tgts[3],
|
||||||
@ -576,7 +576,7 @@ impl Renderer {
|
|||||||
&self.views.tgt_color,
|
&self.views.tgt_color,
|
||||||
&self.views.tgt_depth,
|
&self.views.tgt_depth,
|
||||||
[
|
[
|
||||||
&self.views.tgt_color,
|
&self.views.tgt_color_pp,
|
||||||
&self.views.bloom_tgts[1],
|
&self.views.bloom_tgts[1],
|
||||||
&self.views.bloom_tgts[2],
|
&self.views.bloom_tgts[2],
|
||||||
&self.views.bloom_tgts[3],
|
&self.views.bloom_tgts[3],
|
||||||
|
@ -252,20 +252,18 @@ impl<'frame> Drawer<'frame> {
|
|||||||
let locals = &self.borrow.locals;
|
let locals = &self.borrow.locals;
|
||||||
let views = &self.borrow.views;
|
let views = &self.borrow.views;
|
||||||
|
|
||||||
let encoder = self.encoder.as_mut().unwrap();
|
|
||||||
let device = self.borrow.device;
|
let device = self.borrow.device;
|
||||||
|
let mut encoder = self.encoder.as_mut().unwrap().scope("bloom", device);
|
||||||
|
|
||||||
let mut run_bloom_pass = |bind, view, label: String, pipeline| {
|
let mut run_bloom_pass = |bind, view, label: String, pipeline, load| {
|
||||||
|
let pass_label = format!("bloom {} pass", label);
|
||||||
let mut render_pass =
|
let mut render_pass =
|
||||||
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
|
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
|
||||||
label: Some(&label),
|
label: Some(&pass_label),
|
||||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
view,
|
view,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations { store: true, load },
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
|
||||||
store: true,
|
|
||||||
},
|
|
||||||
}],
|
}],
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
});
|
});
|
||||||
@ -279,16 +277,35 @@ impl<'frame> Drawer<'frame> {
|
|||||||
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
||||||
let bind = &locals.bloom_binds[index].bind_group;
|
let bind = &locals.bloom_binds[index].bind_group;
|
||||||
let view = &views.bloom_tgts[index + 1];
|
let view = &views.bloom_tgts[index + 1];
|
||||||
let label = format!("bloom downsample pass {}", index + 1);
|
let label = format!("downsample {}", index + 1);
|
||||||
run_bloom_pass(bind, view, label, &pipelines.bloom.downsample);
|
run_bloom_pass(
|
||||||
|
bind,
|
||||||
|
view,
|
||||||
|
label,
|
||||||
|
&pipelines.bloom.downsample,
|
||||||
|
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Upsample filter passes
|
// Upsample filter passes
|
||||||
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
||||||
let bind = &locals.bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group;
|
let bind = &locals.bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group;
|
||||||
let view = &views.bloom_tgts[bloom::NUM_SIZES - 2 - index];
|
let view = &views.bloom_tgts[bloom::NUM_SIZES - 2 - index];
|
||||||
let label = format!("bloom upsample pass {}", index + 1);
|
let label = format!("upsample {}", index + 1);
|
||||||
run_bloom_pass(bind, view, label, &pipelines.bloom.upsample);
|
run_bloom_pass(
|
||||||
|
bind,
|
||||||
|
view,
|
||||||
|
label,
|
||||||
|
&pipelines.bloom.upsample,
|
||||||
|
if index + 2 == bloom::NUM_SIZES {
|
||||||
|
// Clear for the final image since that is just stuff from the pervious frame.
|
||||||
|
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT)
|
||||||
|
} else {
|
||||||
|
// Add to less blurred images to get gradient of blur instead of a smudge>
|
||||||
|
// https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/
|
||||||
|
wgpu::LoadOp::Load
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,8 +110,9 @@ struct ShaderModules {
|
|||||||
lod_terrain_frag: wgpu::ShaderModule,
|
lod_terrain_frag: wgpu::ShaderModule,
|
||||||
clouds_vert: wgpu::ShaderModule,
|
clouds_vert: wgpu::ShaderModule,
|
||||||
clouds_frag: wgpu::ShaderModule,
|
clouds_frag: wgpu::ShaderModule,
|
||||||
duel_downsample_frag: wgpu::ShaderModule,
|
dual_downsample_filtered_frag: wgpu::ShaderModule,
|
||||||
duel_upsample_frag: wgpu::ShaderModule,
|
dual_downsample_frag: wgpu::ShaderModule,
|
||||||
|
dual_upsample_frag: wgpu::ShaderModule,
|
||||||
postprocess_vert: wgpu::ShaderModule,
|
postprocess_vert: wgpu::ShaderModule,
|
||||||
postprocess_frag: wgpu::ShaderModule,
|
postprocess_frag: wgpu::ShaderModule,
|
||||||
blit_vert: wgpu::ShaderModule,
|
blit_vert: wgpu::ShaderModule,
|
||||||
@ -260,8 +261,12 @@ impl ShaderModules {
|
|||||||
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
|
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
|
||||||
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
|
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
|
||||||
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
|
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
|
||||||
duel_downsample_frag: create_shader("duel-downsample-frag", ShaderKind::Fragment)?,
|
dual_downsample_filtered_frag: create_shader(
|
||||||
duel_upsample_frag: create_shader("duel-upsample-frag", ShaderKind::Fragment)?,
|
"dual-downsample-filtered-frag",
|
||||||
|
ShaderKind::Fragment,
|
||||||
|
)?,
|
||||||
|
dual_downsample_frag: create_shader("dual-downsample-frag", ShaderKind::Fragment)?,
|
||||||
|
dual_upsample_frag: create_shader("dual-upsample-frag", ShaderKind::Fragment)?,
|
||||||
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
|
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
|
||||||
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
|
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
|
||||||
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
|
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
|
||||||
@ -550,8 +555,9 @@ fn create_ingame_and_shadow_pipelines(
|
|||||||
bloom::BloomPipelines::new(
|
bloom::BloomPipelines::new(
|
||||||
device,
|
device,
|
||||||
&shaders.blit_vert,
|
&shaders.blit_vert,
|
||||||
&shaders.duel_downsample_frag,
|
&shaders.dual_downsample_filtered_frag,
|
||||||
&shaders.duel_upsample_frag,
|
&shaders.dual_downsample_frag,
|
||||||
|
&shaders.dual_upsample_frag,
|
||||||
wgpu::TextureFormat::Rgba16Float,
|
wgpu::TextureFormat::Rgba16Float,
|
||||||
&layouts.bloom,
|
&layouts.bloom,
|
||||||
)
|
)
|
||||||
|
@ -68,8 +68,9 @@ impl assets::Compound for Shaders {
|
|||||||
"lod-terrain-frag",
|
"lod-terrain-frag",
|
||||||
"clouds-vert",
|
"clouds-vert",
|
||||||
"clouds-frag",
|
"clouds-frag",
|
||||||
"duel-downsample-frag",
|
"dual-downsample-filtered-frag",
|
||||||
"duel-upsample-frag",
|
"dual-downsample-frag",
|
||||||
|
"dual-upsample-frag",
|
||||||
"clouds-frag",
|
"clouds-frag",
|
||||||
"postprocess-vert",
|
"postprocess-vert",
|
||||||
"postprocess-frag",
|
"postprocess-frag",
|
||||||
|
Loading…
Reference in New Issue
Block a user