From a097b8a64a874332a26df5516cbe435bdb902c3e Mon Sep 17 00:00:00 2001 From: SArpnt Date: Sun, 20 Oct 2024 18:25:43 +0000 Subject: [PATCH] add panini projection (#288) --- engine/client/render.h | 5 ++- engine/client/view.c | 2 +- engine/gl/gl_rmain.c | 20 ++++++++++ engine/shaders/Makefile | 2 +- engine/shaders/generatebuiltinsl.c | 1 + engine/shaders/glsl/postproc_panini.glsl | 37 +++++++++++++++++++ .../shaders/glsl/postproc_stereographic.glsl | 6 +-- engine/vk/vk_init.c | 20 ++++++++++ quakec/menusys/menu/options_effects.qc | 3 +- 9 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 engine/shaders/glsl/postproc_panini.glsl diff --git a/engine/client/render.h b/engine/client/render.h index 5dc576da9..09bb3a600 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -236,11 +236,12 @@ typedef enum { typedef enum { PROJ_STANDARD = 0, - PROJ_STEREOGRAPHIC = 1, //aka panini + PROJ_STEREOGRAPHIC = 1, PROJ_FISHEYE = 2, //standard fisheye PROJ_PANORAMA = 3, //for nice panoramas PROJ_LAEA = 4, //lambert azimuthal equal-area - PROJ_EQUIRECTANGULAR = 5 //projects a sphere into 2d. used by vr screenshots. + PROJ_EQUIRECTANGULAR = 5, //projects a sphere into 2d. used by vr screenshots. + PROJ_PANINI = 6 //like stereographic, but vertical lines stay straight. } qprojection_t; typedef struct { diff --git a/engine/client/view.c b/engine/client/view.c index d7e8072e9..0cec2e7b3 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include // for isdigit(); -cvar_t r_projection = CVARD("r_projection", "0", "0: regular perspective.\n1: stereographic (aka: pannini).\n2: fisheye.\n3: panoramic.\n4: lambert azimuthal equal-area.\n5: Equirectangular"); +cvar_t r_projection = CVARD("r_projection", "0", "0: regular perspective.\n1: stereographic.\n2: fisheye.\n3: panoramic.\n4: lambert azimuthal equal-area.\n5: equirectangular.\n6: panini."); cvar_t ffov = CVARFD("ffov", "", 0, "Allows you to set a specific field of view for when a custom projection is specified. If empty, will use regular fov cvar, which might get messy."); #if defined(_WIN32) && !defined(MINIMAL) //amusing gimmick / easteregg. diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index ef3ec43a1..b7a3c9204 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -1783,6 +1783,26 @@ qboolean R_RenderScene_Cubemap(void) } #endif break; + case PROJ_PANINI: + shader = R_RegisterShader("postproc_panini", SUF_NONE, + "{\n" + "program postproc_panini\n" + "{\n" + "map $sourcecube\n" + "}\n" + "}\n" + ); + + facemask |= 1<<4; /*front view*/ + if (ffov.value > 70) + { + facemask |= (1<<0) | (1<<1); /*side/top*/ + if (ffov.value > 85) + facemask |= (1<<2) | (1<<3); /*bottom views*/ + if (ffov.value > 300) + facemask |= 1<<5; /*back view*/ + } + break; } //FIXME: we should be able to rotate the view diff --git a/engine/shaders/Makefile b/engine/shaders/Makefile index 35fa85c79..bed13818a 100644 --- a/engine/shaders/Makefile +++ b/engine/shaders/Makefile @@ -6,7 +6,7 @@ VKSDKPATH ?= ~/VulkanSDK/1.1.73.0/x86_64/bin/ all: -NAMES= fixedemu fixedemu_flat altwater bloom_blur bloom_filter bloom_final colourtint crepuscular_opaque crepuscular_rays crepuscular_sky depthonly default2d defaultadditivesprite defaultskin defaultsky defaultskybox defaultfill defaultsprite defaultwall defaultwarp defaultgammacb drawflat_wall lpp_depthnorm lpp_light lpp_wall postproc_fisheye postproc_panorama postproc_laea postproc_stereographic postproc_equirectangular fxaa underwaterwarp menutint terrain rtlight +NAMES= fixedemu fixedemu_flat altwater bloom_blur bloom_filter bloom_final colourtint crepuscular_opaque crepuscular_rays crepuscular_sky depthonly default2d defaultadditivesprite defaultskin defaultsky defaultskybox defaultfill defaultsprite defaultwall defaultwarp defaultgammacb drawflat_wall lpp_depthnorm lpp_light lpp_wall postproc_fisheye postproc_panorama postproc_laea postproc_stereographic postproc_equirectangular postproc_panini fxaa underwaterwarp menutint terrain rtlight ALLNAMES+=$(foreach v,$(NAMES),glsl/$v.glsl) diff --git a/engine/shaders/generatebuiltinsl.c b/engine/shaders/generatebuiltinsl.c index 447b4dacb..85fbe4fc9 100644 --- a/engine/shaders/generatebuiltinsl.c +++ b/engine/shaders/generatebuiltinsl.c @@ -38,6 +38,7 @@ char shaders[][64] = "postproc_laea", "postproc_stereographic", "postproc_equirectangular", + "postproc_panini", "postproc_ascii", "fxaa", "underwaterwarp", diff --git a/engine/shaders/glsl/postproc_panini.glsl b/engine/shaders/glsl/postproc_panini.glsl new file mode 100644 index 000000000..d95d4d809 --- /dev/null +++ b/engine/shaders/glsl/postproc_panini.glsl @@ -0,0 +1,37 @@ +!!cvarf ffov +!!samps screen:samplerCube=0 + +//panini view rendering, for high fovs that are still playable. + +#ifdef VERTEX_SHADER +attribute vec2 v_texcoord; +varying vec2 texcoord; +uniform float cvar_ffov; +void main() +{ + texcoord = v_texcoord.xy; + + //make sure the ffov cvar actually does something meaningful + texcoord *= cvar_ffov / 90.0; + + gl_Position = ftetransform(); +} +#endif +#ifdef FRAGMENT_SHADER +varying vec2 texcoord; +void main() +{ + vec3 tc; + vec2 d; + vec2 ang; + d = texcoord; + + //compute the 2d->3d projection + float div = 1.0 + d.x*d.x + d.y*d.y; + tc.x = 2.0*d.x/div; + tc.y = -d.y; + tc.z = -(-1.0 + d.x*d.x + d.y*d.y)/div; + + gl_FragColor = textureCube(s_screen, tc); +} +#endif diff --git a/engine/shaders/glsl/postproc_stereographic.glsl b/engine/shaders/glsl/postproc_stereographic.glsl index da0858850..c2f3df379 100644 --- a/engine/shaders/glsl/postproc_stereographic.glsl +++ b/engine/shaders/glsl/postproc_stereographic.glsl @@ -21,9 +21,9 @@ void main() varying vec2 texcoord; void main() { - vec3 tc; - vec2 d; - vec2 ang; + vec3 tc; + vec2 d; + vec2 ang; d = texcoord; //compute the 2d->3d projection diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 3e3155068..7baa6edbb 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -2687,6 +2687,26 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb) } #endif break; + case PROJ_PANINI: + shader = R_RegisterShader("postproc_panini", SUF_NONE, + "{\n" + "program postproc_panini\n" + "{\n" + "map $sourcecube\n" + "}\n" + "}\n" + ); + + facemask |= 1<<4; /*front view*/ + if (ffov.value > 70) + { + facemask |= (1<<0) | (1<<1); /*side/top*/ + if (ffov.value > 85) + facemask |= (1<<2) | (1<<3); /*bottom views*/ + if (ffov.value > 300) + facemask |= 1<<5; /*back view*/ + } + break; } if (!shader || !shader->prog) diff --git a/quakec/menusys/menu/options_effects.qc b/quakec/menusys/menu/options_effects.qc index 524d3ac07..32bf53584 100644 --- a/quakec/menusys/menu/options_effects.qc +++ b/quakec/menusys/menu/options_effects.qc @@ -96,11 +96,12 @@ nonstatic void(mitem_desktop desktop) M_Options_Effects = )), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("View Projection"), "r_projection", '280 8', _( "0 \"Standard\" " - "1 \"Stereographic / Pannini\" " + "1 \"Stereographic\" " "2 \"Fish-Eye\" " "3 \"Panoramic\" " "4 \"Lambert Azimuthal Equal-Area\" " "5 \"Equirectangular\" " + "6 \"Panini\" " )), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("View Projection Fov"), "ffov", '280 8', _( "90 \"Normal\" "