Шейдер работает в ShaderToy, а не в собственном приложении - PullRequest
0 голосов
/ 26 мая 2020

Я пишу трассировщик Sphere и обнаружил ошибку, когда мой шейдер превышает windows 'TDR и вылетает. Я подумал, что это может быть из-за моего интенсивного использования циклов, но меняю TdrDelay на 30 se c в реестре не помогло. Затем я попытался поместить тот же код в ShaderToy, и там он работает нормально (~ 7FPS) после длительного периода компиляции. Скопируйте, вставив этот точный код (и изменив основные методы и униформу по мере необходимости, чтобы они были совместимы с моим кодом) в моем приложении, компиляция занимает несколько секунд, а затем остается на 3FPS, пока TDR не закроет приложение. Chrome использует мой встроенный графический процессор (Intel UHD630), в то время как мое приложение использует мой выделенный NVidia 1650. При использовании моей карты Intel в моем приложении OpenGL просто прибегает к конвейеру с фиксированными функциями и ничего не делает с ограничением fps. Использование моей карты NVidia в shadertoy работает и отрисовывает быстрее ~ 25 кадров в секунду. Согласно glGetShaderInfo() шейдер не выдает ошибок компиляции. В моем приложении вершинный шейдер просто создает четырехугольник, как и shaderToy. Я пробовал все, что мог придумать, и ничего не изменил, и я не мог найти похожие проблемы в Google. Эта проблема кажется мне очень странной, поскольку Shadertoy использует более базовую c версию OpenGL (WebGL), чем мое приложение, и работает на гораздо более слабом графическом процессоре. По этим двум причинам я не думаю, что мое приложение зависает из-за ограничений вычислений. Любая помощь будет принята с благодарностью. Если потребуется дополнительная информация, я могу ее добавить.

Фрагментный шейдер:

#version 400 core

in vec2 uvPos;
in vec4 backColor;
out vec4 outColor;
uniform vec2 iResolution = vec2(1536, 864);
uniform int MAX_AA = 50;
uniform int MAX_MARCH = 100;
uniform float MAX_DIST = 20.0;

#define OBJ_COUNT 2
#define FLT_MAX 3.402823466e+38

uint hash(uint x){
    x+=(x<<10u);
    x^=(x>>6u);
    x+=(x<<3u);
    x^=(x>>11u);
    x+=(x<<15u);
    return x;
}
uint hash(uvec2 v){return hash(v.x^hash(v.y));}
uint hash(uvec3 v){return hash( v.x ^ hash(v.y) ^ hash(v.z));}
uint hash(uvec4 v){return hash( v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w));}
float floatConstruct(uint m){
    const uint ieeeMantissa = 0x007FFFFFu;
    const uint ieeeOne = 0x3F800000u;
    m &= ieeeMantissa;
    m |= ieeeOne;
    return uintBitsToFloat(m) - 1.0;
}
float randf(float x){return floatConstruct(hash(floatBitsToUint(x)));}
float randf(int x){return randf(float(x));}
float randf(vec2 v){return floatConstruct(hash(floatBitsToUint(v)));}
float randf(vec3 v){return floatConstruct(hash(floatBitsToUint(v)));}
float randf(vec4 v){return floatConstruct(hash(floatBitsToUint(v)));}
//random: range [0, 1]
int randIterator = 0;

vec3 randomOnBall(){
    vec3 p;
    do{
        p = vec3(randf(randIterator++)*2+1,
                 randf(randIterator++)*2+1,
                 randf(randIterator++)*2+1);
    }while(length(p) > 1.0);
    return p;
}


struct Material{
    int type;
    vec3 albedo;
};

struct Object{
    int type;
    vec3 c;
    float r;
    vec3 size;

    Material mat;
};

struct Scene{
    Object objects[OBJ_COUNT];
};

struct HitRecord{
    vec3 p;
    vec3 n;
    Object o;
    Material m;
};
struct Ray {
    vec3 o;
    vec3 d;
};

float sphereSDF(Object o, vec3 p){
    return length(p-o.c)-o.r;
}
float boxSDF(Object o, vec3 p){
    vec3 q = abs(p-o.c) - 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 < OBJ_COUNT; i++){
        switch(s.objects[i].type){
            case 1:
                dist = min(dist, sphereSDF(s.objects[i], p));
                break;
            case 2:
                dist = min(dist, boxSDF(s.objects[i], p));
                break;
        }
    }
    return dist;
}
float sceneSDF(vec3 p, Scene s, inout HitRecord rec){
    float dist = FLT_MAX;
    for(int i = 0; i < OBJ_COUNT; i++){
        float tmpDist = FLT_MAX;
        switch(s.objects[i].type){
            case 1:
                tmpDist = sphereSDF(s.objects[i], p);
                break;
            case 2:
                tmpDist = boxSDF(s.objects[i], p);
                break;
        }
        if(tmpDist < dist){
            dist = tmpDist;
            rec.o = s.objects[i];
            rec.m = s.objects[i].mat;
        }
    }
    return dist;
}

