Raymarching Beginners' Thread
category: code [glöplog]
You are not the first to figure that out ;)
Watch for instance cdak.
Watch for instance cdak.
or Pulse
I think there's a TON of quite stuff to use that actually..
@SLeo I use this for my glow, completely kills banding.
Code:
// Marching Loop...
m+=d;
a+=smoothstep(.0,.00555555/m,d);
// Bla bla bla
glow=smoothstep(0,1.6180339887,a*0.00555555);
glow*=glow*1.6180339887;
You don't need to use 1.6180- but it looks good for HDR, plus I love messing with the golden ration. Try twisting things with it :D
@psonice I ported it perfectly over to opengl, it compiles fine, just doesn't render anything. Here's my ported code for that part. A few hacks, but I just want to get it to render the weights. All the other passes I've ported without problems. I think its something to do with the area calculation texture, but I don't have a clue what it does so I can't work it out.
Code:
uniform sampler2D Tex0;
uniform sampler2D Tex1;
float SearchXLeft(vec2 texcoord) {
texcoord -= vec2(1.5, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
int i;
// We offset by 0.5 to sample between edgels, thus fetching two in a row
for (i = 0; i < 8; i++) {
e = texture2D(Tex0, texcoord).g;
// We compare with 0.9 to prevent bilinear access precision problems
//if (e < 0.9) {return max(-2.0 * i - 2.0 * e, -2.0 * 8);};
texcoord -= vec2(2.0, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
// 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 * 8);
}
float SearchXRight(vec2 texcoord) {
texcoord += vec2(1.5, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
int i;
for (i = 0; i < 8; i++) {
e = texture2D(Tex0, texcoord).g;
//if (e < 0.9) {min(2.0 * i + 2.0 * e, 2.0 * 8);};
texcoord += vec2(2.0, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return min(2.0 * i + 2.0 * e, 2.0 * 8);
}
float SearchYUp(vec2 texcoord) {
texcoord -= vec2(0.0, 1.5) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
int i;
for (i = 0; i < 8; i++) {
e = texture2D(Tex0, texcoord).r;
// if (e < 0.9) {max(-2.0 * i - 2.0 * e, -2.0 * 8);};
texcoord -= vec2(0.0, 2.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return max(-2.0 * i - 2.0 * e, -2.0 * 8);
}
float SearchYDown(vec2 texcoord) {
texcoord += vec2(0.0, 1.5) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
int i;
for (i = 0; i < 8; i++) {
e = texture2D(Tex0, texcoord).r;
//if (e < 0.9) {min(2.0 * i + 2.0 * e, 2.0 * 8);};
texcoord += vec2(0.0, 2.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return min(2.0 * i + 2.0 * e, 2.0 * 8);
}
vec4 mad(vec4 a, vec4 b, vec4 c) {
return a*b+c;
}
vec2 Area(vec2 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 = 32 * 5;
vec2 r = 4.0 * vec2(e1, e2);
//r.x = sign(r.x) * floor(abs(r.x) + 0.5);
//r.y = sign(r.y) * floor(abs(r.y) + 0.5);
vec2 pixcoord = 32 * r + distance;
vec2 texcoord = pixcoord / (areaSize - 1.0);
return texture2D(Tex1, texcoord).rg;
}
void main(void)
{
vec4 weights = vec4(0.0);
vec2 e = texture2D(Tex0, gl_TexCoord[0].xy).rg;
if (e.g>0.0f) { // Edge at north
// Search distances to the left and to the right:
vec2 d = vec2(SearchXLeft(gl_TexCoord[0].xy), SearchXRight(gl_TexCoord[0].xy));
// 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:
vec4 coords = mad(vec4(d.x, -0.25, d.y + 1.0, -0.25), vec2(1.0 / 1920.0, 1.0 / 1080.0).xyxy, gl_TexCoord[0].xyxy);
float e1 = texture2D(Tex0, coords.xy, 0).r;
float e2 = texture2D(Tex0, 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);
}
if (e.r>0.0f) { // Edge at west
// Search distances to the top and to the bottom:
vec2 d = vec2(SearchYUp(gl_TexCoord[0].xy), SearchYDown(gl_TexCoord[0].xy));
// Now fetch the crossing edges (yet again):
vec4 coords = mad(vec4(-0.25, d.x, -0.25, d.y + 1.0), vec2(1.0 / 1920.0, 1.0 / 1080.0).xyxy, gl_TexCoord[0].xyxy);
float e1 = texture2D(Tex0, coords.xy, 0).g;
float e2 = texture2D(Tex0, coords.zw, 0).g;
// Get the area for this direction:
weights.ba = Area(abs(d), e1, e2);
}
gl_FragColor = clamp(weights, 0.0, 1.0);
}
Worked for me, after I fixed the 'nvidia bugs' ;) (There was still a bunch of int * float operations, and floats declared without the decimal). I've increased the brightness a lot to make it more visible (maybe if your screen isn't quite right, it's working ok but it's too dark to see?)
Code:
uniform sampler2D Tex0;
uniform sampler2D Tex1;
float SearchXLeft(vec2 texcoord) {
texcoord -= vec2(1.5, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
float i;
// We offset by 0.5 to sample between edgels, thus fetching two in a row
for (i = 0.; i < 8.; i++) {
e = texture2D(Tex0, texcoord).g;
// We compare with 0.9 to prevent bilinear access precision problems
//if (e < 0.9) {return max(-2.0 * i - 2.0 * e, -2.0 * 8);};
texcoord -= vec2(2.0, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
// 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 * 8.);
}
float SearchXRight(vec2 texcoord) {
texcoord += vec2(1.5, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
float i;
for (i = 0.; i < 8.; i++) {
e = texture2D(Tex0, texcoord).g;
//if (e < 0.9) {min(2.0 * i + 2.0 * e, 2.0 * 8);};
texcoord += vec2(2.0, 0.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return min(2.0 * i + 2.0 * e, 2.0 * 8.);
}
float SearchYUp(vec2 texcoord) {
texcoord -= vec2(0.0, 1.5) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
float i;
for (i = 0.; i < 8.; i++) {
e = texture2D(Tex0, texcoord).r;
// if (e < 0.9) {max(-2.0 * i - 2.0 * e, -2.0 * 8);};
texcoord -= vec2(0.0, 2.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return max(-2.0 * i - 2.0 * e, -2.0 * 8.);
}
float SearchYDown(vec2 texcoord) {
texcoord += vec2(0.0, 1.5) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
float e = 0.0;
float i;
for (i = 0.; i < 8.; i++) {
e = texture2D(Tex0, texcoord).r;
//if (e < 0.9) {min(2.0 * i + 2.0 * e, 2.0 * 8);};
texcoord += vec2(0.0, 2.0) * vec2(1.0 / 1920.0, 1.0 / 1080.0);
}
return min(2.0 * i + 2.0 * e, 2.0 * 8.);
}
vec4 mad(vec4 a, vec4 b, vec4 c) {
return a*b+c;
}
vec2 Area(vec2 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 = 32. * 5.;
vec2 r = 4.0 * vec2(e1, e2);
//r.x = sign(r.x) * floor(abs(r.x) + 0.5);
//r.y = sign(r.y) * floor(abs(r.y) + 0.5);
vec2 pixcoord = 32. * r + distance;
vec2 texcoord = pixcoord / (areaSize - 1.0);
return texture2D(Tex1, texcoord).rg;
}
void main(void)
{
vec4 weights = vec4(0.0);
vec2 e = texture2D(Tex0, gl_TexCoord[0].xy).rg;
if (e.g>0.0) { // Edge at north
// Search distances to the left and to the right:
vec2 d = vec2(SearchXLeft(gl_TexCoord[0].xy), SearchXRight(gl_TexCoord[0].xy));
// 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:
vec4 coords = mad(vec4(d.x, -0.25, d.y + 1.0, -0.25), vec2(1.0 / 1920.0, 1.0 / 1080.0).xyxy, gl_TexCoord[0].xyxy);
float e1 = texture2D(Tex0, coords.xy, 0.).r;
float e2 = texture2D(Tex0, 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);
}
if (e.r>0.0) { // Edge at west
// Search distances to the top and to the bottom:
vec2 d = vec2(SearchYUp(gl_TexCoord[0].xy), SearchYDown(gl_TexCoord[0].xy));
// Now fetch the crossing edges (yet again):
vec4 coords = mad(vec4(-0.25, d.x, -0.25, d.y + 1.0), vec2(1.0 / 1920.0, 1.0 / 1080.0).xyxy, gl_TexCoord[0].xyxy);
float e1 = texture2D(Tex0, coords.xy, 0.).g;
float e2 = texture2D(Tex0, coords.zw, 0.).g;
// Get the area for this direction:
weights.ba = Area(abs(d), e1, e2);
}
gl_FragColor = clamp(weights*4., 0.0, 1.0);
}
looks... beautiful... should i wear some sort of fancy yellow-blue stereo glasses?
No, weighted glasses ;) (it's generating weightings for the AA filter).
Thanks allot psonice! But it still doesn't work... Still completely black no matter how much I increase the weights by. How are you loading the areatexture(Tex1)? I might be doing it the wrong way of something.
mewler: I had no idea what the textures should be, so I just stuck the colour texture into one and depth into the other. It seemed to generate a weight map, but maybe that's what's wrong?
in code you are using, tex0 is texture with detected edges (they provide some shader code how to do it) and tex1 (areatexture) is some precalculated texture to get proper weights for blending. If you've downloaded their source code you can find this texture in Media folder - AreaMap9.dds. And you should sample it with clamping and without filtering.
@dys129 Thanks, but it still doesn't work. I've ported and tweaked the edge detection shaders to give the EXACT same output as the example program. I've loaded the areatex9 file (Converted to png, is that a problem?), turned of filtering and clamping, and the weight shader is still giving me bogus output. :(
Heres the areatex9 load code, am I doing it right?
Also, if I'm loading the areamap9 file, then shouldn't the maxdistance be 8?
Heres the areatex9 load code, am I doing it right?
Code:
int b=8;
unsigned char *data = stbi_load(fname, &sx, &sy, &b, 4);
glGenTextures(1, &this->tex[texnum]);
glBindTexture(GL_TEXTURE_2D, this->tex[texnum]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sx, sy, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
Also, if I'm loading the areamap9 file, then shouldn't the maxdistance be 8?
you should be using (i guess, im using dx):
and areamap9 is in A8L8 format, so in png you propably have L value in rgb, and A in alpha. So you should sample it like this:
texture2D(Tex1, texcoord).ra;
difference between areamap9,17,33 is just resolution (higher = better and slower).
Code:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
and areamap9 is in A8L8 format, so in png you propably have L value in rgb, and A in alpha. So you should sample it like this:
texture2D(Tex1, texcoord).ra;
difference between areamap9,17,33 is just resolution (higher = better and slower).
dys129: That should probably be GL_CLAMP_TO_EDGE, not GL_CLAMP.
uniform sampler2D Tex0;
uniform sampler2D Tex1;
So, here you have 2 textures. Can you show with which portion of your code you assign each of these to your texture images? If you don't initialize your uniforms they will be initialized to 0 by default, so Tex1 should have the same content as Tex0.
Well, i mean, i use something like this in my engine to bind a texture image to a uniform sampler from a fragment shader:
uniform sampler2D Tex1;
So, here you have 2 textures. Can you show with which portion of your code you assign each of these to your texture images? If you don't initialize your uniforms they will be initialized to 0 by default, so Tex1 should have the same content as Tex0.
Well, i mean, i use something like this in my engine to bind a texture image to a uniform sampler from a fragment shader:
Code:
void shaderTextureSet::setTextures( shader *s )
{
glUseProgram( s->programID );
for (int i=0; i<int(textures.size()); i++)
{
glActiveTexture( GL_TEXTURE0+textures[i].textureUnit );
glBindTexture( textures[i].tex->target, textures[i].tex->textureId );
int location = glGetUniformLocation( s->programID, textures[i].name );
if (location >= 0)
{
glUniform1i( location, textures[i].textureUnit );
}
}
}
Can anybody explain me how I can convert an implicit surface formula to a form usable for raymarching?
Let's say I wanna raymarch the Ding-Dong surface:
[code]x^2+y^2=(1-z)z^2[code]
Let's say I wanna raymarch the Ding-Dong surface:
[code]x^2+y^2=(1-z)z^2[code]
to and /code... well.
Hello, thank you guys for awesome threads. Well that's my first experience in raymarching http://vimeo.com/23018524
Clickable(I hope so) link http://vimeo.com/23018524
nice, but it needs more contrast and a ground with shadow :)
a13X_B: better than most! :)
Yep, that's a cool start. Definitely needs more contrast though, maybe just crank up the AO?
@RareWtFailWhale: replace = with < or > ?
I thought so too. 0 schould be where the surface is. I tried it, but it didn't work somehow...
That is that the function should return something else than 0 for points inside/outside the object...
Hi,
Maybe:
Maybe:
Code:
evaldraw syntax :)dingdong(x,y,z){
r2=x*x+y*y;
r=r2-z^2*(1-z);
dr=sqrt(4*r2+((3*z-2)*z)^2);//modulus of the gradient
abs((r-param1)/(dr+param2))*0.75;//learned from IQ.
//param1 is to change the level set.
//param2 in order to remove singularity. Not always necessary.
//0.75 is for scaling the DE
}