Зернистая сфера в моем трассировщике лучей

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

 create an empty image (to fill each pixel, via ray tracing)
 for each pixel [for each row, each column]
 create the equation of the ray emanating from our pixel
 trace() ray:
 if ray intersects SPHERE
 compute local shading (including shadow determination)
 return color;

Теперь данные сцены такие: серая сфера радиуса 1 устанавливается в (0,0,-3). Он устанавливает источник белого света в начале координат.

 2
 amb: 0.3 0.3 0.3
 sphere
 pos: 0.0 0.0 -3.0
 rad: 1
 dif: 0.3 0.3 0.3
 spe: 0.5 0.5 0.5
 shi: 1
 light
 pos: 0 0 0
 col: 1 1 1

Мой выглядит очень странно:

введите здесь описание изображения

//check ray intersection with the sphere
boolean intersectsWithSphere(struct point rayPosition, struct point rayDirection,    Sphere sp,float* t){

//float a = (rayDirection.x * rayDirection.x) + (rayDirection.y * rayDirection.y) +(rayDirection.z * rayDirection.z);
// value for a is 1 since rayDirection vector is normalized
double radius = sp.radius;
double xc = sp.position[0];
double yc =sp.position[1];
double zc =sp.position[2];

double xo = rayPosition.x;
double yo = rayPosition.y;
double zo = rayPosition.z;

double xd = rayDirection.x;
double yd = rayDirection.y;
double zd = rayDirection.z;

double b = 2 * ((xd*(xo-xc))+(yd*(yo-yc))+(zd*(zo-zc)));
double c = (xo-xc)*(xo-xc) + (yo-yc)*(yo-yc) + (zo-zc)*(zo-zc) - (radius * radius);
float D = b*b + (-4.0f)*c;

//ray does not intersect the sphere
if(D < 0 ){
    return false;
}

D = sqrt(D);
float t0 = (-b - D)/2 ;
float t1 = (-b + D)/2;

//printf("D=%f",D);
//printf(" t0=%f",t0);
//printf(" t1=%f\n",t1);

if((t0 > 0) && (t1 > 0)){
    *t = min(t0,t1);
    return true;
}
else {
    *t = 0;
    return false;
}

}

Ниже приведена функция trace():

