Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 4 of 4

Thread: GLSL Raytracer Circular Artifacts

  1. #1
    Newbie Newbie
    Join Date
    Jan 2019
    Posts
    2

    GLSL Raytracer Circular Artifacts

    Click image for larger version. 

Name:	artifacts.jpg 
Views:	15 
Size:	42.0 KB 
ID:	2934 Click image for larger version. 

Name:	artifacts2.jpg 
Views:	12 
Size:	5.2 KB 
ID:	2935 Click image for larger version. 

Name:	artifacts2.jpg 
Views:	10 
Size:	5.5 KB 
ID:	2936
    I'm trying to write a raytracer using OpenGL fragment shader and having a problem with strange looking circular artifacts on boxes' surfaces. I suspect that the problems with rand() and/or scatter() can be responsible for this.
    Code :
    #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.

  2. #2
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,057
    Quote Originally Posted by Crimz8n View Post
    I suspect that the problems with rand() and/or scatter() can be responsible for this.
    Almost certainly rand(), and the way you're using it.

    Quote Originally Posted by Crimz8n View Post
    Code :
    float rand(in vec2 seed) {
    	return 2*fract(sin(dot(seed, vec2(12.9898,78.233)))*43758.5453) - 1;
    }
    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.

    Quote Originally Posted by Crimz8n View Post
    vec3 direction = hit.normal + vec3(rand(hit.position.xy), rand(hit.position.zy), rand(hit.position.xz));
    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.
    Last edited by GClements; 01-05-2019 at 05:34 PM.

  3. #3
    Newbie Newbie
    Join Date
    Jan 2019
    Posts
    2

    I'm just too dumb to get it working...

    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...

    My current code is:
    Code :
    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!

  4. #4
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    3,057
    Quote Originally Posted by Crimz8n View Post
    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...
    Code :
    float hash(float n) { return fract(sin(n) * 1e4); }
    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:
    Code :
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •