Welcome to Fractal Forums

Fractal Software => Programming => Topic started by: willvarfar on July 10, 2012, 10:52:52 AM




Title: bug in perspective
Post by: willvarfar on July 10, 2012, 10:52:52 AM
I am failing at an embarrassingly simple task - trying to draw a mandelbox (or anything) on the CPU.

I have a width*height bitmap to render to.

For each pixel, I want to march towards the cube:

Code:
    static float eye = 0.0f; eye = glm::clamp(eye+0.005f,0.0f,1.0f); // animate
    const glm::mat4 projection = glm::perspective(35.0f, (float)width/height, 0.1f, 10.0f),
            modelview = glm::lookAt(glm::vec3(cos(eye),sin(eye),-1),glm::vec3(0,0,0),glm::vec3(0,0,1));    
    const float epsilon = sqrt(1.0f/std::max(width,height))/2.0f;
    for(int y=0; y<height; y++) {
            for(int x=0; x<width; x++) {
                    glm::vec3 p = glm::unProject(glm::vec3(x,y,0),modelview,projection,glm::vec4(0,0,width,height)),
                            dir = glm::unProject(glm::vec3(x,y,1),modelview,projection,glm::vec4(0,0,width,height))-p;
                    //std::cout << x << "," << y << " " << p.x << "," << p.y << "," << p.z << " " << dir.x << "," << dir.y << "," << dir.z << std::endl;
                    float D = 0;
                    for(int i=0; i<MAX_ITER; i++) {
                            const float d = DE(dir*D + p);
                            D += d;
                            if(d<epsilon) {
                                    depth_bmp[y*width+x] = 255.0f/(i+1);
                                    break;
                            }
                    }
            }
    }

I think my fundamental problem is working out where my rays should go for each pixel.

My distance estimator function is a very literal classic mandelbox and looks like this:

Code:
    float DE(glm::vec3 p) {
     const float Scale = -1.77f, fixedRadius2 = 1.0f, minRadius2 = (0.5f*0.5f);
     const glm::vec3 p0 = p;
     float dr = 1.0f;
     for(int n = 0; n < 13; n++) {
     // Reflect
     p = (glm::clamp(p,-1.0f,1.0f) * 2.0f) - p;
     // Sphere Inversion
     const float r2 = glm::dot(p,p);
     if(r2<minRadius2) {
     const float t = (fixedRadius2/minRadius2);
     p *= t;
     dr *= t;
     } else if(r2<fixedRadius2) {
     const float t = (fixedRadius2/r2);
     p *= t;
     dr *= t;
     }
     // Scale & Translate
                    p = p * Scale + p0;
                    dr = dr * abs(Scale) + 1.0f;
     }
     return glm::length(p)/abs(dr);
    }

And the output looks completely unbox-like:

