Logo by DsyneGrafix - Contribute your own Logo!

END OF AN ERA, FRACTALFORUMS.COM IS CONTINUED ON FRACTALFORUMS.ORG

it was a great time but no longer maintainable by c.Kleinhuis contact him for any data retrieval,
thanks and see you perhaps in 10 years again

this forum will stay online for reference
News: Did you know ? you can use LaTex inside Postings on fractalforums.com!
 
*
Welcome, Guest. Please login or register. April 27, 2024, 04:16:52 AM


Login with username, password and session length


The All New FractalForums is now in Public Beta Testing! Visit FractalForums.org and check it out!


Pages: [1]   Go Down
  Print  
Share this topic on DiggShare this topic on FacebookShare this topic on GoogleShare this topic on RedditShare this topic on StumbleUponShare this topic on Twitter
Author Topic: Ray Marching Distance Functions  (Read 7970 times)
0 Members and 1 Guest are viewing this topic.
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« on: April 05, 2012, 05:58:02 AM »

I realize this may be an inappropriate place to ask this question as it is not directly fractal related, but in another topic about Ray Marching, ker2x pointed out a website by Iñigo Quilez that shows distance functions for primitives:

Distance Functions: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
Other topic: http://www.fractalforums.com/general-discussion/ray-marching/msg45288/

I've been trying to test out some of these in a general Ray Marching sketch (after having made one for Mandelbulb) but only the sphere makes sense to me. I've just been using the pythagorean theorem like this:

float distFromSphere = sqrt(sq(i-400)+sq(j-200)+sq(ray-200))-100;

Where 'i' and 'j' represent rays shot from the screen and "ray" is the depth of the ray. This makes a sphere at (400, 200, 200) with a radius of 100. If anyone could help me make a cube I would really appreciate it, I can't understand his explanation.
« Last Edit: April 05, 2012, 08:44:42 AM by asimes » Logged
fractower
Iterator
*
Posts: 173


« Reply #1 on: April 05, 2012, 08:39:54 AM »

I feel you pain. I am a beginner to DE calculations, and I struggled with this page a while also. The notation is compact to the point of being opaque. On the other hand, a picture is worth 4 lines of code.

Code:
float udBox( vec3 p, vec3 b )
{
  return length(max(abs(p)-b,0.0));
}

b is a vector that describes a box. For example b=(1,1,2) describes a 2x2x4 box at the origin.

abs(p) in this case is an element abs operation. If p = (0, -5, 5) then abs(p) returns (0, 5, 5).

The subtraction is an element subtraction so abs(p) - r  gives (-1, 4, 3).

The max operation is zeros the negative elements giving (0, 4, 3).

The length returns 5 which is the distance from one of the edges of the box.

Logged
eiffie
Guest
« Reply #2 on: April 05, 2012, 04:47:06 PM »

It gets easier with time. For me now the simplest way to think of it is the DE for a plane...
float Plane(vec3 z){return z.y;}//if y is negative you've marched too far. so then a rectangular box is...
float Rect(vec3 z, vec3 r){return max(abs(z.x)-r.x, max(abs(z.y)-r.y, abs(z.z)-r.z));}//where r holds the half lengths

I write these with a fourth dimension z.w which starts at 1.0 and is used for scaling so...
float Cylinder(vec4 z, float r, float h){return max(length(z.xz)-r,abs(z.y)-h)/z.w;}//properly returns a scaled result

z*=2.0;
return Cylinder(z,0.25,1.0);//this is useful for difs

It is easy to string these together to make more complicated CSG shapes using min and max.
Then anything is possible...
<a href="http://www.youtube.com/v/Mbu3nRjOSS0&rel=1&fs=1&hd=1" target="_blank">http://www.youtube.com/v/Mbu3nRjOSS0&rel=1&fs=1&hd=1</a>
« Last Edit: April 05, 2012, 05:14:12 PM by eiffie » Logged
Syntopia
Fractal Molossus
**
Posts: 681