unsigned char* trace(struct point rayPosition, struct point rayDirection, Sphere * totalspheres) {

struct point tempRayPosition = rayPosition;
struct point tempRayDirection = rayDirection;
float  f=0;
float tnear = INFINITY;
boolean sphereIntersectionFound = false;
int sphereIndex = -1;
for(int i=0; i < num_spheres ; i++){
    float t = INFINITY;
    if(intersectsWithSphere(tempRayPosition,tempRayDirection,totalspheres[i],&t)){
        if(t < tnear){
            tnear = t;
            sphereIntersectionFound = true;
            sphereIndex = i;
        }
    }
}

if(sphereIndex < 0){
    //printf("No interesection found\n");
    mycolor[0] = 1;
    mycolor[1] = 1;
    mycolor[2] = 1;
    return mycolor;
}
else {
       Sphere sp = totalspheres[sphereIndex];
        //intersection point
        hitPoint[0].x = tempRayPosition.x + tempRayDirection.x * tnear;
        hitPoint[0].y = tempRayPosition.y + tempRayDirection.y * tnear;
        hitPoint[0].z = tempRayPosition.z + tempRayDirection.z * tnear;

        //normal at the intersection point
        normalAtHitPoint[0].x = (hitPoint[0].x - totalspheres[sphereIndex].position[0])/ totalspheres[sphereIndex].radius;
        normalAtHitPoint[0].y = (hitPoint[0].y - totalspheres[sphereIndex].position[1])/ totalspheres[sphereIndex].radius;
        normalAtHitPoint[0].z = (hitPoint[0].z - totalspheres[sphereIndex].position[2])/ totalspheres[sphereIndex].radius;
        normalizedNormalAtHitPoint[0] = normalize(normalAtHitPoint[0]);

        for(int j=0; j < num_lights ; j++) {

            for(int k=0; k < num_spheres ; k++){

                shadowRay[0].x = lights[j].position[0] - hitPoint[0].x;
                shadowRay[0].y = lights[j].position[1] - hitPoint[0].y;
                shadowRay[0].z = lights[j].position[2] - hitPoint[0].z;
                normalizedShadowRay[0]  = normalize(shadowRay[0]);

                //R = 2 * ( N dot L) * N - L
                reflectionRay[0].x = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].x +normalizedShadowRay[0].x;
                reflectionRay[0].y = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].y +normalizedShadowRay[0].y;
                reflectionRay[0].z = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].z +normalizedShadowRay[0].z;
                normalizeReflectionRay[0] = normalize(reflectionRay[0]);

                        struct point temp;
                        temp.x = hitPoint[0].x + (shadowRay[0].x * 0.0001 );
                        temp.y = hitPoint[0].y + (shadowRay[0].y * 0.0001);
                        temp.z = hitPoint[0].z + (shadowRay[0].z * 0.0001);

                        struct point ntemp = normalize(temp);
                        float f=0;
                        struct point tempHitPoint;
                        tempHitPoint.x = hitPoint[0].x + 0.001;
                        tempHitPoint.y = hitPoint[0].y + 0.001;
                        tempHitPoint.z = hitPoint[0].z + 0.001;

                        if(intersectsWithSphere(hitPoint[0],ntemp,totalspheres[k],&f)){
                        //  if(intersectsWithSphere(tempHitPoint,ntemp,totalspheres[k],&f)){
                            printf("In shadow\n");
                            float r = lights[j].color[0];                       
                            float g = lights[j].color[1];
                            float b = lights[j].color[2];
                            mycolor[0] = ambient_light[0] + r;
                            mycolor[1] = ambient_light[1] + g;
                            mycolor[2] = ambient_light[2] + b;
                            return mycolor;

                    } else {

                        // point is not is shadow , use Phong shading to determine the color of the point.      
                        //I = lightColor * (kd * (L dot N) + ks * (R dot V) ^ sh)       
                        //(for each color channel separately; note that if L dot N < 0, you should clamp L dot N to zero; same for R dot V)

                       float x = dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]);
                       if(x < 0)
                           x = 0;

                       V[0].x = - rayDirection.x;
                       V[0].x = - rayDirection.y;
                       V[0].x = - rayDirection.z;
                       normalizedV[0] = normalize(V[0]);
                       float y = dot(normalizeReflectionRay[0],normalizedV[0]);
                       if(y < 0)
                           y = 0;
                       float ar = totalspheres[sphereIndex].color_diffuse[0] * x;
                       float br =  totalspheres[sphereIndex].color_specular[0] * pow(y,totalspheres[sphereIndex].shininess);
                       float r = lights[j].color[0] * (ar+br);
                    //----------------------------------------------------------------------------------
                       float bg =  totalspheres[sphereIndex].color_specular[1] * pow(y,totalspheres[sphereIndex].shininess);
                       float ag = totalspheres[sphereIndex].color_diffuse[1] * x;
                       float g = lights[j].color[1] * (ag+bg);
                    //----------------------------------------------------------------------------------
                       float bb =  totalspheres[sphereIndex].color_specular[2] * pow(y,totalspheres[sphereIndex].shininess);
                       float ab = totalspheres[sphereIndex].color_diffuse[2] * x;
                       float b = lights[j].color[2] * (ab+bb);
                        mycolor[0] =  r + ambient_light[0];
                        mycolor[1] =  g + ambient_light[1];
                        mycolor[2] =  b+ ambient_light[2];
                        return mycolor;
                        } 
                }
        }
    }           
}

Код, вызывающий trace(), выглядит так:

void draw_scene()
{
//Aspect Ratio
double a = WIDTH / HEIGHT;
double angel = tan(M_PI * 0.5 * fov/ 180);
ray[0].x = 0.0;
ray[0].y = 0.0;
ray[0].z = 0.0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
unsigned int x,y;
float sx, sy;
for(x=0;x < WIDTH;x++)
{
    glPointSize(2.0); 
    glBegin(GL_POINTS);
    for(y=0;y < HEIGHT;y++)
    {
        sx = (((x + 0.5) / WIDTH) * 2.0 ) - 1;
        sy = (((y + 0.5) / HEIGHT) * 2.0 ) - 1;;
        sx  = sx * angel * a;
        sy = sy * angel;
        //set ray direction
        ray[1].x = sx;
        ray[1].y = sy;
        ray[1].z = -1;
        normalizedRayDirection[0] = normalize(ray[1]);
        unsigned char* color = trace(ray[0],normalizedRayDirection[0],spheres);
        unsigned char  x1 = color[0] * 255;
        unsigned char  y1 = color[1] * 255;
        unsigned char  z1 = color[2] * 255;
        plot_pixel(x,y,x1 %256,y1%256,z1%256);
    }
   glEnd();
   glFlush();
 }
}

Может быть много-много проблем с кодом/пониманием.


