GLSL Raytracer Circular Artifacts

[ATTACH=CONFIG]1922[/ATTACH] [ATTACH=CONFIG]1923[/ATTACH] [ATTACH=CONFIG]1924[/ATTACH]
I’m trying to write a raytracer using OpenGL fragment shader and having a problem with strange looking circular artifacts on boxes’ surfaces. :doh: I suspect that the problems with rand() and/or scatter() can be responsible for this.


#version 330 core
//#extension GL_ARB_gpu_shader_fp64 : require
//#define float double
//#define vec3 dvec3
//precision highp float;

in vec2 vTexcoord;

out vec4 FragColor;

uniform float uTime;
uniform mat4 uViewMatrix;
uniform ivec2 uResolution;

//////////////////////////////////////////////////

#define MAX_RAY_LENGTH 1000.0
#define FOV 70.0

struct Ray {
	vec3 origin;
	vec3 direction;
};
struct Material {
	vec4 albedo;
};
struct Hit {
	Material material;
	vec3 position;
	vec3 normal;
};
struct Box {
	Material material;
	vec3 min;
	vec3 max;
};

Material red_lambert_material = Material(vec4(0.9, 0.2, 0.1, 1.0));
Material green_lambert_material = Material(vec4(0.2, 0.8, 0.1, 1.0));
Material blue_lambert_material = Material(vec4(0.1, 0.2, 0.8, 1.0));
Material purple_lambert_material = Material(vec4(0.5, 0.2, 0.7, 1.0));

Box BOXES[] = {
	Box(red_lambert_material, vec3(-5.0, -1.0, -5.0), vec3(5.0, 0.0, 5.0)),
	Box(green_lambert_material, vec3(-0.5, 0.0, -0.5), vec3(0.5, 1.0, 0.5)),
	Box(blue_lambert_material, vec3(0.5, 0.0, 0.5), vec3(1.5, 1.0, 1.5)),
	Box(purple_lambert_material, vec3(-0.25, 0.5, 2.0), vec3(0.25, 1.0, 2.5))
};

float rand(in vec2 seed) {
	return 2*fract(sin(dot(seed, vec2(12.9898,78.233)))*43758.5453) - 1;
}

bool scatter(in Hit hit, out Ray ray, out vec3 attenuation) {
	attenuation = hit.material.albedo.rgb;
	vec3 direction = hit.normal + vec3(rand(hit.position.xy), rand(hit.position.zy), rand(hit.position.xz));
	ray = Ray(hit.position, direction);
	return true;
}

vec2 intersect_box(in Ray ray, in Box box) {
	vec3 t_min = (box.min - ray.origin) / ray.direction;
	vec3 t_max = (box.max - ray.origin) / ray.direction;
	vec3 t1 = min(t_min, t_max);
	vec3 t2 = max(t_min, t_max);
	float t_near = max(max(t1.x, t1.y), t1.z);
	float t_far = min(min(t2.x, t2.y), t2.z);
	return vec2(t_near, t_far);
}

bool intersect_world(in Ray ray, out Hit hit) {
	float smallest = MAX_RAY_LENGTH;
	bool found = false;
	for(int i=0; i<BOXES.length(); i++) {
		vec2 lambda = intersect_box(ray, BOXES[i]);
		if(lambda.x > 0.0 && lambda.x < lambda.y && lambda.x < smallest) {
			hit.material = BOXES[i].material;
			hit.position = ray.origin + ray.direction*(lambda.x);
			hit.normal = ivec3((hit.position-(BOXES[i].min+BOXES[i].max)/2.0) / (abs(BOXES[i].min-BOXES[i].max)/2.0));
			smallest = lambda.x;
			found = true;
		}
	}
	return found;
}

vec3 trace(in Ray ray) {
	Hit hit;
	vec3 color = vec3(0, 0, 0);
	vec3 total_attenuation = vec3(1.0, 1.0, 1.0);

	for(int bounce=0; bounce<4; bounce++) {
		if(intersect_world(ray, hit)) {
			vec3 attenuation;
			if(scatter(hit, ray, attenuation)) {
				total_attenuation *= attenuation;
			} else {
				total_attenuation *= vec3(0,0,0);
			}
		} else {
			vec3 unit_dir = normalize(ray.direction);
			float t = 0.5 * (unit_dir.y + 1.0);
			color = total_attenuation * ((1.0-t)*vec3(1.0,1.0,1.0)+t*vec3(0.5,0.7,1.0));
			break;
		}
	}
	return color;
}

Ray get_ray(in vec2 texcoord) {
	float h = tan(radians((float(uResolution.x)/float(uResolution.y))*FOV)/2.0);
	float v = tan(radians(FOV)/2.0);
	
	vec3 origin = (uViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
	vec3 dir = normalize(vec3(mix(-h, h, texcoord.x), mix(-v, v, texcoord.y), -1.0));
	dir = (uViewMatrix * vec4(dir, 0.0)).xyz;
	
	return Ray(origin, dir);
}

void main() {
	BOXES[2].min.y = 1.0+sin(radians(uTime/11.11))+1.0;
	BOXES[2].max.y = 2.0+sin(radians(uTime/11.11))+1.0;
	
	BOXES[3].min.x = -0.25+3.0*sin(radians(uTime/11.11));
	BOXES[3].max.x = 0.25+3.0*sin(radians(uTime/11.11));
	BOXES[3].min.z = -0.25+3.0*cos(radians(uTime/11.11));
	BOXES[3].max.z = 0.25+3.0*cos(radians(uTime/11.11));
	
	vec3 color = vec3(0.0);
	int samples = 16;
	for(int sample=0; sample < samples; sample++) {
		Ray ray = get_ray(vTexcoord+vec2(rand(vec2(color.r, sample))-0.5)/vec2(uResolution));
		color += trace(ray);
	}
	color /= samples;
	
    FragColor = vec4(color, 1.0);
}

I’ve tried to fix this by using GL_ARB_gpu_shader_fp64 extension, but even when every float is replaced with double, there is always a small circular artifact on every surface. :tired:

Almost certainly rand(), and the way you’re using it.

This looks like something pulled out of thin air, and isn’t likely to make a good PRNG.

If you’re relying upon GLSL 3.30, I’d suggest using floatBitsToUint() and an integer hash.

If hit.position lies on an axis-aligned plane (e.g. a face of an axis-aligned cube), one of those coordinates will be a constant.

I get it, what are you talking about, but these PRNGs gave me way too painful headache…
I’ve tried lots of different combinations, and I just can’t get it working… :frowning:

My current code is:

float hash(float n) { return fract(sin(n) * 1e4); }

float t = hash(floatBitsToUint(uTime));
float pixelUUID = hash(hash(floatBitsToUint(vTexcoord.x+uTime/1000.0))+hash(floatBitsToUint(vTexcoord.y+t/1000.0)));
float pu = hash(floatBitsToUint(pixelUUID));
float RAND_LAST = hash(hash(floatBitsToUint(vTexcoord.x+pu))+hash(floatBitsToUint(vTexcoord.y+pixelUUID)));
float rand(vec2 seed) {
	RAND_LAST = hash(hash(floatBitsToUint(seed.x+RAND_LAST))+hash(floatBitsToUint(seed.y+RAND_LAST)));
	return RAND_LAST;
}

Help me. Please… Give me some code!

[QUOTE=Crimz8n;1293404]I get it, what are you talking about, but these PRNGs gave me way too painful headache…
I’ve tried lots of different combinations, and I just can’t get it working… :frowning:

float hash(float n) { return fract(sin(n) * 1e4); }

[/QUOTE]
This really isn’t a very good hash. Analytic functions tend not to disrupt patterns very well; modular arithmetic tends to be more effective. As a first attempt, I’d try something like:


float rand(float f1, float f2, float f3) {
    uint u1 = floatBitsToUint(f1);
    uint u2 = floatBitsToUint(f2);
    uint u3 = floatBitsToUint(f3);
    uint u = u1*u1*C1 + u2*u2*C2 + u3*u3*C3;
    return (u & 0x7fffff80) / 2147483520.0;
}

where C1,C2,C3 are large primes (or at least, large odd numbers which share no common factors) chosen experimentally. The function can easily be extended to handle more parameters.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.