0

I am writing a basic Sphere-Tracer in a fragment shader, everything is doing fine if I just color points according to their surface normals, but as soon as I try to implement the reflecting algorithm, it takes a bit longer to compile, and just blackscreens. The fps starts out at 1 but quickly goes up to 1000, which is the code limit I put, telling me the shader is actually not doing anything and just ignores my code, which should be much slower than that. I initially thought I was hitting the instruction limit of my GPU, but I don't think that's the problem, as using the normal-shading code, I can set MAX_MARCH to high values, like 1000, but using reflections, even with MAX_REFLECTIONS and MAX_AA to one, which should be similar to the amount of instructions of normal-shading(maybe ~50 more, not significant I don't think). But I need to set MAX_MARCH to 1 for it to render, even setting it to two causes the bug.

Vertex shader:

//Draw a quad on the whole display and calculates sky color
#version 400 core

in vec3 position;
out vec2 uvPos;
out vec4 backColor;

void main(void){
    gl_Position = vec4(position, 1.0);
    uvPos = position.xy*0.5+0.5;
    backColor = mix(vec4(1, 1, 1, 1), vec4(0.5, 0.7, 1, 1), uvPos.y);
}

Fragment shader:

#version 400 core

#define FLT_MAX 3.402823466e+38
#define FLT_MIN 1.175494351e-38
#define DBL_MAX 1.7976931348623158e+308
#define DBL_MIN 2.2250738585072014e-308
#define PI 3.141592653589793115997963468544185161590576171875

#define MAX_AA 1
#define MAX_MARCH 1000
#define MAX_REFLECTIONS 10
#define MAX_DIST 10

in vec2 uvPos;
in vec4 backColor;
out vec4 outColor;

int randomIterator = 0;
//############################################################  Structure definitions   #########################################################################
struct Material{
    int type;
    vec3 albedo;
};

struct Object{
    int type; //1:Sphere, 2:Box
    vec3 center;
    float radius;
    vec3 size;

    Material material;
};

struct Scene{
    Object objects[3];
};

struct Ray{
    vec3 origin;
    vec3 dir;
};

struct HitRecord{
    vec3 p;
    vec3 n;
    Object o;
    Material mat;
    float closest;
};
struct Camera{
    vec3 origin;
    vec3 lowerLeftCorner;
    vec3 horizontal;
    vec3 vertical;
};
//############################################################  Uniforms    ####################################################################################
uniform float random[2048];
uniform vec2 resolution;
uniform Camera cam;
uniform Scene scene;
uniform int objectAmount;
//############################################################  Tools   
float randf(){
    return random[randomIterator++];
}
Ray getRay(Camera cam, vec2 v){
    return Ray(cam.origin, normalize(cam.lowerLeftCorner+cam.horizontal*v.s+cam.vertical*v.t-cam.origin));
}
vec3 randOnBall(){
    vec3 p;
    do{
        p = vec3(randf(), randf(), randf())*2-1;
    }while(p.length() >= 1);
    return p;
}
//############################################################  Signed Distance Functions   
float sphereSDF(vec3 p, Object o){
    return length(p-o.center)-o.radius;
}
float boxSDF(vec3 p, Object o){
    vec3 q = abs(p-o.center) - o.size;
    return (length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0));
}
float sceneSDF(vec3 p, Scene s){
    float dist = FLT_MAX;
    for(int i = 0; i < objectAmount; i++){
        switch(s.objects[i].type){
            case 1:
                dist = min(dist, sphereSDF(p, s.objects[i]));
                break;
            case 2:
                dist = min(dist, boxSDF(p, s.objects[i]));
                break;
            default:
                break;
        }
    }
    return dist;
}
float sceneSDF(vec3 p, Scene s, inout HitRecord rec){
    float dist = FLT_MAX;
    for(int i = 0; i < objectAmount; i++){
        float tmpDist=FLT_MAX; 
        switch(s.objects[i].type){
            case 1:
                tmpDist = sphereSDF(p, s.objects[i]);
                break;
            case 2:
                tmpDist = boxSDF(p, s.objects[i]);
                break;
            default:
                break;
        }
        if(tmpDist<dist){
            dist = tmpDist;
            rec.o = s.objects[i];
            rec.mat = s.objects[i].material;
        }
    }
    return dist;
}
//############################################################  Material Scatter Function   
bool scatterDiffuse(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
    tmpAtt = vec3(rec.mat.albedo);
    scattered = Ray(rec.p, rec.n+randOnBall());
    return true;
}

