Jump to content
  • Advertisement
Sign in to follow this  
japro

Spherical "fog"

This topic is 2515 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

so normally I feel pretty competent about anything math related and can figure it out, but this issue is driving me nuts. I want a landscape to blend into the background at a given distance. Now simply using the linearized z values results in larger view distance in the corners than in the center (since the z value is the distance from the projection plane an not the actual camera position). So I figured I can apply that distance falloff in my deferred step. But it seems I'm incapable of calculating the correct "spherical distance". First I figured I'd do the brute force approach and reconstruct the world position of my fragment and calculate the distance that way. So this is how I calculate the world space coordinates in the shader:

[source]
vec3 worldspace(vec3 x) {
vec4 pos = invprojection*vec4(x, 1);
return pos.xyz / pos.w;
}
[/source]
where x is vec3(screen.xy, depth) (depth as read from the depth buffer)

It almost seems to work but it's always slightly off and the coordinates "creep" around when moving the camera. I did check that invprojection is actually the inverse (proj*invproj = identity). Also it seems to work when I transform a vector back and forth on the cpu side. As a result I now got the reverse effect that the view distance in the corners was lower...

After spending hours on trying to figure out what was wrong I started over and tried to do it without the explicit reverse transform:

[source]
float spherical(float z, float f) {
return f*(near_far.x + (near_far.y-near_far.x)*2.*near_far.x/(near_far.x + near_far.y - z * (near_far.y-near_far.x)))/near_far.y;
}
[/source]

where f is a correction factor that is calculated as sqrt(1+dot(screen.xy, screen.xy)), which should be the cosine between view and fragment direction.
Interestingly this gives the exact same result as my former approach... ARGH

Anyway:
1. is there something Obviously wrong with the way i calculate the world space coordinates?
2. Am I missing something else that prevents this from working that is not related to my actual distance calculation?
3. Is there a better (canonical) way to do this?

Share this post


Link to post
Share on other sites
Advertisement
Hello!

If you refer to world space you actually mean view space, right?

You are using vec3 so I assume you’re using GLSL? If so, how do you compute the clipping space coordinate? In xy it must be something in [-1,1]² so it is not the gl_FragCoord.xy, since that one is in [0.5,viewport_resolution.x+0.5] x [0.5, viewport_resolution.y + 0.5]. A little scale and bias helps here.

I just did a quick test and it looks good:

vec2 resolution = vec2(800, 800); // use uniforms here...
vec4 clippingSpace = vec4(gl_FragCoord.xy / resolution * 2.0 - 1.0, gl_FragCoord.z * 2.0 - 1.0, 1.0);
vec4 viewSpace = InverseProjectionMatrix * clippingSpace;
float sphereDepth = length(viewSpace.xyz / viewSpace.w);


Was that what you was looking for?
Hope it helps! smile.png

Cheers!

Share this post


Link to post
Share on other sites
I simply cannot do it ARRRRGH. This is driving me nuts.

I actually want worldspace coordinates. But since I don't make the distinction between projection and modelview matrix that shouldn't matter anyway. Before I get back to my original problem I simply want my world space thing to work.sad.png

I already do the scaling of gl_FragCoords...

I tried to reduce my code to show the issue but it is far from being a "minimal" example sadly (see zip for a version with all the headers, should only depend on sfml).

#define GL_GLEXT_PROTOTYPES
#include <SFML/System.hpp>
#include <SFML/Window.hpp>

#include <GL/gl.h>
#include <GL/glu.h>

#include <iostream>

#include "include/MathMatrix.h"
#include "include/MathVector.h"
#include "include/GraphicsMatrices.h"
#include "include/GLSLShader.h"
#include "include/GLVBO2.h"

#include <boost/fusion/include/vector.hpp>

namespace fusion = boost::fusion;


int main(/*int argc, char *argv[]*/)
{
const int width = 1600;
const int height = 900;
const float ratio = float(width)/height;

sf::Window app(sf::VideoMode(width, height, 32), "Test"/*, sf::Style::None*/);
app.UseVerticalSync(true);
app.ShowMouseCursor(false);
sf::Clock clock;

//define shader
GLSLShader shader;
shader.setVertexShaderSource(
"#version 150\n"
"uniform mat4 projection;\n"
"in vec4 vertex;\n"
"in vec4 color;\n"
"out vec4 vertexcolor;\n"
"out vec3 vertexpos;\n"
"void main() {\n"
" vertexcolor = color;\n"
" vertexpos = vec3(vertex);\n"
" gl_Position = projection*vertex;\n"
"}\n"
);

shader.setFragmentShaderSource(
"#version 150\n"

"uniform mat4 invprojection;\n"
"in vec4 vertexcolor;\n"
"in vec3 vertexpos;\n"
"out vec4 FragColor;\n"

"vec3 worldspace(vec3 x) {\n"
" vec4 pos = invprojection*vec4(x, 1);\n"
" return pos.xyz / pos.w;\n"
"}\n"

"void main() {\n"
" vec3 pos = worldspace(vec3(2*gl_FragCoord.xy/vec2(1600,900)-1,gl_FragCoord.z));\n"
//~ " vec3 pos = vertexpos;\n" //uncomment this to see how it should look like
" float blend = float((int(pos.x) + int(pos.y) + int(pos.z))&1);\n"
" FragColor = vertexcolor*blend;\n"
"}\n"
);

shader.compileProgram();
shader.bindAttributeLocation(0, "vertex");
shader.bindAttributeLocation(1, "color");
shader.bindFragDataLocation(0, "FragColor");
shader.linkProgram();

//vbo for our cube

GlVBO<fusion::vector< Vector<float, 3>, Vector<float, 3> > > cube_vbo;
cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5, 5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5, 5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5, 5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5, 5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5, 5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5, 5.5), Vector<float,3>(1,0,0));

cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5,-5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5,-5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5,-5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5,-5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5,-5.5), Vector<float,3>(1,0,0));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5,-5.5), Vector<float,3>(1,0,0));

cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5,-5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5,-5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5,-5.5), Vector<float,3>(0,1,0));

cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5,-5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5,-5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5, 5.5), Vector<float,3>(0,1,0));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5,-5.5), Vector<float,3>(0,1,0));

cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5,-5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>( 5.5, 5.5,-5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>( 5.5,-5.5,-5.5), Vector<float,3>(0,0,1));

cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5,-5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5, 5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>(-5.5, 5.5,-5.5), Vector<float,3>(0,0,1));
cube_vbo.addVertex(Vector<float,3>(-5.5,-5.5,-5.5), Vector<float,3>(0,0,1));
cube_vbo.build();


float alpha = 0.0f, beta = 0.0f;

const sf::Input &input = app.GetInput();
double t = clock.GetElapsedTime();
bool has_focus = true;
Vector<float, 3> position(0,50,0);

glEnable(GL_CULL_FACE);
glDisable(GL_BLEND);

while(app.IsOpened())
{
double dt = clock.GetElapsedTime() - t;
t += dt;

Matrix<float, 4, 4> rotation = RotationMatrix(beta, 1.f, 0.f, 0.f)*RotationMatrix(alpha, 0.f, 1.f, 0.f);

//input processing
if(input.IsKeyDown(sf::Key::W))
{
position -= 20.f*float(dt)*Transpose(SubMatrix<3,3>(rotation, 0, 0))*Vector<float, 3>(0,0,1);
}
if(input.IsKeyDown(sf::Key::S))
{
position += 20.f*float(dt)*Transpose(SubMatrix<3,3>(rotation, 0, 0))*Vector<float, 3>(0,0,1);
}
if(has_focus)
{
alpha += 0.004*(input.GetMouseX()-width/2);
beta += 0.004*(input.GetMouseY()-height/2);
app.SetCursorPosition(width/2, height/2);
}
sf::Event event;
while(app.GetEvent(event))
{
if(event.Type == sf::Event::LostFocus)
{
has_focus = false;
}
if(event.Type == sf::Event::GainedFocus)
{
has_focus = true;
}
if(event.Type == sf::Event::Closed)
{
app.Close();
}
if(event.Type == sf::Event::KeyPressed)
{
if(event.Key.Code == sf::Key::Escape)
{
app.Close();
}
}
}


//setup projection and its inverse

float view_dist = 128;

float s = 0.1;
float near = 5.f*s;
float far = view_dist;

Matrix<float, 4, 4> projection = FrustumMatrix(-3.f*s, 3.f*s, -3.f*s/ratio, 3.f*s/ratio, near, far);
rotation = RotationMatrix(beta, 1.f, 0.f, 0.f)*RotationMatrix(alpha, 0.f, 1.f, 0.f);
projection *= rotation;
projection *= TranslationMatrix(-position[0], -position[1], -position[2]);

Matrix<float, 4, 4> invprojection = TranslationMatrix(position[0], position[1], position[2]);
invprojection *= Transpose(rotation);
invprojection *= InverseFrustumMatrix(-3.f*s, 3.f*s, -3.f*s/ratio, 3.f*s/ratio, near, far);

std::cout << projection*invprojection << std::endl; // show that it is the actual inverse


//draw

glEnable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

shader.bindProgram();
shader.setUniformMatrix("projection", 4, 4, projection.raw());
shader.setUniformMatrix("invprojection", 4, 4, invprojection.raw());

cube_vbo.draw(GL_TRIANGLES);

app.Display();
}

return 0;
}


Share this post


Link to post
Share on other sites
You should keep the calculation in view space, it is the same as in world space without error-prone performance killer tongue.png
When you have reconstructed your view space vector, just take the length of it, that's it, the rest is a simple fog calculation, maybe combined with a linear fading at the far end to ensure that it fades away when no fog is available. Something like this:



vec3 view_vector = ...

// calculate normlized dist
float dist = min(1.0, length(view_vector) / far_z);

// calculate fog value 0=no fog, 1=dense fog
float fog_value = fog_calculation(dist);

// ensure to blend at the far clip plane, 10%
fog_value = 1.0- (1.0-fog_value) * (1.0-smoothstep(0.9,1.0,dist));

Share this post


Link to post
Share on other sites

Well it turns out i also have to use 2*glFragCoord.z-1...

Argh… yeah that’s right. In OpenGL the clipping space is in [-1..1]³.
Somehow my mind was in D3D-land where it is in [-1..1]²x[0..1].

Sorry, I should have seen that. (I corrected the code above.)
Cheers!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!