person Margi    schedule 03.12.2013    source источник
comment
Попробуйте просто печатать черным цветом, если есть хит, и белым, если его нет. Таким образом, мы можем видеть результаты и исходить оттуда.   -  person Guido    schedule 03.12.2013
comment
Кроме того, не используйте примитив GL_POINTS в попытке покрыть квадраты пикселей 2x2. Реализации должны поддерживать только минимальный и максимальный размер точек 1,0, а GL_POINTS не всегда являются квадратными в зависимости от определенных состояний рендеринга. Вы можете легко рисовать квадраты 2x2 с помощью glDrawPixels (...), что приемлемо, поскольку вы все равно используете устаревшие вещи, такие как немедленный режим.   -  person Andon M. Coleman    schedule 03.12.2013
comment
@Guido: Не повезло, я сделал то, что ты предложил. Подсветка из-за блеска убрана, остальное все то же самое   -  person Margi    schedule 03.12.2013
comment
@Margi Тогда это означает, что в функции intersectsWithSphere что-то не так! Теперь я посмотрю на это более внимательно.   -  person Guido    schedule 03.12.2013


Ответы (3)


У меня не было времени, чтобы понять весь ваш код, и я определенно не эксперт по графике, но я полагаю, что ваша проблема называется «поверхностными прыщами». В данном случае это, вероятно, происходит потому, что ваши теневые лучи пересекаются с самим объектом. Что я сделал в своем коде, чтобы исправить это, так это добавил epsilon * hitPoint.normal к источнику теневого луча. Это эффективно отдаляет луч от вашего объекта, поэтому они не пересекаются.

Значение, которое я использую для эпсилон, — это квадратный корень из 1.19209290 * 10^-7, поскольку это квадратный корень из константы с именем EPSILON, которая определена в конкретном языке, который я использую.

person gsingh2011    schedule 11.07.2015

Какая возможная причина у вас есть для этого (в нетеневой ветке trace (...)):

V[0].x = - rayDirection.x;
V[0].x = - rayDirection.y;
V[0].x = - rayDirection.z;

С таким же успехом вы можете закомментировать первые два вычисления, поскольку вы записываете результаты каждого из них в один и тот же компонент. Я думаю, вы, вероятно, хотели сделать это вместо этого:

V[0].x = - rayDirection.x;
V[0].y = - rayDirection.y;
V[0].z = - rayDirection.z;

Тем не менее, вам также следует избегать использования примитивов GL_POINT для покрытия четырехугольника 2x2 пикселя. Не гарантируется, что точечные примитивы будут квадратными, а реализации OpenGL не обязаны поддерживать размер, отличный от 1.0. На практике большинство поддерживает 1.0 - ~64.0, но glDrawPixels (...) — гораздо лучший способ записи 2x2 пикселей, поскольку он пропускает примитивную сборку и вышеупомянутые ограничения. Вы все равно используете немедленный режим в этом примере, поэтому glRasterPos (...) и glDrawPixels (...) по-прежнему допустимы.

person Andon M. Coleman    schedule 03.12.2013
comment
Спасибо, я не заметил этой ошибки. Изменил код, а картинка все та же. Я действительно не понимаю, почему я не могу использовать GL_POINT. Как я уже сказал, я все еще новичок, это то, что мы должны использовать в соответствии с исходным кодом, предоставленным инструктором. - person Margi; 03.12.2013
comment
@Марги: А. Это стыд. Мое возражение против GL_POINT заключается только в том, что не всегда гарантируется выполнение того, о чем вы просите, в этом примере кода. Большинство реализаций OpenGL допускают размер точки 2,0, но минимум, который требуется для поддержки любой реализации OpenGL, на самом деле составляет 1,0. Они также не всегда квадратные, если у вас включено сглаживание или сглаживание точек. Вы, вероятно, можете спокойно игнорировать эти детали для вашего случая, но они связаны с проблемами переносимости, о которых следует знать. - person Andon M. Coleman; 03.12.2013

Похоже, вы реализуете формулу здесь, но в конце отклоняетесь от направления, статья занимает.

Во-первых, статья предупреждает, что D и b могут быть очень близки по значению, так что -b + D дает вам очень ограниченное число. Предлагают альтернативу.

Кроме того, вы проверяете, что и t0, и t1 > 0. Это не обязательно должно быть правдой, чтобы вы попали в сферу, вы можете оказаться внутри нее (хотя вы, очевидно, не должны быть в своей тестовой сцене).

Наконец, я бы добавил тест в начале, чтобы подтвердить, что вектор направления нормализован. Я испортил это более одного раза в своих рендерерах.

person woolstar    schedule 03.12.2013
comment
Спасибо, сверился со статьей, проверил на нормализацию. Кажется, у меня ничего не работает. - person Margi; 03.12.2013