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: 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: 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: I asked on StackOverflow but no bites. How do I set the eye transform up so I see the cube properly?
|
|
« Last Edit: July 10, 2012, 10:54:50 AM by willvarfar »
|
Logged
|
|
|
|
bib
|
|
« Reply #1 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.
|
|
|
Logged
|
Between order and disorder reigns a delicious moment. (Paul Valéry)
|
|
|
willvarfar
|
|
« Reply #2 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 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; } } } } 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?
|
|
« Last Edit: July 10, 2012, 01:06:49 PM by willvarfar »
|
Logged
|
|
|
|
taurus
|
|
« Reply #3 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
|
|
|
Logged
|
when life offers you a lemon, get yourself some salt and tequila!
|
|
|
willvarfar
|
|
« Reply #4 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 - 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 - it draws... this: As it rotates it wobbles; its not properly box-shaped. 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.
|
|
|
Logged
|
|
|
|
taurus
|
|
« Reply #5 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.
|
|
|
Logged
|
when life offers you a lemon, get yourself some salt and tequila!
|
|
|
Syntopia
|
|
« Reply #6 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: 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 :-)
|
|
|
Logged
|
|
|
|
willvarfar
|
|
« Reply #7 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.
|
|
|
Logged
|
|
|
|
willvarfar
|
|
« Reply #8 on: July 12, 2012, 02:33:48 PM » |
|
I don't have swizzle so this is my translation of Rrrola's mandelbox: 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: What can I possibly have mistranslated to C++?
|
|
|
Logged
|
|
|
|
willvarfar
|
|
« Reply #9 on: July 12, 2012, 09:50:41 PM » |
|
I seem beset with problems For example, rendering a very normal mandelbulb with this 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 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!
|
|
|
Logged
|
|
|
|
eiffie
Guest
|
|
« Reply #10 on: July 13, 2012, 12:02:33 AM » |
|
Try a smaller bailout like 2.0 perhaps.
|
|
|
Logged
|
|
|
|
|