(http://i.stack.imgur.com/xoY4D.jpg)

I asked on StackOverflow (http://stackoverflow.com/questions/11402495/drawing-a-cube-with-perspective) but no bites.

How do I set the eye transform up so I see the cube properly?


Title: Re: bug in perspective
Post by: bib on July 10, 2012, 11:40:59 AM
I have no idea what your code means but I can tell you that your image definitely looks like a cut Mandelbox, so you are on the right track.
Perhaps you should try to set the viewpoint somewhere else, farther from the origin, in order to get the big picture.


Title: Re: bug in perspective
Post by: willvarfar on July 10, 2012, 01:01:52 PM
Obvious fixes and now it draws cubes and spheres and such spinning as I'd expect.

It doesn't draw any mandelbox though :(

Code:
enum { DEBUG = 0 };
const float rotation = (double)now_nanosecs()/1000000000, distance = 8.0f;
const glm::vec3 eye = glm::vec3(cos(rotation),0.5f,sin(rotation))*distance;
const glm::mat4
projection = glm::perspective(35.0f,(float)depth_w/depth_h,0.1f,100.0f),
modelview = glm::lookAt(eye,glm::vec3(0,0,0),glm::vec3(0.0f,1.0f,0.0f));
for(int y=0; y<depth_h; y++) {
for(int x=0; x<depth_w; x++) {
glm::vec3
p = glm::unProject(glm::vec3(x,y,0),modelview,projection,glm::vec4(0,0,depth_w,depth_h)),
dir = glm::unProject(glm::vec3(x,y,1),modelview,projection,glm::vec4(0,0,depth_w,depth_h))-p;
const float len = glm::length(dir),
epsilon = 0.001; // how best to compute scaled epsilon too?
dir = glm::normalize(dir);
if(DEBUG) std::cout << x << "," << y << " " << p.x << "," << p.y << "," << p.z << " " << dir.x << "," << dir.y << "," << dir.z << std::endl;
float D = 0.01f;
for(int i=0; i<MAX_ITER; i++) {
const float d = DE(p + dir*D);
if(DEBUG) std::cout << " " << x << "," << y << " " << i << " " << D << "," << d << "," << (D+d) << "," << (D+d>len) << std::endl;
D += d;
if(D>len)
break;
if(d<epsilon) {
depth_bmp[y*depth_w+x] = 255.0f/(i+1); // colour by ray march steps
break;
}
}
}
}

(http://i.imgur.com/Zqfs5.png)

Presumably the mandelbox is at -1,-1,-1 to 1,1,1 ?  And asking for a DE from a point outside that will compute the distance to that?


Title: Re: bug in perspective
Post by: taurus on July 10, 2012, 01:15:50 PM
Presumably the mandelbox is at -1,-1,-1 to 1,1,1 ?

i have also no clou about code, but depending on scale it should be bigger. At least -2 to 2


Title: Re: bug in perspective
Post by: willvarfar on July 10, 2012, 05:39:03 PM
Basic spheres and cubes and such that I ray-march on work fine with proper perspective and draw where I put them.

But my mandelbox - which I copied, naturally (http://blog.hvidtfeldts.net/index.php/2011/11/distance-estimated-3d-fractals-vi-the-mandelbox/) - doesn't want to compute properly; it shows nothing, and the estimates it gives back are in the 1000s.

When I change the dr to Scale - as emphasised in this link by Buddhi (http://www.fractalforums.com/3d-fractal-generation/a-mandelbox-distance-estimate-formula/msg14893/#msg14893) - it draws... this:

(http://i.imgur.com/UkgM1.jpg)

As it rotates it wobbles; its not properly box-shaped.

Code:
	const float Scale = -1.77f, fixedRadius2 = 1.0f, minRadius2 = (0.5f*0.5f);
const glm::vec3 p0 = p;
float dr = Scale;
for(int n = 0; n < 13; n++) {
// Reflect
p = (glm::clamp(p,-1.0f,1.0f) * 2.0f) - p;
// Sphere Inversion
const float r2 = glm::dot(p,p);
if(r2<minRadius2) {
const float t = (fixedRadius2/minRadius2);
p *= t;
dr *= t;
} else if(r2<fixedRadius2) {
const float t = (fixedRadius2/r2);
p *= t;
dr *= t;
}
// Scale & Translate
                p = p * Scale + p0;
                dr *= Scale;
}
return glm::length(p)/abs(dr);

I had imagined it was clipping problems but, really, how?  The sphere and box tests spin around the entire viewport.


Title: Re: bug in perspective
Post by: taurus on July 10, 2012, 06:12:54 PM
still looks cut. Try a standard scale like 2 and get the camera off from the orgin, five or six i'd suggest.


Title: Re: bug in perspective
Post by: Syntopia on July 10, 2012, 10:15:45 PM
Hi Willvarfar,

You DE-code look right - it works in Fragmentarium, but I must set a fudge-factor to 0.5 to avoid clipping, when the scale is negative. This might also be your problem: try multiplying the returned DE-value by 0.5.

Since you have a clamp-function, you can also try this version (due to Rrrola, I think), which works good for both positive and negative values:
Code:
float AbsScalem1 = abs(Scale - 1.0);
float AbsScaleRaisedTo1mIters = pow(abs(Scale), float(1-Iterations));

float DE(vec3 pos) {
vec4 p = vec4(pos,1), p0 = p;  // p.w is the distance estimate
for (int i=0; i<Iterations; i++) {
p.xyz = clamp(p.xyz, -1.0, 1.0) * 2.0 - p.xyz; 
float r2 = dot(p.xyz, p.xyz);
p *= clamp(max(MinRad2/r2, MinRad2), 0.0, 1.0); 
p = p*scale + p0;
}
return ((length(p.xyz) - AbsScalem1) / p.w - AbsScaleRaisedTo1mIters);
}

Btw, consider using a 'using namespace glm;' - then I could have pasted your C++ directly as GLSL :-)


Title: Re: bug in perspective
Post by: willvarfar on July 11, 2012, 09:28:47 AM
Thx Syntopia,

a scale of 2 or -2 works; -1.77 doesn't.  I have not experimented with other values nor tried to work out why.

I will park this mystery as I now have a nice -2 mandelbox to depth-estimate against.

http://www.pouet.net/topic.php?which=6675&page=4 sounds like the demo guys used a put-spheres-on-a-list sphere-marching kind of thing.


Title: Re: bug in perspective
Post by: willvarfar on July 12, 2012, 02:33:48 PM
I don't have swizzle so this is my translation of Rrrola's mandelbox:

Code:
	const int Iterations = 13;
const float Scale = 2.0f,
MinRad2 = (0.5f*0.5f),
AbsScalem1 = abs(Scale - 1.0f),
AbsScaleRaisedTo1mIters = pow(abs(Scale),float(1-Iterations));
const vec3 p0 = p;
float p_w = 1.0f;
for(int i=0; i<Iterations; i++) {
p = (clamp(p,-1.0f,1.0f) * 2.0f) - p;
const float r2 = dot(p,p),
adj = clamp(max(MinRad2/r2,MinRad2),0.0f,1.0f)*Scale;
p = p*adj + p0;
p_w = p_w*adj + 1.0f;
}
return ((length(p)-AbsScalem1)/p_w) - AbsScaleRaisedTo1mIters;

But I must have a strange bug in it (apart from not working with negative numbers etc too); it draws this ugly unstable thing that wobbles when viewed from different angles:

(http://i.imgur.com/P5Rfc.jpg)

What can I possibly have mistranslated to C++?


Title: Re: bug in perspective
Post by: willvarfar on July 12, 2012, 09:50:41 PM
I seem beset with problems

For example, rendering a very normal mandelbulb with this code:

Code:
	const Vec3 pos = p;
const int Iterations = 20, Power = 8;
const Float DEPTH_OF_FIELD = 1000.0;
Float dr = 1.0f, r = 0.0f;
for(int i=0; i<Iterations; i++) {
r = length(p);
if(r>DEPTH_OF_FIELD) break;
const Float theta = acos(p.z/r)*Power,
        phi = atan2(p.y,p.x)*Power;
dr =  pow(r,Power-1)*Power*dr + 1.0f;
p.x = sin(theta)*cos(phi);
p.y = sin(phi)*sin(theta);
p.z = cos(theta);
p *= pow(r,Power);
p += pos;
}
return 0.5f*log(r)*r/dr;

I have typedefs to switch between floats (32 bits) and doubles (64).

The left column shows doubles
The right column shows floats
The top row shows deep inside the mandelbulb
The bottom row shows it from outside

(http://i.imgur.com/Vx5Zl.jpg)

How do I get this to work with floats?  GPUs are 32 bits; why does a literal copy of the GLSL code, like the mandelbox in the code in my previous post, not work when I compile it (linux, gcc 4.6.3, 64-bit)?

What simple thing am I doing wrong?  I'm going to kick myself when you point this out to me, promise!


Title: Re: bug in perspective
Post by: eiffie on July 13, 2012, 12:02:33 AM
Try a smaller bailout like 2.0 perhaps.