Raymarching Beginners' Thread
category: code [glöplog]
thats it? come on, most 4k's are widows-only anyway...
I don't even have a windows box anymore (other than vmware but fuck writing a raymarcher on that ;)
@xernobyl: If I wanted to do so I would just take a photo. The algorithm may seem inficient but the computer is very powerful.
@ RareWtFailWhale: The gradient is a vector. What I called modulus is in fact the norm. Sorry!
In your second example (the one that doesn't work properly) you should have
It would be better (for readability) to do:
Here is a fragmentarium script with some functions:
The method is not perfect but seem to give some good results.
@ RareWtFailWhale: The gradient is a vector. What I called modulus is in fact the norm. Sorry!
In your second example (the one that doesn't work properly) you should have
Code:
dr=sqrt(4.0*p.x*p.x + 4.0*p.y*py + (p.z*p.z + 2.0*(p.z - 1.0)*p.z)*(p.z*p.z + 2.0*(p.z - 1.0)*p.z));
It would be better (for readability) to do:
Code:
vec3 gradient=vec3(2.0*p.x, 2.0*p.y, p.z*(3.0*p.z - 2.0));
dr=length(gradient);
Here is a fragmentarium script with some functions:
Code:
#version 110 //to make sure it will work on glsl 1.10 compliant drivers
#info algebraic surfaces (and others) Distance Estimator (knighty 2011)
#include "DE-Raytracer.frag"
#include "MathUtils.frag"
#group DE parametres
//DE correction param: scaling factor
uniform float param1; slider[0,.75,1]
//DE correction param: Lipschitzator (lol) factor
uniform float param2; slider[0,4,10]
//Level set
uniform float LevelSet; slider[-1,0,1]
//Size
uniform float Sz; slider[-5,1,5]
uniform float time;
#define Phi (.5*(1.+sqrt(5.)))
#define W 1.6
#define W2 2.56
#define PHI 1.618034
#define PHI2 2.618034
#define PHI4 6.854102
#define Tau (1.+2.*Phi)
#define PI 3.141593
#define Eps 0.00048828125 //epsilon
#define IEps 2048.0 //Inverse of epsilon
void init() {
}
float sinesphere(in vec3 P){
P=sin(P*PI*Sz);
return (dot(P,P)-LevelSet);
}
float sine(in vec3 P){
P.xyz=P.xzy;
return (P.z-0.25*sin(length(P.xy)*5.*Sz-2.0*time)/(dot(P.xy,P.xy)+1.0));
}
float absolutely(in vec3 P){
return P.z-abs(P.x*P.y);
}
float CrossCap(in vec3 P){//cubic
P.xyz=5.*P.xzy;
vec3 P2=P*P;
return (-P2.x*P.y + P2.z-LevelSet);
}
float Chmutov8(in vec3 P){//octic
vec3 P2=P*P;
vec3 R=1.0+P2*32.0*Sz*(-1.0+P2*(5.0+P2*(-8.0+P2*4.0)));
return R.x+R.y+R.z;
}
float Barth6(vec3 z)//Bart's sextic
{
vec3 z2=z*z;
vec3 z3=0.2*Sz*PHI2*z2-z2.yzx;
float p1=4.*z3.x*z3.y*z3.z;
float r2=dot(z,z)-1.;
float p2=Tau*(r2*r2);
return p2-p1;
}
float Barth6Cusp(vec3 z)//Another Bart's sextic
{
vec3 z2=z*z;
vec3 z3=0.2*Sz*PHI2*z2-z2.yzx;
float p1=4.*z3.x*z3.y*z3.z;
float r2=dot(z,z)-1.;
float p2=Tau*(r2*r2*r2);
return p2-p1;
}
float BarthDecic(in vec3 P){//decic
float r2=dot(P,P);
vec3 P2=P*P;
float r4=dot(P2,P2);
vec3 P4=P2*P2;
return (8.0*Sz*(P2.x-PHI4*P2.y)*(P2.y-PHI4*P2.z)*(P2.z-PHI4*P2.x)*(r4-2.0*((P.x*P.y)*(P.x*P.y)+(P.x*P.z)*(P.x*P.z)+(P.y*P.z)*(P.y*P.z)))+(3.0+5.0*PHI)*(r2-W2)*(r2-W2)*(r2-(2.0-PHI)*W2)*(r2-(2.0-PHI)*W2)*W2);
}
float CrossCap4(vec3 P){//Quartic
vec3 P2=P*P;
return 4.*P2.x*dot(P,P)+P2.y*(P2.y+P2.z-1.);
}
float Mitchell(vec3 P){//sextic
vec3 P2=P*P;
float r2=dot(P,P);
float r2yz=dot(P.yz,P.yz);
return 4.*Sz*(P2.x*P2.x+r2yz*r2yz)+17.*P2.x*r2yz-20.*r2+17.;
}
float Dervish(vec3 P){//Quintic
P.z=-P.z;
float c=sqrt(5.-sqrt(5.))/2.;
float r=(1.+3.*sqrt(5.))/4.;
float q=(P.x*P.x+P.y*P.y-1.+r*P.z*P.z);q=(1.-c*P.z)*q*q;
float a=-(8./5.)*(1.+1./sqrt(5.))*sqrt(5.-sqrt(5.));
float h1=P.x-P.z;
float h2=cos(2.*PI/5.)*P.x-sin(2.*PI/5.)*P.y-P.z;
float h3=cos(4.*PI/5.)*P.x-sin(4.*PI/5.)*P.y-P.z;
float h4=cos(6.*PI/5.)*P.x-sin(6.*PI/5.)*P.y-P.z;
float h5=cos(8.*PI/5.)*P.x-sin(8.*PI/5.)*P.y-P.z;
float FF=h1*h2*h3*h4*h5;
return a*FF+Sz*q;
}
float CayleyCubic(vec3 P){//cubic
return 4.*Sz*dot(P,P) + 16.*P.x*P.y*P.z - 1.;
}
float FlipFlap(vec3 P){//cubic
return Sz*dot(P.xy,P.xy)+P.x*P.y*P.z;
}
float heart(vec3 z)//Thanks Alex-B
{
vec3 z2=z*z;
float lp = z2.x + Sz*z2.y + z2.z - 1.0;
float rp = z2.x*z2.z*z.z + 0.08888*z2.y*z2.z*z.z;
return lp*lp*lp-rp;
}
#define Fn Barth6Cusp//heart//sine//sinesphere//Chmutov8//BarthDecic//Dervish//CayleyCubic//FlipFlap//Barth6//Barth6//BarthDecic//Mitchell//CrossCap4//absolutely//
float DE(vec3 z)
{
float v =Fn(z);
float dv=length(IEps*(vec3(Fn(z+vec3(Eps,0.,0.)),Fn(z+vec3(0.,Eps,0.)),Fn(z+vec3(0.,0.,Eps)))-vec3(v)));
v-= LevelSet;
float k = 1.-1./(abs(v)+1.);
return param1*abs(v)/(dv+param2*k+0.01);//adding 0.01 in case the gradient is zero on the isosurface
}
The method is not perfect but seem to give some good results.
Quote:
why you ppl are into glsl so much? if you only knew the power of the hlsl side...
oh believe me, i know :))
otoh, it's true that there's still no "glsl-only" platform powerful enough for heavy raymarching
HEEEEEELP - I'm going crazy over here!!!
So I've made my first attempts at porting my raymarcher to a GPU shader.
I tried downloading VVVV or whatever it was called, but I never got it to work.
Instead I settled on using Mr.Doobs WebGL base, adapting/changing a bit, and soon I had my first GLSL code up and running.
I then started porting my Processing code. I soon noticed that I had to cut down on the number of iterations I ran, otherwise the browser would hang when loading the page (I'm guessing it does some kind of shader-compile in this step).
Anyway, got it running so far that my geometry rendered correctly, that is, I got cubes where I wanted and spheres where I wanted, so the basic marcher is working. However, the shading is totally fucked, my guess is that the normal calculation borks out somehow, but since there is no way of actually debugging the GLSL code, I'm totally stuck.
Look at this:
The sides of the cubes where X>0.0 looks somewhat correct, but spheres and X<0.0 of boxes is just way off...?
Any ideas what I'm doing wrong.
Here's the code:
So I've made my first attempts at porting my raymarcher to a GPU shader.
I tried downloading VVVV or whatever it was called, but I never got it to work.
Instead I settled on using Mr.Doobs WebGL base, adapting/changing a bit, and soon I had my first GLSL code up and running.
I then started porting my Processing code. I soon noticed that I had to cut down on the number of iterations I ran, otherwise the browser would hang when loading the page (I'm guessing it does some kind of shader-compile in this step).
Anyway, got it running so far that my geometry rendered correctly, that is, I got cubes where I wanted and spheres where I wanted, so the basic marcher is working. However, the shading is totally fucked, my guess is that the normal calculation borks out somehow, but since there is no way of actually debugging the GLSL code, I'm totally stuck.
Look at this:
The sides of the cubes where X>0.0 looks somewhat correct, but spheres and X<0.0 of boxes is just way off...?
Any ideas what I'm doing wrong.
Here's the code:
Code:
uniform vec2 resolution;
uniform float time;
float MAXD=1000.0;
float perspective=1.25,zoom=2.0;
vec3 cameraLocation=vec3(0.0, 0.0, -0.6);
vec3 light=vec3(-0.1,-0.1, -1.0);
vec3 sphere1=vec3(0.0,0.0,0);
vec3 sphere2=vec3(0.25,-0.25,-0.1);
vec3 sphere3=vec3(-0.25,-0.25,-0.1);
vec3 cube1=vec3(0,0.75,0);
vec3 cube2=vec3(-0.25,0.85,-0.25);
vec3 cube3=vec3(0.25,0.65,0.25);
float dist_sphere(vec3 pos, vec3 v,float r)
{
return distance(pos,v)-r;
}
float dist_cube(vec3 pos, vec3 v,float s)
{
float dx=abs(v.x-pos.x)-s;
float dy=abs(v.y-pos.y)-s;
float dz=abs(v.z-pos.z)-s;
vec3 tmpV=vec3(max(dx,0.0),max(dy,0.0),max(dz,0.0));
return length(tmpV);
}
float dist(vec3 v)
{
float d,mind=MAXD;
d=dist_sphere(sphere1,v,0.25);
if(d<mind) mind=d;
d=dist_sphere(sphere2,v,0.15);
if(d<mind) mind=d;
d=dist_sphere(sphere3,v,0.15);
if(d<mind) mind=d;
d=dist_cube(cube1,v,0.3);
if(d<mind) mind=d;
d=dist_cube(cube2,v,0.3);
if(d<mind) mind=d;
d=dist_cube(cube3,v,0.3);
if(d<mind) mind=d;
return mind;
}
vec3 calc_normal(vec3 v)
{
float e=0.0001;
vec3 n=vec3(
dist(vec3(v.x+e,v.y,v.z))-dist(vec3(v.x-e,v.y,v.z)),
dist(vec3(v.x,v.y+e,v.z))-dist(vec3(v.x,v.y-e,v.z)),
dist(vec3(v.x,v.y,v.z+e))-dist(vec3(v.x,v.y,v.z-e)));
n/=(2.0*e);
return n;
}
float ao(vec3 p, vec3 n, float d)
{
float o=1.0,ii=5.0;
for(int i=0;i<5;i++)
{
vec3 tmpV=p+n*(ii*d);
float tmp=dist(tmpV);
if(tmp<0.0) tmp=0.0;
o-=(ii*d-tmp)/pow(2.0,ii);
ii=ii-1.0;
}
return o;
}
void main(void)
{
float x,y,off=0.0;
vec4 col;
float rx,ry,d;
int steps=0;
vec3 ray,direction;
vec2 p = 2.0*gl_FragCoord.xy;
p.x/= resolution.x;
p.y/= resolution.x;
p-=1.0;
p.y=-p.y;
p=p/zoom;
ray=vec3(p.x,p.y,0);
ray=ray+cameraLocation;
direction=vec3(p.x*perspective,p.y*perspective,1.0);
direction=normalize(direction);
col=vec4(0.25+(p.y+0.5)/3.0,0.25+(p.y+0.5)/3.0,0.33+(p.y+0.5)/3.0,1.0);
for(int i=0;i<50;i++)
{
d=dist(ray);
if(d>=MAXD)
{
// Infinite distance
break;
}
if(d<0.0001)
{
vec3 n=calc_normal(ray);
float normlight=0.25*dot(light,n);
float aolight=ao(ray,n,0.15);
if(normlight<0.0) normlight=0.0;
float ambient=0.75;
float c=(normlight+ambient)*aolight;
col=vec4(c,c,c,1.0);
break;
}
ray+=direction*d*0.75;
}
gl_FragColor = col;
}
For some reason "n/=(2.0*e);" is not a good idea, use normalize.
Works for me, I only added the normalize, changed your distance function a bit and removed the *0.75 - because you don't need that for your current scene :)
The output from your original shader was not very different from the processing thing and didn't look as bad as what you posted.
Code:
float MAXD=1000.0;
float perspective=1.25,zoom=2.0;
vec3 cameraLocation=vec3(0.0, 0.0, -0.6);
vec3 light=vec3(-0.1,-0.1, -1.0);
vec3 sphere1=vec3(0.0,0.0,0);
vec3 sphere2=vec3(0.25,-0.25,-0.1);
vec3 sphere3=vec3(-0.25,-0.25,-0.1);
vec3 cube1=vec3(0,0.75,0);
vec3 cube2=vec3(-0.25,0.85,-0.25);
vec3 cube3=vec3(0.25,0.65,0.25);
float dist_sphere(vec3 pos, vec3 v,float r)
{
return distance(pos,v)-r;
}
float dist_cube(vec3 pos, vec3 v,float s)
{
float dx=abs(v.x-pos.x)-s;
float dy=abs(v.y-pos.y)-s;
float dz=abs(v.z-pos.z)-s;
vec3 tmpV=vec3(max(dx,0.0),max(dy,0.0),max(dz,0.0));
return length(tmpV);
}
float dist(vec3 v)
{
float d = dist_sphere(sphere1,v,0.25);
d=min(d, dist_sphere(sphere2,v,0.15));
d=min(d, dist_sphere(sphere3,v,0.15));
d=min(d, dist_cube(cube1,v,0.3));
d=min(d, dist_cube(cube2,v,0.3));
d=min(d, dist_cube(cube3,v,0.3));
return d;
}
vec3 calc_normal(vec3 v)
{
float e=0.0001;
vec3 n=vec3(
dist(vec3(v.x+e,v.y,v.z))-dist(vec3(v.x-e,v.y,v.z)),
dist(vec3(v.x,v.y+e,v.z))-dist(vec3(v.x,v.y-e,v.z)),
dist(vec3(v.x,v.y,v.z+e))-dist(vec3(v.x,v.y,v.z-e)));
return normalize(n);
}
float ao(vec3 p, vec3 n, float d)
{
float o=1.0,ii=5.0;
for(int i=0;i<5;i++)
{
vec3 tmpV=p+n*(ii*d);
float tmp=dist(tmpV);
if(tmp<0.0) tmp=0.0;
o-=(ii*d-tmp)/pow(2.0,ii);
ii=ii-1.0;
}
return o;
}
void main(void)
{
float x,y,off=0.0;
vec4 col;
float rx,ry,d;
int steps=0;
vec3 ray,direction;
vec2 p = 2.0*gl_FragCoord.xy;
p.x/= resolution.x;
p.y/= resolution.x;
p-=1.0;
p.y=-p.y;
p=p/zoom;
ray=vec3(p.x,p.y,0);
ray=ray+cameraLocation;
direction=vec3(p.x*perspective,p.y*perspective,1.0);
direction=normalize(direction);
col=vec4(0.25+(p.y+0.5)/3.0,0.25+(p.y+0.5)/3.0,0.33+(p.y+0.5)/3.0,1.0);
for(int i=0;i<50;i++)
{
d=dist(ray);
if(d>=MAXD)
{
// Infinite distance
break;
}
if(d<0.0001)
{
vec3 n=calc_normal(ray);
float normlight=0.25*dot(light,n);
float aolight=ao(ray,n,0.15);
if(normlight<0.0) normlight=0.0;
float ambient=0.75;
float c=(normlight+ambient)*aolight;
col=vec4(c,c,c,1.0);
break;
}
ray+=direction*d;
}
gl_FragColor = col;
}
Works for me, I only added the normalize, changed your distance function a bit and removed the *0.75 - because you don't need that for your current scene :)
The output from your original shader was not very different from the processing thing and didn't look as bad as what you posted.
Thanks for taking the time to look into this las, much appreciated!
I tried with your changes, and the left of boxes/spheres are no longer white, but instead they are constant grey, so still not working properly.
Could it be something in my WebGL setup code (stolen straight from mr doob) that is wrong? Or maybe that I have an ATI card?
Could you please try and run this in your WebGL-compatible browser and see how it looks:
http://ag1976.com/tmp/raymarching.html
(same scene but with a bit of movement added).
I tried with your changes, and the left of boxes/spheres are no longer white, but instead they are constant grey, so still not working properly.
Could it be something in my WebGL setup code (stolen straight from mr doob) that is wrong? Or maybe that I have an ATI card?
Could you please try and run this in your WebGL-compatible browser and see how it looks:
http://ag1976.com/tmp/raymarching.html
(same scene but with a bit of movement added).
Works here, shading looks correct, and I'm on ATI (although on mac - it doesn't tend to make much difference). Speedy too.
On my NV card that one almost looks perfect (I see some artifacts when the camera comes too close).
You might want to try to replace
with
+ ATI sucks.
You might want to try to replace
Code:
float normlight=0.25*dot(light,n);
with
Code:
float normlight=0.25*max(0.,dot(light,n));
+ ATI sucks.
(And I don't think this can be caused by the WebGL Code)
A graphics-card driver update helped me in a similar case.
@Sdw: don't know if it helps, but i get the same error as you.
Windows 7 x64, GeForce 285 GTX, Firefox 4
Windows 7 x64, GeForce 285 GTX, Firefox 4
Works on my work box too (also ati + mac, but different card + chrome instead of firefox).
@Sdw: Doesn't work here either.
Windows XP, GeForce Quadro FX 580 + recent driver, Firefox 4
Windows XP, GeForce Quadro FX 580 + recent driver, Firefox 4
Radeon HD 6950, Catalyst 11.5
Same look as kusma's here on gtx460, 270.61...
Yeah, I get the same result as kusma's screenshot (which appears to have disappeared now btw).
Can someone who has it working post a screenshot? I'd love to see how it looks when it runs properly! :D
I must say that I'm very confused to as to why it isn't working, I think what I have done is pretty straightforward, but I'm a toooootal noob on GLSL so I might have totally missed something.
Can someone who has it working post a screenshot? I'd love to see how it looks when it runs properly! :D
I must say that I'm very confused to as to why it isn't working, I think what I have done is pretty straightforward, but I'm a toooootal noob on GLSL so I might have totally missed something.
This is what I see:
(white borders are my crap screen grabbing skills, and I get the same "closest face of the bottom cube glitches when camera gets close" thing too)
psonice: Yeah, that's pretty much the expected outcome, the glitching is probably due to me having to settle for max 25 iterations instead of 50 as I want to. With 50 the shader compile takes so long that the browser gives up on the Javascript.
Really strange, so it works for you and some others, but for most people, including me, it doesn't work and that applies to both ATI and Nvidia?
Confusion...
Really strange, so it works for you and some others, but for most people, including me, it doesn't work and that applies to both ATI and Nvidia?
Confusion...
what i'm learning right now is that nothing changed.
web-something and platform-independent-bullshit hypes still suffer from the same problems as they did 10 years ago.
web-something and platform-independent-bullshit hypes still suffer from the same problems as they did 10 years ago.
sdw: what's the compilation time with 25 iterations like it is now? Because for me, it's like 0 seconds, and unless it's trying to do some kind of crazy optimisation, that should be the same at 50 (I mean, I've used 300+ iterations before now, and not noticed any compiling delay at all!)
psonice: Well, I don't know if it is compile time, I just assumed it was, I mean the time that elapses from when I load the page in the browser until the first frame is rendered.
I just tried here at work (somewhat slower computer than at home) and it took just about 30 seconds with 25 iterations enabled.
I just tried here at work (somewhat slower computer than at home) and it took just about 30 seconds with 25 iterations enabled.
Here's a link to a page with the screenshot. Sorry for the annoyance, seems awesomescreenshot tries really hard not to have people link directly.