Add resolution scaling to Vulkan renderer

This commit adds a new cvar called vk_pixel_size that represents how big
pixels should look in the rendered world in order to simulate lower
screen resolutions. With its default value of 1 everything looks normal,
but with bigger sizes (e.g. 4) the rendered world starts to look
"pixelated" due to pixels appearing bigger.

To implement the effect, the viewport and scissor are modified when
drawing the world so the rendering results cover a smaller area in the
top-left corner of the image.

The post-processing fragment shader is used to scale the image back to
the swapchain size before drawing UI elements on top of it.

The UI is not affected by this change, so the existing UI scaling
options continue to work as before with no changes, adding some
flexibility to the mix.

Related to feature request #588.
This commit is contained in:
Ricardo Garcia 2020-12-23 23:06:10 +01:00
parent 983f7f6b4b
commit 930872b358
7 changed files with 94 additions and 38 deletions

View file

@ -405,6 +405,12 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable`
* **vk_picmip**: Shrink factor for the textures. (default: `0`) * **vk_picmip**: Shrink factor for the textures. (default: `0`)
* **vk_pixel_size**: Pixel size when rendering the world, used to simulate
lower screen resolutions. The value represents the length, in pixels, of the
side of each pixel block. For example, with size 2 pixels are 2x2 squares,
and at 1600x1200 the image is effectively an upscaled 800x600 image.
(default: `1`)
* **vk_dynamic**: Use dynamic lighting. (default: `1`) * **vk_dynamic**: Use dynamic lighting. (default: `1`)
* **vk_showtris**: Display mesh triangles. (default: `0`) * **vk_showtris**: Display mesh triangles. (default: `0`)

View file

@ -145,6 +145,7 @@ extern cvar_t *vk_flashblend;
extern cvar_t *vk_finish; extern cvar_t *vk_finish;
extern cvar_t *vk_polyblend; extern cvar_t *vk_polyblend;
extern cvar_t *vk_shadows; extern cvar_t *vk_shadows;
extern cvar_t *vk_pixel_size;
extern cvar_t *vk_particle_size; extern cvar_t *vk_particle_size;
extern cvar_t *vk_particle_att_a; extern cvar_t *vk_particle_att_a;
extern cvar_t *vk_particle_att_b; extern cvar_t *vk_particle_att_b;
@ -294,6 +295,7 @@ typedef struct
qboolean stereo_enabled; qboolean stereo_enabled;
VkPipeline current_pipeline; VkPipeline current_pipeline;
qvkrenderpasstype_t current_renderpass;
} vkstate_t; } vkstate_t;
extern vkconfig_t vk_config; extern vkconfig_t vk_config;

View file

@ -1,42 +1,44 @@
// 8.13.3559 // 1011.0.0
#pragma once #pragma once
const uint32_t world_warp_frag_spv[] = { const uint32_t world_warp_frag_spv[] = {
0x07230203,0x00010000,0x00080008,0x00000078,0x00000000,0x00020011,0x00000001,0x0006000b, 0x07230203,0x00010000,0x0008000a,0x0000007e,0x00000000,0x00020011,0x00000001,0x0006000b,
0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000000c,0x00000070,0x00030010, 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000000c,0x00000076,0x00030010,
0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00090004,0x415f4c47,0x735f4252, 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00090004,0x415f4c47,0x735f4252,
0x72617065,0x5f657461,0x64616873,0x6f5f7265,0x63656a62,0x00007374,0x00040005,0x00000004, 0x72617065,0x5f657461,0x64616873,0x6f5f7265,0x63656a62,0x00007374,0x00040005,0x00000004,
0x6e69616d,0x00000000,0x00030005,0x00000009,0x00007675,0x00060005,0x0000000c,0x465f6c67, 0x6e69616d,0x00000000,0x00030005,0x00000009,0x00007675,0x00060005,0x0000000c,0x465f6c67,
0x43676172,0x64726f6f,0x00000000,0x00060005,0x00000012,0x68737550,0x736e6f43,0x746e6174, 0x43676172,0x64726f6f,0x00000000,0x00060005,0x00000012,0x68737550,0x736e6f43,0x746e6174,
0x00000000,0x00050006,0x00000012,0x00000000,0x656d6974,0x00000000,0x00050006,0x00000012, 0x00000000,0x00050006,0x00000012,0x00000000,0x656d6974,0x00000000,0x00050006,0x00000012,
0x00000001,0x6c616373,0x00000065,0x00060006,0x00000012,0x00000002,0x57726373,0x68746469, 0x00000001,0x6c616373,0x00000065,0x00060006,0x00000012,0x00000002,0x57726373,0x68746469,
0x00000000,0x00060006,0x00000012,0x00000003,0x48726373,0x68676965,0x00000074,0x00030005, 0x00000000,0x00060006,0x00000012,0x00000003,0x48726373,0x68676965,0x00000074,0x00060006,
0x00000014,0x00006370,0x00030005,0x0000002c,0x00007873,0x00030005,0x0000003d,0x00007973, 0x00000012,0x00000004,0x65786970,0x7a69536c,0x00000065,0x00030005,0x00000014,0x00006370,
0x00040005,0x0000004c,0x69685378,0x00007466,0x00040005,0x00000057,0x69685379,0x00007466, 0x00030005,0x0000002c,0x00007873,0x00030005,0x0000003d,0x00007973,0x00040005,0x0000004c,
0x00050005,0x00000060,0x74736964,0x6974726f,0x00006e6f,0x00060005,0x00000070,0x67617266, 0x69685378,0x00007466,0x00040005,0x00000057,0x69685379,0x00007466,0x00050005,0x00000060,
0x746e656d,0x6f6c6f43,0x00000072,0x00050005,0x00000074,0x78655473,0x65727574,0x00000000, 0x74736964,0x6974726f,0x00006e6f,0x00060005,0x00000076,0x67617266,0x746e656d,0x6f6c6f43,
0x00040047,0x0000000c,0x0000000b,0x0000000f,0x00050048,0x00000012,0x00000000,0x00000023, 0x00000072,0x00050005,0x0000007a,0x78655473,0x65727574,0x00000000,0x00040047,0x0000000c,
0x00000044,0x00050048,0x00000012,0x00000001,0x00000023,0x00000048,0x00050048,0x00000012, 0x0000000b,0x0000000f,0x00050048,0x00000012,0x00000000,0x00000023,0x00000044,0x00050048,
0x00000002,0x00000023,0x0000004c,0x00050048,0x00000012,0x00000003,0x00000023,0x00000050, 0x00000012,0x00000001,0x00000023,0x00000048,0x00050048,0x00000012,0x00000002,0x00000023,
0x00030047,0x00000012,0x00000002,0x00040047,0x00000070,0x0000001e,0x00000000,0x00040047, 0x0000004c,0x00050048,0x00000012,0x00000003,0x00000023,0x00000050,0x00050048,0x00000012,
0x00000074,0x00000022,0x00000000,0x00040047,0x00000074,0x00000021,0x00000000,0x00020013, 0x00000004,0x00000023,0x00000054,0x00030047,0x00000012,0x00000002,0x00040047,0x00000076,
0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017, 0x0000001e,0x00000000,0x00040047,0x0000007a,0x00000022,0x00000000,0x00040047,0x0000007a,
0x00000007,0x00000006,0x00000002,0x00040020,0x00000008,0x00000007,0x00000007,0x00040017, 0x00000021,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,
0x0000000a,0x00000006,0x00000004,0x00040020,0x0000000b,0x00000001,0x0000000a,0x0004003b, 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000002,0x00040020,0x00000008,
0x0000000b,0x0000000c,0x00000001,0x00040015,0x0000000d,0x00000020,0x00000000,0x0004002b, 0x00000007,0x00000007,0x00040017,0x0000000a,0x00000006,0x00000004,0x00040020,0x0000000b,
0x0000000d,0x0000000e,0x00000000,0x00040020,0x0000000f,0x00000001,0x00000006,0x0006001e, 0x00000001,0x0000000a,0x0004003b,0x0000000b,0x0000000c,0x00000001,0x00040015,0x0000000d,
0x00000012,0x00000006,0x00000006,0x00000006,0x00000006,0x00040020,0x00000013,0x00000009, 0x00000020,0x00000000,0x0004002b,0x0000000d,0x0000000e,0x00000000,0x00040020,0x0000000f,
0x00000012,0x0004003b,0x00000013,0x00000014,0x00000009,0x00040015,0x00000015,0x00000020, 0x00000001,0x00000006,0x0007001e,0x00000012,0x00000006,0x00000006,0x00000006,0x00000006,
0x00000001,0x0004002b,0x00000015,0x00000016,0x00000002,0x00040020,0x00000017,0x00000009, 0x00000006,0x00040020,0x00000013,0x00000009,0x00000012,0x0004003b,0x00000013,0x00000014,
0x00000006,0x0004002b,0x0000000d,0x0000001b,0x00000001,0x0004002b,0x00000015,0x0000001e, 0x00000009,0x00040015,0x00000015,0x00000020,0x00000001,0x0004002b,0x00000015,0x00000016,
0x00000003,0x0004002b,0x00000015,0x00000023,0x00000000,0x0004002b,0x00000006,0x00000026, 0x00000002,0x00040020,0x00000017,0x00000009,0x00000006,0x0004002b,0x0000000d,0x0000001b,
0x00000000,0x00020014,0x00000027,0x00040020,0x0000002b,0x00000007,0x00000006,0x0004002b, 0x00000001,0x0004002b,0x00000015,0x0000001e,0x00000003,0x0004002b,0x00000015,0x00000023,
0x00000015,0x0000002d,0x00000001,0x0004002b,0x00000006,0x00000032,0x40000000,0x0004002b, 0x00000000,0x0004002b,0x00000006,0x00000026,0x00000000,0x00020014,0x00000027,0x00040020,
0x00000006,0x00000052,0x40490e56,0x0004002b,0x00000006,0x00000054,0x41200000,0x0004002b, 0x0000002b,0x00000007,0x00000006,0x0004002b,0x00000015,0x0000002d,0x00000001,0x0004002b,
0x00000006,0x0000006a,0x3bda3c21,0x00040020,0x0000006f,0x00000003,0x0000000a,0x0004003b, 0x00000006,0x00000032,0x40000000,0x0004002b,0x00000006,0x00000052,0x40490e56,0x0004002b,
0x0000006f,0x00000070,0x00000003,0x00090019,0x00000071,0x00000006,0x00000001,0x00000000, 0x00000006,0x00000054,0x41200000,0x0004002b,0x00000006,0x0000006a,0x3bda3c21,0x0004002b,
0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000072,0x00000071,0x00040020, 0x00000015,0x0000006f,0x00000004,0x00040020,0x00000075,0x00000003,0x0000000a,0x0004003b,
0x00000073,0x00000000,0x00000072,0x0004003b,0x00000073,0x00000074,0x00000000,0x00050036, 0x00000075,0x00000076,0x00000003,0x00090019,0x00000077,0x00000006,0x00000001,0x00000000,
0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b,0x00000078,0x00000077,0x00040020,
0x00000079,0x00000000,0x00000078,0x0004003b,0x00000079,0x0000007a,0x00000000,0x00050036,
0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000008, 0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003b,0x00000008,
0x00000009,0x00000007,0x0004003b,0x0000002b,0x0000002c,0x00000007,0x0004003b,0x0000002b, 0x00000009,0x00000007,0x0004003b,0x0000002b,0x0000002c,0x00000007,0x0004003b,0x0000002b,
0x0000003d,0x00000007,0x0004003b,0x0000002b,0x0000004c,0x00000007,0x0004003b,0x0000002b, 0x0000003d,0x00000007,0x0004003b,0x0000002b,0x0000004c,0x00000007,0x0004003b,0x0000002b,
@ -87,7 +89,10 @@ const uint32_t world_warp_frag_spv[] = {
0x0000006a,0x0003003e,0x00000060,0x0000006b,0x0004003d,0x00000007,0x0000006c,0x00000060, 0x0000006a,0x0003003e,0x00000060,0x0000006b,0x0004003d,0x00000007,0x0000006c,0x00000060,
0x0004003d,0x00000007,0x0000006d,0x00000009,0x00050081,0x00000007,0x0000006e,0x0000006d, 0x0004003d,0x00000007,0x0000006d,0x00000009,0x00050081,0x00000007,0x0000006e,0x0000006d,
0x0000006c,0x0003003e,0x00000009,0x0000006e,0x000200f9,0x0000002a,0x000200f8,0x0000002a, 0x0000006c,0x0003003e,0x00000009,0x0000006e,0x000200f9,0x0000002a,0x000200f8,0x0000002a,
0x0004003d,0x00000072,0x00000075,0x00000074,0x0004003d,0x00000007,0x00000076,0x00000009, 0x00050041,0x00000017,0x00000070,0x00000014,0x0000006f,0x0004003d,0x00000006,0x00000071,
0x00050057,0x0000000a,0x00000077,0x00000075,0x00000076,0x0003003e,0x00000070,0x00000077, 0x00000070,0x0004003d,0x00000007,0x00000072,0x00000009,0x00050050,0x00000007,0x00000073,
0x000100fd,0x00010038 0x00000071,0x00000071,0x00050088,0x00000007,0x00000074,0x00000072,0x00000073,0x0003003e,
0x00000009,0x00000074,0x0004003d,0x00000078,0x0000007b,0x0000007a,0x0004003d,0x00000007,
0x0000007c,0x00000009,0x00050057,0x0000000a,0x0000007d,0x0000007b,0x0000007c,0x0003003e,
0x00000076,0x0000007d,0x000100fd,0x00010038
}; };

View file

@ -1938,6 +1938,7 @@ VkResult QVk_BeginFrame(const VkViewport* viewport, const VkRect2D* scissor)
{ {
// reset tracking variables // reset tracking variables
vk_state.current_pipeline = VK_NULL_HANDLE; vk_state.current_pipeline = VK_NULL_HANDLE;
vk_state.current_renderpass = RP_COUNT;
vk_config.vertex_buffer_usage = 0; vk_config.vertex_buffer_usage = 0;
// triangle fan index buffer data will not be cleared between frames unless the buffer itself is too small // triangle fan index buffer data will not be cleared between frames unless the buffer itself is too small
vk_config.index_buffer_usage = vk_triangleFanIboUsage; vk_config.index_buffer_usage = vk_triangleFanIboUsage;
@ -2115,6 +2116,7 @@ void QVk_BeginRenderpass(qvkrenderpasstype_t rpType)
} }
vkCmdBeginRenderPass(vk_commandbuffers[vk_activeBufferIdx], &renderBeginInfo[rpType], VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(vk_commandbuffers[vk_activeBufferIdx], &renderBeginInfo[rpType], VK_SUBPASS_CONTENTS_INLINE);
vk_state.current_renderpass = rpType;
} }
#if 0 #if 0

View file

@ -166,7 +166,7 @@ void QVk_CreatePipeline(const VkDescriptorSetLayout *descriptorLayout, const uin
{ {
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = 17 * sizeof(float), .offset = 17 * sizeof(float),
.size = 4 * sizeof(float) .size = 5 * sizeof(float)
}}; }};
VkPipelineLayoutCreateInfo plCreateInfo = { VkPipelineLayoutCreateInfo plCreateInfo = {

View file

@ -104,6 +104,7 @@ cvar_t *r_lockpvs;
cvar_t *vk_polyblend; cvar_t *vk_polyblend;
cvar_t *r_modulate; cvar_t *r_modulate;
cvar_t *vk_shadows; cvar_t *vk_shadows;
cvar_t *vk_pixel_size;
cvar_t *vk_particle_size; cvar_t *vk_particle_size;
cvar_t *vk_particle_att_a; cvar_t *vk_particle_att_a;
cvar_t *vk_particle_att_b; cvar_t *vk_particle_att_b;
@ -540,7 +541,9 @@ void R_DrawParticles (void)
float att_c; float att_c;
} particleUbo; } particleUbo;
particleUbo.particleSize = vk_particle_size->value; // Particle size needs to be scaled down proportionally to vk_pixel_size.
const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value);
particleUbo.particleSize = vk_particle_size->value / divisor;
particleUbo.particleScale = vid.width * ri.Cvar_Get("viewsize", "100", CVAR_ARCHIVE)->value / 102400; particleUbo.particleScale = vid.width * ri.Cvar_Get("viewsize", "100", CVAR_ARCHIVE)->value / 102400;
particleUbo.minPointSize = vk_particle_min_size->value; particleUbo.minPointSize = vk_particle_min_size->value;
particleUbo.maxPointSize = vk_particle_max_size->value; particleUbo.maxPointSize = vk_particle_max_size->value;
@ -871,6 +874,17 @@ R_SetupVulkan (void)
.minDepth = 0.f, .minDepth = 0.f,
.maxDepth = 1.f, .maxDepth = 1.f,
}; };
// When rendering the world, reduce viewport size proportionally to vk_pixel_size.
if (vk_state.current_renderpass == RP_WORLD)
{
const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value);
viewport.x /= divisor;
viewport.y /= divisor;
viewport.width /= divisor;
viewport.height /= divisor;
}
vkCmdSetViewport(vk_activeCmdbuffer, 0, 1, &viewport); vkCmdSetViewport(vk_activeCmdbuffer, 0, 1, &viewport);
// set up projection matrix // set up projection matrix
@ -930,6 +944,16 @@ static void RE_RenderView (refdef_t *fd)
.extent = { r_newrefdef.width, r_newrefdef.height } .extent = { r_newrefdef.width, r_newrefdef.height }
}; };
// When rendering the world, scale down scissor proportionally to vk_pixel_size.
if (vk_state.current_renderpass == RP_WORLD)
{
const float divisor = (vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value);
scissor.offset.x /= divisor;
scissor.offset.y /= divisor;
scissor.extent.width /= divisor;
scissor.extent.height /= divisor;
}
vkCmdSetScissor(vk_activeCmdbuffer, 0, 1, &scissor); vkCmdSetScissor(vk_activeCmdbuffer, 0, 1, &scissor);
R_PushDlights(); R_PushDlights();
@ -986,13 +1010,25 @@ qboolean RE_EndWorldRenderpass(void)
// finish rendering world view to offsceen buffer // finish rendering world view to offsceen buffer
vkCmdEndRenderPass(vk_activeCmdbuffer); vkCmdEndRenderPass(vk_activeCmdbuffer);
// apply postprocessing effects (underwater view warp if the player is submerged in liquid) to offscreen buffer // apply postprocessing effects to offscreen buffer:
// * underwater view warp if the player is submerged in liquid
// * restore world view to the full screen size when vk_pixel_size is >1.0
QVk_BeginRenderpass(RP_WORLD_WARP); QVk_BeginRenderpass(RP_WORLD_WARP);
float pushConsts[] = { (r_newrefdef.rdflags & RDF_UNDERWATER) ? r_newrefdef.time : 0.f, viewsize->value / 100, vid.width, vid.height }; float pushConsts[] =
{
(r_newrefdef.rdflags & RDF_UNDERWATER) ? r_newrefdef.time : 0.f,
viewsize->value / 100.0f,
vid.width,
vid.height,
(vk_pixel_size->value < 1.0f ? 1.0f : vk_pixel_size->value),
};
vkCmdPushConstants(vk_activeCmdbuffer, vk_worldWarpPipeline.layout, vkCmdPushConstants(vk_activeCmdbuffer, vk_worldWarpPipeline.layout,
VK_SHADER_STAGE_FRAGMENT_BIT, 17 * sizeof(float), sizeof(pushConsts), pushConsts); VK_SHADER_STAGE_FRAGMENT_BIT, 17 * sizeof(float), sizeof(pushConsts), pushConsts);
vkCmdBindDescriptorSets(vk_activeCmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vk_worldWarpPipeline.layout, 0, 1, &vk_colorbuffer.descriptorSet, 0, NULL); vkCmdBindDescriptorSets(vk_activeCmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vk_worldWarpPipeline.layout, 0, 1, &vk_colorbuffer.descriptorSet, 0, NULL);
QVk_BindPipeline(&vk_worldWarpPipeline); QVk_BindPipeline(&vk_worldWarpPipeline);
// Restore full viewport for future steps.
vkCmdSetViewport(vk_activeCmdbuffer, 0u, 1u, &vk_viewport);
vkCmdSetScissor(vk_activeCmdbuffer, 0u, 1u, &vk_scissor);
vkCmdDraw(vk_activeCmdbuffer, 3, 1, 0, 0); vkCmdDraw(vk_activeCmdbuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(vk_activeCmdbuffer); vkCmdEndRenderPass(vk_activeCmdbuffer);
@ -1159,6 +1195,7 @@ R_Register( void )
vk_polyblend = ri.Cvar_Get("vk_polyblend", "1", 0); vk_polyblend = ri.Cvar_Get("vk_polyblend", "1", 0);
r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE); r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE);
vk_shadows = ri.Cvar_Get("vk_shadows", "0", CVAR_ARCHIVE); vk_shadows = ri.Cvar_Get("vk_shadows", "0", CVAR_ARCHIVE);
vk_pixel_size = ri.Cvar_Get("vk_pixel_size", "1", CVAR_ARCHIVE);
vk_particle_size = ri.Cvar_Get("vk_particle_size", "40", CVAR_ARCHIVE); vk_particle_size = ri.Cvar_Get("vk_particle_size", "40", CVAR_ARCHIVE);
vk_particle_att_a = ri.Cvar_Get("vk_particle_att_a", "0.01", CVAR_ARCHIVE); vk_particle_att_a = ri.Cvar_Get("vk_particle_att_a", "0.01", CVAR_ARCHIVE);
vk_particle_att_b = ri.Cvar_Get("vk_particle_att_b", "0.0", CVAR_ARCHIVE); vk_particle_att_b = ri.Cvar_Get("vk_particle_att_b", "0.0", CVAR_ARCHIVE);

View file

@ -1,7 +1,8 @@
#version 450 #version 450
#extension GL_ARB_separate_shader_objects : enable #extension GL_ARB_separate_shader_objects : enable
// Underwater screen warp effect similar to what software renderer provides // Underwater screen warp effect similar to what software renderer provides.
// Pixel size to simulate lower screen resolutions is used to restore world to full screen size.
layout(push_constant) uniform PushConstant layout(push_constant) uniform PushConstant
{ {
@ -9,6 +10,7 @@ layout(push_constant) uniform PushConstant
layout(offset = 72) float scale; layout(offset = 72) float scale;
layout(offset = 76) float scrWidth; layout(offset = 76) float scrWidth;
layout(offset = 80) float scrHeight; layout(offset = 80) float scrHeight;
layout(offset = 84) float pixelSize;
} pc; } pc;
layout(set = 0, binding = 0) uniform sampler2D sTexture; layout(set = 0, binding = 0) uniform sampler2D sTexture;
@ -32,5 +34,7 @@ void main()
uv += distortion; uv += distortion;
} }
uv /= pc.pixelSize;
fragmentColor = texture(sTexture, uv); fragmentColor = texture(sTexture, uv);
} }