syntopiadk
WWW
« Reply #3 on: April 05, 2012, 07:45:48 PM »

Then anything is possible...

Impressive videos, Eiffie! I'll need to keep an eye on your youtube-stream!
Logged
marius
Fractal Lover
**
Posts: 206


« Reply #4 on: April 06, 2012, 12:31:38 AM »

Impressive videos, Eiffie! I'll need to keep an eye on your youtube-stream!

Eiffie is the king of fractal mash-up videos. Not to mention ingenious and prolific.
I keep thinking if only I had the spare time, but there's a lot more to it than that.  A Beer Cup for eiffie
Logged
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« Reply #5 on: April 06, 2012, 05:36:33 AM »

Thank you very much, I finally have a functional cube. I set it up like this:
Code:
float cubeDist = sqrt(sq(max(abs(i-400)-20, 0))+sq(max(abs(j-150)-20, 0))+sq(max(abs(ray-50)-20, 0)));

I have one more favor to ask, how do I display this correctly with perspective? I wrote this with Processing (Java) but I didn't get any feedback from their forum unfortunately. My sketch works correctly if I display it like this (flat):
Code:
pixVals[i+j*width] += steps;
pixDivs[i+j*width]++;

Here is an image of the flat result (with two spheres behind the cube):


This is my attempt at doing perspective using Processing's built in screenX and screenY. I'm not sure if the screenX / screenY are inaccurate or if I am doing this wrong. I instead display points like this:
Code:
int px = round(screenX(fi, fj, -ray));
int py = round(screenY(fi, fj, -ray));
if (px >= 0 && px < width && py >= 0 && py < height) {
  pixVals[px+py*width] += steps;
  pixDivs[px+py*width]++;
}

Here is an image of the perspective attempt:


If anyone has Processing installed and the patience to look through my code, this should be good to go. Thank you to anyone so patient, and thank you for the responses, I know that this is somewhat out of place for this forum:
Code:
float minThreshold = 0.0001;
int compact = 1;
int maxDepth = 800;

void setup() {
  size(800, 600, P3D);
  noLoop();
}

void draw() {
  loadPixels();
  float[] pixVals = new float[width*height];
  int[] pixDivs = new int[width*height];
  for (int i = 0; i < width*compact; i++) {
    for (int j = 0; j < height*compact; j++) {
      float ray = 0;
      int steps = 255;
      while (steps > 0 && ray < maxDepth) {
        float fi = (float)i/compact;
        float fj = (float)j/compact;
        float currDist1 = sqrt(sq(fi-400)+sq(fj-200)+sq(ray-200))-100;
        float currDist2 = sqrt(sq(fi-400)+sq(fj-300)+sq(ray-100))-100;
        float currDist3 = sqrt(sq(max(abs(fi-400)-20, 0))+sq(max(abs(fj-150)-20, 0))+sq(max(abs(ray-50)-20, 0)));
        float smallestDist = currDist1;
        if (currDist2 < smallestDist) smallestDist = currDist2;
        if (currDist3 < smallestDist) smallestDist = currDist3;
        if (smallestDist*0.5 < minThreshold) {
          int px = round(screenX(fi, fj, -ray));
          int py = round(screenY(fi, fj, -ray));
          if (px >= 0 && px < width && py >= 0 && py < height) {
            pixVals[px+py*width] += steps;
            pixDivs[px+py*width]++;
          }
          break;
        }
        else {
          ray += smallestDist*0.5;
          steps--;
        }
      }
    }
  }
  float highestVal = 0;
  float[] pixAv = new float[width*height];
  for (int i = 0; i < width*height; i++) {
    pixAv[i] = pixVals[i]/pixDivs[i];
    if (pixAv[i] > highestVal) highestVal = pixAv[i];
  }
  for (int i = 0; i < width*height; i++) {
    int val = (int)(pixAv[i]/highestVal*255);
    pixels[i] = (val<<16)+(val<<8)+val;
  }
  updatePixels();
}
« Last Edit: April 06, 2012, 05:40:12 AM by asimes » Logged
eiffie
Guest
« Reply #6 on: April 07, 2012, 05:11:34 PM »

marius - my mom always said I could be the best at anything I set my mind to, as long as no-one else set their's.

CAUTION: I do not have Processing installed but I compiled it in my graphics prog and made some changes so you may have to fix a few bugs. In your version the rays are still cast orthogonally and then you add perspective in post-processing. This reduces your world-space to a long rectangular box (always 800x600). Try to move a sphere in the backbround and it disappears. The answer is to cast the rays along perspective lines. Imagine a ray starting at your eye and going thru a pixel at (i,j). You need to determine how far the ray travels thru x,y and z (I call these dx,dy,dz) as the ray marchs 1 "unit".
Code:

float minThreshold = 0.0001;
int compact = 1;
int maxDepth = 800;

void setup() {
  size(800, 600, P3D);
  noLoop();
}

void draw() {
  loadPixels();
  for (int i = 0; i < width*compact; i++) {
    for (int j = 0; j < height*compact; j++) {
      float dx=(float)i/(float)width*compact-0.5;//creating a perspective ray direction
      float dy=((float)j/(float)height*compact-0.5)*(float)height/(float)width;//aspect ratio
      float dz=1.0,r=1.0/sqrt(dx*dx+dy*dy+1.0);dx*=r;dy*=r;dz*=r;//normalize the ray
      float x=0,y=0,z=0,rayLen=0; //the camera position, these were called fi,fj and ray before
      int steps = 255;
      while (steps > 0 && rayLen < maxDepth) {
        float currDist1 = sqrt(sq(x)+sq(y+20)+sq(z-200))-20;
        float currDist2 = sqrt(sq(x)+sq(y-10)+sq(z-100))-20;
        float currDist3 = max(abs(x)-5,max(abs(y-15)-5, abs(z-50)-5));//got rid of sqrt(sq(...
        float smallestDist = min(currDist1,min(currDist2,currDist3))*0.5;//just simplified
        if (smallestDist < minThreshold) {
          pixels[i+j*width] = (steps<<16)+(steps<<8)+steps; //no need for pixDiv
          break;
        }
        else {
          rayLen += smallestDist;
          x=dx*rayLen;y=dy*rayLen;z=dz*rayLen;//advance the ray
          steps--;
        }
      }
    }
  }
  updatePixels();
}
But why stop there. Now that you have a Processing ray-marcher try making a fractal.
Code:
const float minThreshold = 0.0001,maxDepth=8.0;//8.0 is about right for most fractals

void setup() {
  size(800, 600, P3D);
  noLoop();
}
float DE(float x, float y, float z){//this is our old friend menger
int n,iters=5;float t;
for(n=0;n<iters;n++){
x=fabs(x);y=fabs(y);z=fabs(z);//fabs is just abs for floats
if(x<y){t=x;x=y;y=t;}
if(y<z){t=y;y=z;z=t;}
if(x<y){t=x;x=y;y=t;}
x=x*3.0-2.0;y=y*3.0-2.0;z=z*3.0-2.0;
if(z<-1.0)z+=2.0;
}
return (sqrt(x*x+y*y+z*z)-1.5)*pow(3.0,-(float)iters);
}
void draw() {
  loadPixels();
  for (int i = 0; i < width; i++) {
    for (int j = 0; j < height; j++) {
      float dx=(float)i/(float)width-0.5;
      float dy=((float)j/(float)height-0.5)*(float)height/(float)width;
      float dz=1.0,r=1.0/sqrt(dx*dx+dy*dy+1.0);dx*=r;dy*=r;dz*=r;
      float x=0,y=0,z=0,rayLen=0,dist=maxDepth;
      int steps = 255;
      while (steps-- > 0 && dist>=minThreshold && rayLen < maxDepth) {
        rayLen += dist = DE(x,y,z-4.0)*0.5;
        x=dx*rayLen;y=dy*rayLen;z=dz*rayLen;
      }
      if(dist<minThreshold){//eventually you will want to do more here
        //... like find the surface normal and calculate lighting
        pixels[i+j*width] = (steps<<16)+(steps<<8)+steps;
      }
    }
  }
  updatePixels();
}
« Last Edit: April 07, 2012, 07:34:11 PM by eiffie » Logged
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« Reply #7 on: April 08, 2012, 06:38:31 AM »