bool scatter(Ray r, HitRecord rec, inout vec3 tmpAtt, inout Ray scattered){
    return scatterDiffuse(r, rec, tmpAtt, scattered);  //Starting out with diffuse materials, planned to 
    add switch-case for different materials
}
//############################################################  Main    
vec3 findSceneNormal(Scene s, vec3 p){
    const float h = 0.0001; // replace by an appropriate value
    const vec2 k = vec2(1,-1);
    return normalize( k.xyy*sceneSDF( p + k.xyy*h, s ) + 
                      k.yyx*sceneSDF( p + k.yyx*h, s ) + 
                      k.yxy*sceneSDF( p + k.yxy*h, s ) + 
                      k.xxx*sceneSDF( p + k.xxx*h, s ) );
}

float findSceneIntersect(Ray r, Scene scene, inout HitRecord rec){
    float t = 0.005;
    vec3 p;
    for(int i = 0; i < MAX_MARCH; i++){
        p = r.origin+t*r.dir;
        float dist = abs(sceneSDF(p, scene, rec));
        if(dist < 0.001){
            rec.n = findSceneNormal(scene, p);
            rec.p = p;
            return t;
        }else{
            t += dist;
            if(t >= MAX_DIST){
                rec.p = r.origin+t*r.dir;
                rec.n = vec3(0, 0, 0);
                return -1;
            }
        }
    }
    return -1;
}
vec3 calcColor(Ray r){
    vec3 color;
    Material emptyMat = Material(0, vec3(0));
    Object emptyO = Object(0, vec3(0), 0, vec3(0), emptyMat);
    HitRecord rec = HitRecord(vec3(0), vec3(0), emptyO, emptyMat, 0);
    float t = findSceneIntersect(r, scene, rec);
    int reflections = 0;
    vec3 att = vec3(1, 1, 1);
    for(int ref = 0; ref < MAX_REFLECTIONS; ref++){
        if(t != -1){
            vec3 tmpAtt = vec3(0);
            if(scatter(r, rec, tmpAtt, r)){
                att *= tmpAtt;
                t = findSceneIntersect(r, scene, rec);
                reflections++;
            }else {
                att *= tmpAtt;
                t = -1;
            }
        }else {
            color = backColor.xyz*att;
            break;
        }
    }
    return color;
}

void main(void){
    HitRecord rec = HitRecord(vec3(0), vec3(0), Object(-1, vec3(0), 0, vec3(0), Material(-1, vec3(0))), Material(-1, vec3(1, 1, 1)), 0);
    #if 1  //Reflection rendering
        vec3 color = vec3(0);
        for(int s = 0; s < MAX_AA; s++){
            vec2 uv = uvPos+(vec2(randf(), randf())*2-1)/resolution;
            color += calcColor(getRay(cam, uv));
        }
        outColor = vec4(color/MAX_AA, 1);

    #else  //Coloring based on normals
        Ray r = getRay(cam, uvPos);
        float t = findSceneIntersect(r, scene, rec);
        if(t == -1){
            outColor = backColor;
        }else {
            outColor = vec4(rec.n*0.5+0.5, 1);
        }
    #endif
}

Java code where I load and compile the shader (Using LWJGL):

    private static int loadShader(String file, int type) {
        System.out.println("Loading shader at path: " + file);
        StringBuilder shaderSource = new StringBuilder();
        try{
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            while((line = reader.readLine())!=null){
                shaderSource.append(line).append("//\n");
            }
            reader.close();
        }catch(IOException e){
            e.printStackTrace();
            System.exit(-1);
        }
        int shaderID = glCreateShader(type);
        glShaderSource(shaderID, shaderSource);
        glCompileShader(shaderID);
        if(glGetShaderi(shaderID, GL_COMPILE_STATUS )== GL_FALSE){
            System.out.println(glGetShaderInfoLog(shaderID, 500));
            System.err.println("Could not compile shader!");
            System.exit(-1);
        }
        return shaderID;
    }

The function loadShader in my code does not give me any error as it does with syntax errors and doesn't exit the program, thus GL_COMPILE_STATUS is not false.

