Raymarching Beginners' Thread
category: code [glöplog]
Yep that one is from iq. It's works pretty well.
In case you need the euclidean distance one could do something like this:
In case you need the euclidean distance one could do something like this:
Code:
float box(vec3 p, vec3 s) {
vec3 d = abs(p) - s;
float m = max(d.x,max(d.y, d.z));
return mix(m, length(max(d, 0.0)),step(0.,m));
}
thank you, las
there was a mistake in the name of first experiment, not Inverted Malevich 1.0/f(x), but Negative Malevich -1.0*f(x)
my second raymarch experiment
Cornell Box with Ambient Occlusion (AO algorithm by iq)
there was a mistake in the name of first experiment, not Inverted Malevich 1.0/f(x), but Negative Malevich -1.0*f(x)
my second raymarch experiment
Cornell Box with Ambient Occlusion (AO algorithm by iq)
Code:
#ifdef GL_ES
precision highp float;
#endif
uniform float time;
uniform vec2 resolution;
uniform vec4 mouse;
float planeX(in vec3 p, in float x)
{
return distance(p,vec3(x,p.y,p.z));
}
float planeY(in vec3 p, in float y)
{
return distance(p,vec3(p.x,y,p.z));
}
float planeZ(in vec3 p, in float z)
{
return distance(p,vec3(p.x,p.y,z));
}
float f(vec3 p)
{
float sdf;
// Cornell Box (left - red, right - green, other - grey)
float sdf1 = planeX(p,-1.0); // left wall red
float sdf2 = planeX(p,1.0); // right wall green
float sdf3 = planeY(p,-1.0); // floor grey
float sdf4 = planeY(p,1.0); // ceil grey
float sdf5 = planeZ(p,1.0); // back wall grey
sdf = min(sdf1,min(sdf2,min(sdf3,min(sdf4,sdf5))));
return sdf;
}
vec3 calcNormal( in vec3 pos )
{
vec3 e = vec3(0.0002, 0.0, 0.0);
vec3 n;
n.x = f(pos + e.xyy) - f(pos - e.xyy);
n.y = f(pos + e.yxy) - f(pos - e.yxy);
n.z = f(pos + e.yyx) - f(pos - e.yyx);
return normalize(n);
}
void main(void)
{
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
vec3 color = vec3(0.0,0.0,0.0);
// perspective fov 45?
vec3 Ro = vec3(0.0,0.0,-1.0); // ray origin
vec3 Rd = vec3(p.x,p.y,1.0); // ray direction
Rd = normalize(Rd);
float t = 0.0;
const int maxsteps = 75;
for(int steps = 0; steps < maxsteps; steps++)
{
vec3 pos = Ro+t*Rd;
float d = f(pos);
const float eps = 0.001;
if(d < eps)
{
if(d == planeX(pos,-1.0))
// color = vec3(0.63,0.06,0.04);
color = vec3(0.64,0.15,0.1);
else if(d == planeX(pos,1.0))
// color = vec3(0.15,0.48,0.09);
color = vec3(0.15,0.5,0.15);
else
color = vec3(0.76,0.75,0.5);
//color = pos;
vec3 n = calcNormal( pos );
// ambient occlusion
float ao;
float totao = 0.0;
float sca = 1.0;
for( int aoi=0; aoi<5; aoi++ )
{
float hr = 0.01 + 0.02*float(aoi*aoi);
vec3 aopos = n * hr + pos;
float dd = f( aopos );
ao = -(dd-hr);
totao += ao*sca;
sca *= 0.75;
}
ao = 1.0 - clamp( totao, 0.0, 1.0 );
color = color * ao;
break;
}
t = t + d;
}
gl_FragColor = vec4(color,1.0);
}
you can change the AO calculation slightly and get some color bleeding:
But at this point it would be beneficial to generate the AO points really in an hemisphere, not just in a line, i guess.
Code:
#ifdef GL_ES
precision highp float;
#endif
uniform float time;
uniform vec2 resolution;
uniform vec4 mouse;
float planeX(in vec3 p, in float x)
{
return distance(p,vec3(x,p.y,p.z));
}
float planeY(in vec3 p, in float y)
{
return distance(p,vec3(p.x,y,p.z));
}
float planeZ(in vec3 p, in float z)
{
return distance(p,vec3(p.x,p.y,z));
}
float sphere( in vec3 p, in vec3 c, in float r )
{
return distance( p, c ) -r;
}
float f(vec3 p, out int id)
{
float sdf;
// Cornell Box (left - red, right - green, other - grey)
float sdf1 = planeX(p,-1.0); // left wall red
float sdf2 = planeX(p,1.0); // right wall green
float sdf3 = planeY(p,-1.0); // floor grey
float sdf4 = planeY(p,1.0); // ceil grey
float sdf5 = planeZ(p,1.0); // back wall grey
float sdf6 = sphere(p,vec3(0.4,-0.8,0.1), 0.2 );
float sdf7 = sphere(p,vec3(0.0,-0.8,0.5), 0.2 );
sdf=sdf1; id=0;
if( sdf2<sdf ) { sdf=sdf2; id=1; }
if( sdf3<sdf ) { sdf=sdf3; id=2; }
if( sdf4<sdf ) { sdf=sdf4; id=3; }
if( sdf5<sdf ) { sdf=sdf5; id=4; }
if( sdf6<sdf ) { sdf=sdf6; id=5; }
if( sdf7<sdf ) { sdf=sdf7; id=6; }
return sdf;
}
vec3 calcNormal( in vec3 pos )
{
vec3 e = vec3(0.0002, 0.0, 0.0);
vec3 n;
int id;
n.x = f(pos + e.xyy,id) - f(pos - e.xyy,id);
n.y = f(pos + e.yxy,id) - f(pos - e.yxy,id);
n.z = f(pos + e.yyx,id) - f(pos - e.yyx,id);
return normalize(n);
}
vec3 getColor( int id )
{
vec3 color;
if(id==0) color = vec3(0.64,0.15,0.1);
else if(id==1) color = vec3(0.15,0.5,0.15);
else if(id==5) color = vec3(0.15,0.15,0.5);
else if(id==6) color = vec3(0.5,0.15,0.5);
else color = vec3(0.76,0.75,0.5);
return color;
}
void main(void)
{
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
vec3 color = vec3(0.0,0.0,0.0);
// perspective fov 45?
vec3 Ro = vec3(0.0,0.0,-1.0); // ray origin
vec3 Rd = vec3(p.x,p.y,1.0); // ray direction
Rd = normalize(Rd);
float t = 0.0;
const int maxsteps = 75;
for(int steps = 0; steps < maxsteps; steps++)
{
vec3 pos = Ro+t*Rd;
int id;
float d = f(pos,id);
const float eps = 0.001;
if(d < eps)
{
color = getColor( id );
vec3 n = calcNormal( pos );
// ambient occlusion
vec4 totao = vec4(0.0);
float sca = 1.0;
for( int aoi=0; aoi<5; aoi++ )
{
float hr = 0.01 + 0.02*float(aoi*aoi);
vec3 aopos = n * hr + pos;
int tid;
float dd = f( aopos, tid );
float ao = clamp( -(dd-hr), 0.0, 1.0);
totao += ao*sca*vec4(getColor(tid),1.0);
sca *= 0.75;
}
totao.w = 1.0 - clamp( 1.5*totao.w, 0.0, 1.0 );
color = 1.2*color*(0.6+0.4*dot(n,vec3(0.577)))*totao.w;
color += 2.4*vec3(1.0)*pow(0.5+0.5*dot(n,Rd),2.0)*totao.w;
color += 2.0*(0.5+0.5*color)*totao.xyz*totao.w;
break;
}
t = t + d;
}
gl_FragColor = vec4(color,1.0);
}
But at this point it would be beneficial to generate the AO points really in an hemisphere, not just in a line, i guess.
Nice work iq + SLeo!
One question though, anyone got any ideas for antialising? Its pretty much the only thing that hasn't been talked about yet.
One question though, anyone got any ideas for antialising? Its pretty much the only thing that hasn't been talked about yet.
yes hemispherical AO is that it supposed to be by definition of AO, I will try to implement it later, there are some similarities to casting rays in random direction i.e. vec3 noise(int n) and calculation average!?
Adding point light halfed FPS and also this my perspective Rd is sux
Antialiasing has Scatter Not Gather problem, so I think it is possible using multistep post-processing using world space positions texture? Doing this in fragment shader of two triangles will be not realtime?
Adding point light halfed FPS and also this my perspective Rd is sux
Antialiasing has Scatter Not Gather problem, so I think it is possible using multistep post-processing using world space positions texture? Doing this in fragment shader of two triangles will be not realtime?
For anti aliasing, you can use "cone tracing" described in this paper.
Else you can implement MLAA in a post process pass!
Else you can implement MLAA in a post process pass!
sleo: nice shadows.
I love how with these things, the first pic looks really crappy (like your first box, and my first one which was just a grey sphere) but it's awesome because of what it represents. Then in a short time it goes from something crappy looking to something really cool :)
I love how with these things, the first pic looks really crappy (like your first box, and my first one which was just a grey sphere) but it's awesome because of what it represents. Then in a short time it goes from something crappy looking to something really cool :)
psonice: hope you dont mean the shadows in the last image?
psionice: uhmm.. the last picture is (more or less) the original cornell box, ie the "target"
haha, missed that :D Oops!
Nice cornell box you got there. Can you share the code? :)
I would like to try some further colorbleeding - "hemisphere" stuff.
I would like to try some further colorbleeding - "hemisphere" stuff.
(and yes I am even too lazy to do my own cornell box) ;)
las, most of the code is there above :)
I had a quick go at the cornell box during lunch. Got it looking kind of ok, but the lighting sucks compared to the one above. My soft shadows don't look anywhere near as good.
I tried colour bleeding too, but it ended up looking like broken reflections. So in the end I turned on reflections properly and played with a cornell mirror box ;)
I had a quick go at the cornell box during lunch. Got it looking kind of ok, but the lighting sucks compared to the one above. My soft shadows don't look anywhere near as good.
I tried colour bleeding too, but it ended up looking like broken reflections. So in the end I turned on reflections properly and played with a cornell mirror box ;)
it is not my Cornell Box, it is original Cornell Box, I also would like to see shadertoyish code for it :)
sleo: you pretty much have the code, no? Just replace the balls with boxes. And add some crazy lighting :)
I'm sure it's possible to render at that quality with raymarching, just maybe not quite in realtime ;) We should be able to get a decent compromise though.
I'm sure it's possible to render at that quality with raymarching, just maybe not quite in realtime ;) We should be able to get a decent compromise though.
psonice: given that you're dealing with a bunch of boxes there are you seriously going to raymarch? ray/box test anyone? :)
raytrace the boxes, raymarch the shadows? (That's how I've been doing it anyway.. or combination trace/march for scenes with stuff I can't trace).
One thing I've not managed to figure out though: is it possible to raytrace a cube with domain repetition?
One thing I've not managed to figure out though: is it possible to raytrace a cube with domain repetition?
@XT95^FRq Thanks! I'd seen that site but didn't know they had released the code. Looks my Antialising problem is solved. For now :D
Ok, I just ported the MLAA shader from http://www.iryoku.com/mlaa/ to GLSL and have run into some problems with the weight calculation code. The rest of the shaders are really straightforward but I have no idea what this is doing. All it does is return a black screen when I run it. Its also asking for some areamap texture, which I gave it, but I have no idea what it does. Still a black screen :/
Heres the code in question.
/**
* Ok, we have the distance and both crossing edges, can you please return
* the float2 blending weights?
*/
float2 Area(float2 distance, float e1, float e2) {
// * By dividing by areaSize - 1.0 below we are implicitely offsetting to
// always fall inside of a pixel
// * Rounding prevents bilinear access precision problems
float areaSize = MAX_DISTANCE * 5;
float2 pixcoord = MAX_DISTANCE * round(4.0 * float2(e1, e2)) + distance;
float2 texcoord = pixcoord / (areaSize - 1.0);
return areaTex.SampleLevel(PointSampler, texcoord, 0).rg;
}
/**
* Search functions for the 2nd pass.
*/
float SearchXLeft(float2 texcoord) {
texcoord -= float2(1.5, 0.0) * PIXEL_SIZE;
float e = 0.0;
// We offset by 0.5 to sample between edgels, thus fetching two in a row
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).g;
// We compare with 0.9 to prevent bilinear access precision problems
[flatten] if (e < 0.9) break;
texcoord -= float2(2.0, 0.0) * PIXEL_SIZE;
}
// When we exit the loop without founding the end, we want to return
// -2 * maxSearchSteps
return max(-2.0 * i - 2.0 * e, -2.0 * maxSearchSteps);
}
float SearchXRight(float2 texcoord) {
texcoord += float2(1.5, 0.0) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).g;
[flatten] if (e < 0.9) break;
texcoord += float2(2.0, 0.0) * PIXEL_SIZE;
}
return min(2.0 * i + 2.0 * e, 2.0 * maxSearchSteps);
}
float SearchYUp(float2 texcoord) {
texcoord -= float2(0.0, 1.5) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).r;
[flatten] if (e < 0.9) break;
texcoord -= float2(0.0, 2.0) * PIXEL_SIZE;
}
return max(-2.0 * i - 2.0 * e, -2.0 * maxSearchSteps);
}
float SearchYDown(float2 texcoord) {
texcoord += float2(0.0, 1.5) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).r;
[flatten] if (e < 0.9) break;
texcoord += float2(0.0, 2.0) * PIXEL_SIZE;
}
return min(2.0 * i + 2.0 * e, 2.0 * maxSearchSteps);
}
/**
* S E C O N D P A S S
*/
float4 BlendingWeightCalculationPS(float4 position : SV_POSITION,
float2 texcoord : TEXCOORD0) : SV_TARGET {
float4 weights = 0.0;
float2 e = edgesTex.SampleLevel(PointSampler, texcoord, 0).rg;
[branch]
if (e.g) { // Edge at north
// Search distances to the left and to the right:
float2 d = float2(SearchXLeft(texcoord), SearchXRight(texcoord));
// Now fetch the crossing edges. Instead of sampling between edgels, we
// sample at -0.25, to be able to discern what value has each edgel:
float4 coords = mad(float4(d.x, -0.25, d.y + 1.0, -0.25),
PIXEL_SIZE.xyxy, texcoord.xyxy);
float e1 = edgesTex.SampleLevel(LinearSampler, coords.xy, 0).r;
float e2 = edgesTex.SampleLevel(LinearSampler, coords.zw, 0).r;
// Ok, we know how this pattern looks like, now it is time for getting
// the actual area:
weights.rg = Area(abs(d), e1, e2);
}
[branch]
if (e.r) { // Edge at west
// Search distances to the top and to the bottom:
float2 d = float2(SearchYUp(texcoord), SearchYDown(texcoord));
// Now fetch the crossing edges (yet again):
float4 coords = mad(float4(-0.25, d.x, -0.25, d.y + 1.0),
PIXEL_SIZE.xyxy, texcoord.xyxy);
float e1 = edgesTex.SampleLevel(LinearSampler, coords.xy, 0).g;
float e2 = edgesTex.SampleLevel(LinearSampler, coords.zw, 0).g;
// Get the area for this direction:
weights.ba = Area(abs(d), e1, e2);
}
return saturate(weights);
}
/**
* Ok, we have the distance and both crossing edges, can you please return
* the float2 blending weights?
*/
float2 Area(float2 distance, float e1, float e2) {
// * By dividing by areaSize - 1.0 below we are implicitely offsetting to
// always fall inside of a pixel
// * Rounding prevents bilinear access precision problems
float areaSize = MAX_DISTANCE * 5;
float2 pixcoord = MAX_DISTANCE * round(4.0 * float2(e1, e2)) + distance;
float2 texcoord = pixcoord / (areaSize - 1.0);
return areaTex.SampleLevel(PointSampler, texcoord, 0).rg;
}
/**
* Search functions for the 2nd pass.
*/
float SearchXLeft(float2 texcoord) {
texcoord -= float2(1.5, 0.0) * PIXEL_SIZE;
float e = 0.0;
// We offset by 0.5 to sample between edgels, thus fetching two in a row
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).g;
// We compare with 0.9 to prevent bilinear access precision problems
[flatten] if (e < 0.9) break;
texcoord -= float2(2.0, 0.0) * PIXEL_SIZE;
}
// When we exit the loop without founding the end, we want to return
// -2 * maxSearchSteps
return max(-2.0 * i - 2.0 * e, -2.0 * maxSearchSteps);
}
float SearchXRight(float2 texcoord) {
texcoord += float2(1.5, 0.0) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).g;
[flatten] if (e < 0.9) break;
texcoord += float2(2.0, 0.0) * PIXEL_SIZE;
}
return min(2.0 * i + 2.0 * e, 2.0 * maxSearchSteps);
}
float SearchYUp(float2 texcoord) {
texcoord -= float2(0.0, 1.5) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).r;
[flatten] if (e < 0.9) break;
texcoord -= float2(0.0, 2.0) * PIXEL_SIZE;
}
return max(-2.0 * i - 2.0 * e, -2.0 * maxSearchSteps);
}
float SearchYDown(float2 texcoord) {
texcoord += float2(0.0, 1.5) * PIXEL_SIZE;
float e = 0.0;
for (int i = 0; i < maxSearchSteps; i++) {
e = edgesTex.SampleLevel(LinearSampler, texcoord, 0).r;
[flatten] if (e < 0.9) break;
texcoord += float2(0.0, 2.0) * PIXEL_SIZE;
}
return min(2.0 * i + 2.0 * e, 2.0 * maxSearchSteps);
}
/**
* S E C O N D P A S S
*/
float4 BlendingWeightCalculationPS(float4 position : SV_POSITION,
float2 texcoord : TEXCOORD0) : SV_TARGET {
float4 weights = 0.0;
float2 e = edgesTex.SampleLevel(PointSampler, texcoord, 0).rg;
[branch]
if (e.g) { // Edge at north
// Search distances to the left and to the right:
float2 d = float2(SearchXLeft(texcoord), SearchXRight(texcoord));
// Now fetch the crossing edges. Instead of sampling between edgels, we
// sample at -0.25, to be able to discern what value has each edgel:
float4 coords = mad(float4(d.x, -0.25, d.y + 1.0, -0.25),
PIXEL_SIZE.xyxy, texcoord.xyxy);
float e1 = edgesTex.SampleLevel(LinearSampler, coords.xy, 0).r;
float e2 = edgesTex.SampleLevel(LinearSampler, coords.zw, 0).r;
// Ok, we know how this pattern looks like, now it is time for getting
// the actual area:
weights.rg = Area(abs(d), e1, e2);
}
[branch]
if (e.r) { // Edge at west
// Search distances to the top and to the bottom:
float2 d = float2(SearchYUp(texcoord), SearchYDown(texcoord));
// Now fetch the crossing edges (yet again):
float4 coords = mad(float4(-0.25, d.x, -0.25, d.y + 1.0),
PIXEL_SIZE.xyxy, texcoord.xyxy);
float e1 = edgesTex.SampleLevel(LinearSampler, coords.xy, 0).g;
float e2 = edgesTex.SampleLevel(LinearSampler, coords.zw, 0).g;
// Get the area for this direction:
weights.ba = Area(abs(d), e1, e2);
}
return saturate(weights);
}
Mewler: no idea on the shader as a whole, not tried it, but does the shader compile?
You have a bunch of ints used in floats there (as in float x = 0; instead of float x = 0.;) which will surely break it on ATI as it's against the spec. Nvidia will likely let it go and run it. (Which is why you should code on ATI if you're doing openGL :)
You have a bunch of ints used in floats there (as in float x = 0; instead of float x = 0.;) which will surely break it on ATI as it's against the spec. Nvidia will likely let it go and run it. (Which is why you should code on ATI if you're doing openGL :)
no, it's hlsl and ints are okay to use there, for both nvidia and ati
glsl sucks!=D
glsl sucks!=D
Hmm... but he said he ported it to glsl. And then posted hlsl code, which I totally failed to notice. Somebody needs more sleep, but I don't know who. Maybe all of us need more sleep :)
Mewler: I also tried this implementation of MLAA and it works perfect. But I was using dx9 version.
If you just copied shader code (so its ok) problem must be somewhere else.
If you just copied shader code (so its ok) problem must be somewhere else.
we can use "sphere tracing's" parallel surfaces problem as cool effect :)
Code:
color = vec3(float(steps)/float(maxsteps));
gl_FragColor = vec4(color,1.0);
return;