Because the OP stated that depth testing isn't a good option, I propose a simpler approach based on z-index alone, with tuned parameters to hide the "abrupt transition effect".
Render 0: yellowish solid color layer always on top
Render 1: removed the always on top layer (for comparison). The fog is neutral gray, the top layer changed the mood of the scene before, maybe too much.
Render 2: exaggerated fog intensity, no always on top layer (for comparison)
Some considerations:
If we impose ourselves no buffer access, no low level OpenGL/other APIs calls, no custom shaders, we are a bit limited in what we can do.
The asset used for the fog is provided to examine it. It can be improved, for example: borders must be softer. Suggestion: play with Blur effects and other filters.
The original fog assets came from here: https://opengameart.org/content/thick-fog
The original character assets came from here: https://opengameart.org/content/tmim-heroine-bleeds-game-art
The fog asset is 1024 pixels wide. For our fog, I took a rectangle of 1024x85, very close to the top of the original fog full image.
To avoid artifacts when two rows of fog intercept in the y axis, I used the Gimp lasso tool to round borders, including the bottom border, and then blur bottom and top borders.
While each fog row is 85 pixels in height, their are separate from each other by 20 pixels in the y axis. We want overlapping to have a better continuity effect, but borders must be taken care of with proper blurring, for example. Rough borders won't do. The ones here aren't perfect either and probably only useful for testing.
The rows move at different speeds. The speed table is:
var speed_table = [1, 0.5, 2, 0.25, 0.75, 1.25]
The loop that instantiates the rows cycle through that table, and if it reach the end it starts at the beginning again. This is needed because there are more fog rows than entries in the speed table.
Formula to determine how many rows we need:
rows = viewport_height / row_distance_y
The resulting fog row asset:
Because that image is nearly all transparent, this other version serves to appreciate it better:
But the first one is the one that must be used for experimentation.
All of these parameters, the height of the fog, the separation of 20 pixels, the alpha of the pixels, all must be played with until ideal ones are found. In the first render, for example, the top layer hides the fog rows too much, somewhat defeating its purpose.