Thank you, that makes sense why it didn't work before now. I'm going to play with your first code until I'm comfortable with it. I tried out your Menger code and it looks amazing but I have never made a Menger Sponge myself so I'll get to figuring that one out in a bit. If I could bother you for one last thing, how would you rotate a cube?

Here is an image of your Sponge as it looks in Processing: smiley

Logged
eiffie
Guest
« Reply #8 on: April 10, 2012, 05:55:10 PM »

Put the rotation as the first line of your DE function:

float DERotCube(float x, float y, float z, float size, float angle){
  float x2=x*cos(angle)-z*sin(angle),z2=z*cos(angle)+x*sin(angle);//rotating around the y axis
  return max(fabs(x2),max(fabs(y),fabs(z2)))-size;//return the distance estimate to the cube
}

Eventually as you explore more you will want something faster than Processing. I recommend you look into Fragmentarium at that point. For now tho just have fun!
Logged
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« Reply #9 on: April 12, 2012, 07:52:52 AM »

Thanks, for all the help eiffie, above and beyond the expectations for forum help. Now I just have to get the hang of positioning after the rotation, it is not very intuitive to me yet. It isn't possible to rotate an object from its center as opposed to around an axis is it?
Logged
eiffie
Guest
« Reply #10 on: April 12, 2012, 03:56:35 PM »

With ray-marching you have to remember you are rotating the ray not the object so sometimes things seem backwards. But certainly you can translate the center of the object to the origin - rotate around an axis then translate back. Just do everything backwards - lol. If you want to move the object up - move the ray down etc.
Logged
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« Reply #11 on: November 11, 2014, 10:00:04 AM »

I came back to this (it has been a while) and have been experimenting with sphere tracing, this is a test that made use of some of eiffie's code smiley

Logged
eiffie
Guest
« Reply #12 on: November 11, 2014, 05:40:38 PM »

The menger looks right but strangely the sphere seems banded. Should be DE=max(SphereDE,MengerDE) right? I haven't done this for a while either. smiley
Logged
asimes
Fractal Lover
**
Posts: 212



asimes
WWW
« Reply #13 on: November 11, 2014, 08:16:20 PM »

I see banding mostly just on the ground unless I'm missing something. Yup, it is the max of a sphere and Menger cube
Logged
Pages: [1]   Go Down
  Print  
 
Jump to:  

Related Topics
Subject Started by Replies Views Last post
Marching Cubes and attractor 3D Fractal Generation mishafe01 0 2226 Last post November 28, 2007, 01:42:09 PM
by mishafe01
What is ray marching General Discussion ricbennet 12 10402 Last post April 02, 2012, 09:48:41 PM
by David Makin
rod marching Programming willvarfar 14 2881 Last post August 02, 2012, 10:42:16 AM
by willvarfar
About periodical complex functions (new) Theories & Research hgjf2 4 588 Last post January 26, 2014, 09:10:23 PM
by Pauldelbrot
High-performance GLSL noise functions Fragmentarium Patryk Kizny 1 14544 Last post October 15, 2015, 12:30:42 AM
by DarkBeam

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines

Valid XHTML 1.0! Valid CSS! Dilber MC Theme by HarzeM
Page created in 0.331 seconds with 24 queries. (Pretty URLs adds 0.02s, 2q)