diff --git a/src/g_statusbar/sbar.h b/src/g_statusbar/sbar.h
index be8b7bb00..e4ad4e69b 100644
--- a/src/g_statusbar/sbar.h
+++ b/src/g_statusbar/sbar.h
@@ -396,7 +396,7 @@ public:
 	uint32_t GetTranslation() const;
 
 	void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY);
-	void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, double y, int flags, double Alpha, int translation, int spacing, bool monospaced, int shadowX, int shadowY);
+	void DrawString(FFont *font, const FString &cstring, double x, double y, int flags, double Alpha, int translation, int spacing, bool monospaced, int shadowX, int shadowY);
 
 	void BeginStatusBar(int resW, int resH, int relTop, bool completeborder = false, bool forceScaled = false);
 	void BeginHUD(int resW, int resH, double Alpha, bool forceScaled = false);
diff --git a/src/polyrenderer/drawers/poly_draw_args.cpp b/src/polyrenderer/drawers/poly_draw_args.cpp
index eaa812dc9..64fe7a882 100644
--- a/src/polyrenderer/drawers/poly_draw_args.cpp
+++ b/src/polyrenderer/drawers/poly_draw_args.cpp
@@ -45,6 +45,14 @@ void PolyDrawArgs::SetClipPlane(const PolyClipPlane &plane)
 	mClipPlane[3] = plane.D;
 }
 