Ray getRay(vec2 uv){
    const vec3 lookFrom = vec3(0.0, 0.0, 2.0);
    const vec3 lookAt = vec3(0.0, 0.0, 0.0);
    const vec3 viewUp = vec3(0.0, 1.0, 0.0);
    const float verticalFOV = 90.0;
    float aspect = float(iResolution.x)/float(iResolution.y);
    vec3 u, v, w;
    float theta = radians(verticalFOV);
    float halfHeight = tan(theta/2.0);
    float halfWidth = aspect*halfHeight;
    vec3 origin = lookFrom;
    w = normalize(lookFrom-lookAt);
    u = normalize(cross(viewUp, w));
    v = cross(w, u);
    vec3 lowerLeftCorner = origin-u*halfWidth-v*halfHeight-w;
    vec3 hor = u*2.0*halfWidth;
    vec3 ver = v*2.0*halfHeight;
    return Ray(origin, normalize(lowerLeftCorner-origin+hor*uv.x+ver*uv.y));
}

vec3 calcNormal(vec3 p, Scene s){
    #if 0
    const float eps = 0.0001; // or some other value
    const vec2 h = vec2(eps,0);
    return normalize( vec3(sceneSDF(p+h.xyy, s) - sceneSDF(p-h.xyy, s),
                           sceneSDF(p+h.yxy, s) - sceneSDF(p-h.yxy, s),
                           sceneSDF(p+h.yyx, s) - sceneSDF(p-h.yyx, s) ) );
    #else //From Inigo Quilez  https://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm
    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 ) );
    #endif

}

float findSceneIntersect(Ray r, Scene s, inout HitRecord rec){
    float t = 0.005;
    vec3 p;
    for(int i = 0; i < 100; i++){
        p = r.o+r.d*t;
        float dist = sceneSDF(p, s, rec);
        if(dist < 0.001){
            rec.n = calcNormal(p, s);
            rec.p = p;
            return t;
        }else{
            t+=dist;
            if(t >= MAX_DIST)
                return -1.0;
        }
    }
    return -1.0;
}

bool scatter(inout Ray r, HitRecord rec, inout vec3 tmpAtt){
    tmpAtt = rec.m.albedo;
    r = Ray(rec.p, rec.n+randomOnBall());
    return true;
}

vec3 calcColor(Ray ray, Scene s){
    HitRecord rec = HitRecord(vec3(0.0), vec3(0.0), Object(0, vec3(0.0), 0.0, vec3(0.0), Material(1, vec3(0.0))), Material(1, vec3(0.0)));
    vec3 color = vec3(0);
    float t = findSceneIntersect(ray, s, rec);
    int reflections = 0;
    vec3 att = vec3(1.0);
    for(int ref = 0; ref < 50; ref++){
        if(t != -1.0){
            vec3 tmpAtt = vec3(0.0);
            if(scatter(ray, rec, tmpAtt)){
                att *= tmpAtt;
                t = findSceneIntersect(ray, s, rec);
                reflections++;
            }else{
                att *= tmpAtt;
                t = -1.0;
            }
        }else{
            color = mix(vec3(1.0), vec3(0.5, 0.7, 1.0), ray.d.y*0.5+0.5)*att;
            break;
        }
    }
    return color;
}

Scene scene = Scene(Object[OBJ_COUNT](
    Object(1, vec3(0.0, 0.5, 0.0), 1.0, vec3(0.0), Material(1, vec3(0.5))),
    Object(1, vec3(0.0, -100.5, 0.0), 100.0, vec3(0.0), Material(1, vec3(0.5)))
));

void main(void){
    HitRecord rec = HitRecord(vec3(0.0), vec3(0.0), Object(0, vec3(0.0), 0.0, vec3(0.0), Material(1, vec3(0.0))), Material(1, vec3(0.0)));


    #if 1
    const int AA = 50;
    vec3 color = vec3(0.0);
    for(int s = 0; s < AA; s++){
        vec2 xy = uvPos * iResolution;
        vec2 uv = (xy+vec2(randf(randIterator++)*2.0-1.0, randf(randIterator++)*2.0-1.0))/iResolution.xy;
        color += calcColor(getRay(uv), scene);
    }

    outColor = vec4(color/float(AA), 1.0);
    #else
    vec2 uv = uvPos;
    Ray ray = getRay(uv);
    float t = findSceneIntersect(ray, scene, rec);
    if(t == -1.0){
        outColor = vec4(mix(vec3(1.0), vec3(0.5, 0.7, 1.0), uv.y), 1.0);
    }else
        outColor = vec4(rec.n*0.5+0.5, 1.0);
    #endif

    outColor.xyz = pow(outColor.xyz, vec3(1.0/2.2));
}

Вершинный шейдер: (отображает четырехугольник)

#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);
}

Код загрузки шейдера (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);
        System.out.println(glGetShaderInfoLog(shaderID, 1000)); //Returns empty string --> no compile error
        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;
    }

...