I am fully aware nested for loops and my use of conditionals is far from efficient performance-wise, but I would expect it to be slow, not completely broken. I am running this on an Intel UHD 630, which according to https://www.intel.ca/content/www/ca/en/support/products/98909/graphics/graphics-for-7th-generation-intel-processors/intel-hd-graphics-630.html supports openGL 4.4. Therefore, according to GLSL maximum number of instructions, I should have access to 65536 instructions in my frag shader and fully dynamic branching. For these reasons, I don't think instruction limit is the problem and any help would be greatly appreciated. If you need any more information, I'll add it as soon as possible. If the CPU code is necessary, I can add it too, but I don't think it's the issue, as changing only the shader can trigger this bug.

Edit 1: Both glGetShaderInfoLog and glGetProgramInfoLog return nothing when called after the program has been validated and linked.

NatMath
  • 78
  • 6
  • what are the GLSL compilation and link logs saying? if you got a bug in shader code fixed function will kick in (if not in core GL context and if you do not have the [same/compatible input attribute locations/meaning](https://stackoverflow.com/a/20574219/2521214) as your gfx driver nothing renders) ... see `glGetShaderInfoLog,glGetProgramInfoLog` in [complete GL+GLSL+VAO/VBO C++ example](https://stackoverflow.com/a/31913542/2521214). If you want some inspiration see [raytrace through 3D mesh](https://stackoverflow.com/a/45140313/2521214) – Spektre May 22 '20 at 07:49
  • Thank you, @Spektre, but neither of these commands return anything when used after the program is linked and validated. I guess that's where you're supposed to use them. Also, the shader seems to compile OK as `glGetShaderi(shaderID, GL_COMPILE_STATUS )` is GL_TRUE – NatMath May 22 '20 at 12:06
  • Also thank you for the source @Spektre, but My algorithm should work as it's basically just a translation of CPU code that works. I had already found a similar method to what is described in that post. – NatMath May 22 '20 at 12:11
  • shaders do not throw errors ... the functions `glGetShaderInfoLog,glGetProgramInfoLog` retrieve the logs from the GLSL compiler and linker in runtime returning strings (fed into buffer passed to it not as return value). Even if compile status is true the logs usually point out warnings and stuff (including line number where problem is)... which might hint what is the problem. empty string means all ok . To check if you retreive it correctly just make a syntax error in the shader (and do not forget to save it)... Also check [this](https://stackoverflow.com/a/19659648/2521214) out – Spektre May 22 '20 at 12:15
  • In my shader loading code, I already have this condition: if(glGetShaderi(shaderID, GL_COMPILE_STATUS )== GL_FALSE){ System.out.println(glGetShaderInfoLog(shaderID, 500)); System.err.println("Could not compile shader!"); System.exit(-1); } and it detects syntax errors, but does not throw anything in this case @Spektre – NatMath May 22 '20 at 12:33
  • I'll add my shader loading code to the question – NatMath May 22 '20 at 12:40
  • Well if compilation is OK then its most likely some bug in the code ... This [GLSL debug prints](https://stackoverflow.com/a/44797902/2521214) might help you debug and also debug mode render/overlay for the ray tracer would be a good idea (something like I did in the `raytrace through 3D mesh` link above) – Spektre May 23 '20 at 06:26
  • Thank you very much @Spektre, I'll check these links asap. I'all also try to implement my algorithm in ShaderToy, as it might be easier to debug there. I've also seen raymarchers there, so I'll take inspiration. I'll do a bit of work on my own. Can I ask you more questions here if I need more help or I am better starting a new question? – NatMath May 23 '20 at 12:31
  • depends on the question if its related to this then ask here, if not create new one ... – Spektre May 23 '20 at 12:34
  • I've played a bit in ShaderToy, and re-wrote my algorithm and it works ok.https://www.shadertoy.com/view/3dBBzV but when I copy paste the code in eclipse (transforming it a bit to have the uniforms and the correct main method, nothing major), I get a similar bug where it compiles really slowly, then gives me about a sec of black and then crashes. Do you have any idea f what would cause this @Spektre? – NatMath May 25 '20 at 15:59
  • No idea ... try to check if you passing correct data to the shaders, Also I would rem out the whole shader code and incrementaly unreming it to see which part of code breaks the functionality. Too computationaly expensive loops in fragment might be a problem. The might "timeout" the shader (IIRC it happened to me once with similar results: slow compilation and crash in GL driver later). Also check if the loaded shader source code is in ASCII and not translated to UNICODE. Btw are you sure `uniform float random[2048];` fits into GPU shader? – Spektre May 25 '20 at 18:31
  • Thank you very much, I'll look at all of these, hope I can figure it out – NatMath May 25 '20 at 21:48

0 Answers0