+void PolyDrawArgs::SetTexture(const uint8_t *texels, int width, int height)
+{
+	mTexturePixels = texels;
+	mTextureWidth = width;
+	mTextureHeight = height;
+	mTranslation = nullptr;
+}
+
 void PolyDrawArgs::SetTexture(FTexture *texture)
 {
 	mTextureWidth = texture->GetWidth();
diff --git a/src/polyrenderer/drawers/poly_draw_args.h b/src/polyrenderer/drawers/poly_draw_args.h
index 20c73ec58..87cd6a191 100644
--- a/src/polyrenderer/drawers/poly_draw_args.h
+++ b/src/polyrenderer/drawers/poly_draw_args.h
@@ -49,6 +49,7 @@ class PolyDrawArgs
 {
 public:
 	void SetClipPlane(const PolyClipPlane &plane);
+	void SetTexture(const uint8_t *texels, int width, int height);
 	void SetTexture(FTexture *texture);
 	void SetTexture(FTexture *texture, uint32_t translationID, bool forcePal = false);
 	void SetLight(FSWColormap *basecolormap, uint32_t lightlevel, double globVis, bool fixed);
diff --git a/src/polyrenderer/scene/poly_particle.cpp b/src/polyrenderer/scene/poly_particle.cpp
index d4a49334b..53079b6dd 100644
--- a/src/polyrenderer/scene/poly_particle.cpp
+++ b/src/polyrenderer/scene/poly_particle.cpp
@@ -29,6 +29,8 @@
 #include "polyrenderer/poly_renderer.h"
 #include "polyrenderer/scene/poly_light.h"
 
+EXTERN_CVAR(Int, gl_particles_style)
+
 void RenderPolyParticle::Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue)
 {
 	DVector3 pos = particle->Pos;
@@ -76,12 +78,40 @@ void RenderPolyParticle::Render(const TriMatrix &worldToClip, const PolyClipPlan
 	args.SetSubsectorDepth(subsectorDepth);
 	args.SetSubsectorDepthTest(true);
 	args.SetColor(particle->color | 0xff000000, particle->color >> 24);
-	args.SetStyle(TriBlendMode::AlphaBlend, particle->alpha, 1.0 - particle->alpha);
+	args.SetStyle(TriBlendMode::Shaded, particle->alpha, 1.0 - particle->alpha);
 	args.SetTransform(&worldToClip);
 	args.SetFaceCullCCW(true);
 	args.SetStencilTestValue(stencilValue);
 	args.SetWriteStencil(false);
 	args.SetWriteSubsectorDepth(false);
 	args.SetClipPlane(clipPlane);
+	args.SetTexture(GetParticleTexture(), ParticleTextureSize, ParticleTextureSize);
 	args.DrawArray(vertices, 4, PolyDrawMode::TriangleFan);
 }
+
+uint8_t *RenderPolyParticle::GetParticleTexture()
+{
+	static uint8_t particle_texture[NumParticleTextures][ParticleTextureSize * ParticleTextureSize];
+	static bool first_call = true;
+	if (first_call)
+	{
+		double center = ParticleTextureSize * 0.5f;
+		for (int y = 0; y < ParticleTextureSize; y++)
+		{
+			for (int x = 0; x < ParticleTextureSize; x++)
+			{
+				double dx = (center - x - 0.5f) / center;
+				double dy = (center - y - 0.5f) / center;
+				double dist2 = dx * dx + dy * dy;
+				double round_alpha = clamp<double>(1.7f - dist2 * 1.7f, 0.0f, 1.0f);
+				double smooth_alpha = clamp<double>(1.1f - dist2 * 1.1f, 0.0f, 1.0f);
+
+				particle_texture[0][x + y * ParticleTextureSize] = 255;
+				particle_texture[1][x + y * ParticleTextureSize] = (int)(round_alpha * 255.0f + 0.5f);
+				particle_texture[2][x + y * ParticleTextureSize] = (int)(smooth_alpha * 255.0f + 0.5f);
+			}
+		}
+		first_call = false;
+	}
+	return particle_texture[MIN<int>(gl_particles_style, NumParticleTextures)];
+}
diff --git a/src/polyrenderer/scene/poly_particle.h b/src/polyrenderer/scene/poly_particle.h
index e91174591..a5cc3d0d0 100644
--- a/src/polyrenderer/scene/poly_particle.h
+++ b/src/polyrenderer/scene/poly_particle.h
@@ -29,4 +29,13 @@ class RenderPolyParticle
 {
 public:
 	void Render(const TriMatrix &worldToClip, const PolyClipPlane &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue);
+
+private:
+	static uint8_t *GetParticleTexture();
+
+	enum
+	{
+		NumParticleTextures = 3,
+		ParticleTextureSize = 64
+	};
 };
diff --git a/src/portal.cpp b/src/portal.cpp
index 1ec92799f..acf6e022d 100644
--- a/src/portal.cpp
+++ b/src/portal.cpp
@@ -167,7 +167,7 @@ static void BuildBlockmap()
 
 void FLinePortalTraverse::AddLineIntercepts(int bx, int by)
 {
-	if (by < 0 || by >= PortalBlockmap.dx || bx < 0 || bx >= PortalBlockmap.dy) return;
+	if (by < 0 || by >= PortalBlockmap.dy || bx < 0 || bx >= PortalBlockmap.dx) return;
 
 	FPortalBlock &block = PortalBlockmap(bx, by);
 
diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp
index a4467266e..1f7b2d560 100644
--- a/src/swrenderer/drawers/r_draw.cpp
+++ b/src/swrenderer/drawers/r_draw.cpp
@@ -64,7 +64,7 @@ namespace swrenderer
 	int fuzzpos;
 	int fuzzviewheight;
 
-	uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE];
+	uint32_t particle_texture[NUM_PARTICLE_TEXTURES][PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE];
 
 	short zeroarray[MAXWIDTH];
 	short screenheightarray[MAXWIDTH];
@@ -139,6 +139,8 @@ namespace swrenderer
 
 	void R_InitParticleTexture()
 	{
+		static_assert(NUM_PARTICLE_TEXTURES == 3, "R_InitParticleTexture must be updated if NUM_PARTICLE_TEXTURES is changed");
+
 		double center = PARTICLE_TEXTURE_SIZE * 0.5f;
 		for (int y = 0; y < PARTICLE_TEXTURE_SIZE; y++)
 		{
@@ -147,9 +149,12 @@ namespace swrenderer
 				double dx = (center - x - 0.5f) / center;
 				double dy = (center - y - 0.5f) / center;
 				double dist2 = dx * dx + dy * dy;
-				double alpha = clamp<double>(1.1f - dist2 * 1.1f, 0.0f, 1.0f);
+				double round_alpha = clamp<double>(1.7f - dist2 * 1.7f, 0.0f, 1.0f);
+				double smooth_alpha = clamp<double>(1.1f - dist2 * 1.1f, 0.0f, 1.0f);
 
-				particle_texture[x + y * PARTICLE_TEXTURE_SIZE] = (int)(alpha * 128.0f + 0.5f);
+				particle_texture[0][x + y * PARTICLE_TEXTURE_SIZE] = 128;
+				particle_texture[1][x + y * PARTICLE_TEXTURE_SIZE] = (int)(round_alpha * 128.0f + 0.5f);
+				particle_texture[2][x + y * PARTICLE_TEXTURE_SIZE] = (int)(smooth_alpha * 128.0f + 0.5f);
 			}
 		}
 	}
diff --git a/src/swrenderer/drawers/r_draw.h b/src/swrenderer/drawers/r_draw.h
index 5c37873c8..e9b893aba 100644
--- a/src/swrenderer/drawers/r_draw.h
+++ b/src/swrenderer/drawers/r_draw.h
@@ -44,8 +44,9 @@ namespace swrenderer
 	extern int fuzzpos;
 	extern int fuzzviewheight;
 
+	#define NUM_PARTICLE_TEXTURES 3
 	#define PARTICLE_TEXTURE_SIZE 64
-	extern uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE];
+	extern uint32_t particle_texture[NUM_PARTICLE_TEXTURES][PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE];
 
 	class SWPixelFormatDrawers
 	{
diff --git a/src/swrenderer/drawers/r_draw_pal.cpp b/src/swrenderer/drawers/r_draw_pal.cpp
index 522eb51b1..3f75a30ac 100644
--- a/src/swrenderer/drawers/r_draw_pal.cpp
+++ b/src/swrenderer/drawers/r_draw_pal.cpp
@@ -48,6 +48,7 @@
 
 // [SP] r_blendmethod - false = rgb555 matching (ZDoom classic), true = rgb666 (refactored)
 CVAR(Bool, r_blendmethod, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
+EXTERN_CVAR(Int, gl_particles_style)
 
 /*
 	[RH] This translucency algorithm is based on DOSDoom 0.65's, but uses
@@ -2946,7 +2947,8 @@ namespace swrenderer
 		uint8_t *dest = thread->dest_for_thread(_dest_y, pitch, _dest);
 		pitch = pitch * thread->num_cores;
 
-		const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE];
+		int particle_texture_index = MIN<int>(gl_particles_style, NUM_PARTICLE_TEXTURES - 1);
+		const uint32_t *source = &particle_texture[particle_texture_index][(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE];
 		uint32_t particle_alpha = _alpha;
 
 		uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count;
diff --git a/src/swrenderer/drawers/r_draw_rgba.cpp b/src/swrenderer/drawers/r_draw_rgba.cpp
index 453071eae..3dbd38f46 100644
--- a/src/swrenderer/drawers/r_draw_rgba.cpp
+++ b/src/swrenderer/drawers/r_draw_rgba.cpp
@@ -780,7 +780,8 @@ namespace swrenderer
 		uint32_t *dest = thread->dest_for_thread(_dest_y, _pitch, _dest);
 		int pitch = _pitch * thread->num_cores;
 
-		const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE];
+		int particle_texture_index = MIN<int>(gl_particles_style, NUM_PARTICLE_TEXTURES - 1);
+		const uint32_t *source = &particle_texture[particle_texture_index][(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE];
 		uint32_t particle_alpha = _alpha;
 
 		uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count;
diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp
index bf8d856e9..d315d5a01 100644
--- a/src/swrenderer/line/r_walldraw.cpp
+++ b/src/swrenderer/line/r_walldraw.cpp
@@ -42,6 +42,13 @@
 #include "swrenderer/r_renderthread.h"
 #include "swrenderer/r_memory.h"
 
+#ifndef isnan
+// Fallback to C++ function if C99 isnan() macro is not undefined
+// Most likely it was undefined in C++ library header to avoid conflicts with own function
+#include <cmath>
+using std::isnan;
+#endif // !isnan
+
 namespace swrenderer
 {
 	WallSampler::WallSampler(RenderViewport *viewport, int y1, double texturemid, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture)