Webgl raytracing ведет себя противоположно тому, как он предназначен для - PullRequest
0 голосов
/ 11 мая 2019

Так что это школьное задание. Я должен понять, как работает трассировка лучей И отладить эту программу, которую несколько лет назад сделал другой студент.

Трассировка лучей, кажется, работает правильно, но проблема заключается в количестве отскоков. Он предназначен для установки пользователем с помощью ползунка, чтобы он мог взаимодействовать и видеть эффекты отраженных лучей света.

Таким образом, отображаемое изображение кажется идеальным с одним отскоком, чего не должно быть. С нулевым отскоком ничего не отображается. Затем со значениями, такими как 2,3,5 или 10 или даже 1000, визуализированное изображение становится все хуже и хуже, а значение становится все выше и выше, что противоположно предполагаемому поведению. Ошибка очень странная, потому что ядро ​​трассировки лучей работает, но наоборот.

У меня есть файл диагностики в вики, в котором я перечислил то, что я тестировал до сих пор без какого-либо успеха.

https://github.com/ErwanBueche/webgl-pathtracing

Это мой фрагментный шейдер

 <!-- vertex shader -->
        <script id="shader-vs" type="x-shader/x-vertex">
            attribute vec3 aVertexPosition;
            uniform mat4 uPMatrix;

            void main(void)
            {
                gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);
            }
        </script>


            <!-- fragment shader -->
        <script id="shader-fs" type="x-shader/x-fragment">
            precision mediump float;
            const float pi = 3.14159265358;
            const float sphereRayon = 0.25;
            const float lightRayon = 0.01;

            uniform mat3 uRepMatrix;
            uniform float uAlpha;
            uniform mat4 uMVMatrix;
            uniform float uBounce;

            vec3 uDir;
            const vec3 uOri =  vec3(0.0,0.0,2.0);

            uniform vec3 uSpherePos1;
            uniform vec3 uSpherePos2;
            uniform vec3 uLightPos;

            const vec3 boxMin = vec3(-1.0, -1.0, -1.0);
            const vec3 boxMax = vec3(1.0, 1.0, 1.0);

            //Return the distance between origin and a sphere. return 10000.0 if there is no intersection
            float intersectSphere(vec3 dir, vec3 origin, vec3 spherePosition, float sphereRayon)
            {
                vec3 l = origin - spherePosition;
                float a = dot(dir, dir);
                float b = 2.0 * dot(l, dir);
                float c = dot(l, l) - sphereRayon*sphereRayon;
                float discriminant = b*b - 4.0*c*a;
                if(discriminant > 0.0)
                {
                    float t = (-b-sqrt(discriminant))/(2.0*a);
                    if(t > 0.0)
                    {
                        return t;
                    }
                }
                return 10000.0;
            }

            //retun the distance between origin and a cube
            float intersectCube(vec3 dir, vec3 origin, vec3 cubeMin, vec3 cubeMax)
            {
                vec3 tMin = (cubeMin - origin) / dir;
                vec3 tMax = (cubeMax - origin) / dir;
                vec3 t1 = min(tMin, tMax);
                vec3 t2 = max(tMin, tMax);
                float tNear = max(max(t1.x, t1.y), t1.z);
                float tFar = min(min(t2.x, t2.y), t2.z);
                return (tNear >= tFar) ? tNear : tFar;
            }

            //return the distance between a point and a sphere
            vec3 normalForSphere(vec3 hit, vec3 spherePosition, float sphereRayon)
            {
                return (hit - spherePosition) / sphereRayon;
            }

            //return the normal of a point on a cube
            vec3 normalForCube(vec3 hit, vec3 cubeMin, vec3 cubeMax)
            {
                vec3 normal = vec3(0.0, 0.0, 0.0);
                if(hit.x < cubeMin.x + 0.00001)
                {
                    normal = vec3(-1.0, 0.0, 0.0);
                }
                else if (hit.x > cubeMax.x - 0.00001)
                {
                    normal = vec3(1.0, 0.0, 0.0);
                }
                else if(hit.y < cubeMin.y + 0.00001)
                {
                    normal = vec3(0.0, -1.0, 0.0);
                }

                if (hit.y > cubeMax.y - 0.00001)
                {
                    normal = vec3(0.0, 1.0, 0.0);
                }
                else if(hit.z < cubeMin.z + 0.00001)
                {
                    normal = vec3(0.0, 0.0, -1.0);
                }
                else if (hit.z > cubeMax.z - 0.00001)
                {
                    normal = vec3(0.0, 0.0, 1.0);
                }

                return normal;
            }

            //retrun the shadow coefficient (0.1 if shadow, 1.0 if not shadow)
            float shadow(vec3 origin, vec3 dir)
            {
                float tSphere1 = intersectSphere(dir, origin, uSpherePos1, sphereRayon);
                float tSphere2 = intersectSphere(dir, origin, uSpherePos2, sphereRayon);
                float tLight = intersectSphere(dir, origin, uLightPos, lightRayon);

                if(tSphere1 < tLight)
                {
                    return 0.1;
                }

                if(tSphere2 < tLight)
                {
                    return 0.1;
                }
                else
                {
                    return 1.0;
                }
            }

            //return a random vector in a half sphere
            vec3 uniformSampleHemisphere(float r1,float r2)
            {
                float sinTheta = sqrt(1.0 - r1 * r1);
                float phi = pi * 2.0 * r2;
                float x = sinTheta * cos(phi);
                float z = sinTheta * sin(phi);
                return vec3(x, r1, z);
            }

            //return a random number from seed
            float random(vec3 scale, float seed)
            {
                return fract(sin(mod(dot(gl_FragCoord.xyz+seed, scale),pi)) * (43758.5453 + seed) );
            }

            //return the next direction of a ray of light in a scene in a diffuse way
            vec3 getNextDir(vec3 normal, float seed)
            {
                vec3 Nt, Nb;
                if(abs(normal.x) >  abs(normal.y))
                {
                    Nt = vec3(normal.z,0.0,-normal.x) / sqrt(normal.x * normal.x + normal.z * normal.z); ;
                }
                else
                {
                    Nt = vec3(0.0,-normal.z,normal.y) / sqrt(normal.z * normal.z + normal.y * normal.y ); ;
                }

                Nb = cross(normal, Nt);
                float r1 = random(vec3(13.9898, 78.233, 151.7182),seed);
                float r2 = random(vec3(63.7264, 10.873, 623.6736),seed);
                vec3 sampleRand = uniformSampleHemisphere(r1,r2);
                return vec3(sampleRand.x * Nb.x + sampleRand.y * normal.x + sampleRand.z * Nt.x, sampleRand.x * Nb.y + sampleRand.y * normal.y + sampleRand.z * Nt.y, sampleRand.x * Nb.z + sampleRand.y * normal.z + sampleRand.z * Nt.z);
            }


            //Main ray tracing loop
            vec3 raytrace(vec3 dir, vec3 origin, float index)
            {
                vec3 color = vec3(0.0);
                vec3 surfaceColor = vec3(0.8);
                float distanceFromTheLight = 0.0;

                //tests if the ray intersects with the light source
                float tlight = intersectSphere(dir, origin, uLightPos, lightRayon*5.0);
                if(tlight < 10000.0)
                {
                    //if intersect light source, pixel is white
                    return vec3(1.0);
                }

                //Start main loop
                for(float i=1.0; i < 1000.0; i+=1.0)
                {
                    //trick to test bounce number (we cant use a uniform variable in a loop)
                    if(i <= uBounce)
                    {
                        //Test all possible intersections with scene items
                        float tBox = intersectCube(dir, origin, boxMin, boxMax);
                        float tsphere1 = intersectSphere(dir, origin, uSpherePos1, sphereRayon);
                        float tsphere2 = intersectSphere(dir, origin, uSpherePos2, sphereRayon);

                        //test which intersections is the nearest
                        float t = 10000.0;
                        if(tBox < t)
                        {
                            t = tBox;
                        }
                        if(tsphere1 < t)
                        {
                            t = tsphere1;
                        }
                        if(tsphere2 < t)
                        {
                            t = tsphere2;
                        }

                        //coordinates of intersection
                        vec3 hit = origin + dir * t;

                        //Distance between lignt and point
                        vec3 lightToHit = normalize(uLightPos - hit);
                        if(i == 1.0)
                        {
                            distanceFromTheLight += length(uLightPos - hit);
                        }
                        else
                        {
                            distanceFromTheLight += length(origin - hit);
                        }


                        //normal at the intersection point
                        vec3 normal;
                        if (t == tBox)
                        {
                            //inversed, to have the inside of the cube

                            normal = -normalForCube(hit, boxMin, boxMax);
                            if(hit.x < boxMin.x + 0.00001)
                            {
                                //green
                                surfaceColor = vec3(0.2, 1.0, 0.2);
                            }
                            else if(hit.x > boxMax.x - 0.00001)
                            {
                                //red
                                surfaceColor = vec3(1.0, 0.1, 0.1);
                            }
                            else if(hit.z < boxMin.z + 0.00001)
                            {
                                //blue
                                surfaceColor = vec3(0.1, 0.5, 1.0);
                            }
                            else if(hit.z > boxMax.z - 0.00001)
                            {
                                //yellow
                                surfaceColor = vec3(1.0, 0.9, 0.1);
                            }

                        }

                        if(t == tsphere1)
                        {
                            normal = normalForSphere(hit, uSpherePos1, sphereRayon);
                            //white
                            surfaceColor = vec3(1.0);
                        }

                        if(t == tsphere2)
                        {
                            normal = normalForSphere(hit, uSpherePos2, sphereRayon);
                            //white
                            surfaceColor = vec3(1.0);
                        }

                        //We can process the color coefficients, brightness, diffusion and shadow intensity
                        float lightIntensity = 7.0 / (4.0 * pi * distanceFromTheLight);
                        float diffuse = max(0.0, dot(lightToHit, normal));
                        float shadowIntensity = shadow(hit + normal*0.00001, lightToHit);

                        //Accumulation of the color of the pixel
                        color += surfaceColor * diffuse * shadowIntensity * lightIntensity * 1.0/i;

                        //processing of the new direction and new point
                        dir = getNextDir(normal, i + index);
                        origin=hit;


                    }
                    else
                    {
                        break;
                    }
                }

                return color;
            }

            void main(void)
            {
                vec3 pixelScreen = uRepMatrix * vec3(gl_FragCoord.x,gl_FragCoord.y, 1.0);
                vec3 pixelCamera = pixelScreen * vec3(uAlpha, uAlpha, -1.0);
                uDir = normalize(pixelCamera);
                vec3 dir =  vec3(vec4(uDir,0)*uMVMatrix);
                vec3 ori =  vec3(vec4(uOri,0)*uMVMatrix);

                //Multi sampling
                vec3 ray = raytrace(dir,ori,0.0);
                vec3 ray11 = raytrace(vec3(dir.x+0.001,dir.y+0.001,dir.z),ori,2.0);
                vec3 ray10 = raytrace(vec3(dir.x+0.001,dir.y-0.001,dir.z),ori,3.0);
                vec3 ray01 = raytrace(vec3(dir.x-0.001,dir.y+0.001,dir.z),ori,4.0);
                vec3 ray00 = raytrace(vec3(dir.x-0.001,dir.y-0.001,dir.z),ori,5.0);
                vec3 color = sqrt((ray11*ray11 + ray10*ray10 + ray01*ray01 + ray00*ray00 + ray*ray)/5.0);
                gl_FragColor = vec4(color, 1.0);
            }
        </script>

Так что, если кто-то из вас, ребята, столкнулся с подобными проблемами, я был бы признателен за любую помощь в этой точке. Я знаю, что код не идеален, он не мой, я просто хочу понять и исправить его, чтобы значение, заданное пользователем, заставляло рендеринг работать правильно, и чем больше отказов, тем лучше изображение. Спасибо за вашу помощь!

...