Отражение и преломление невозможно без рекурсивного отслеживания лучей?

Я пишу визуализатор raytracing реального времени на основе графического процессора, используя вычислительный шейдер GLSL. Пока это работает очень хорошо, но я наткнулся на кажущуюся неразрешимую проблему, когда речь идет о одновременном отражении и рефракции.

Моя логика говорит мне, что для того, чтобы иметь отражения и преломления на объекте, таком как стекло, луч должен был бы разделиться на два, один луч отражался от поверхности, а другой преломлялся через поверхность. Конечные цвета этих лучей затем будут объединены на основе некоторой функции и в конечном итоге будут использоваться в качестве цвета пикселя, из которого исходит луч. Проблема в том, что я не могу разделить лучи в коде шейдера, так как мне пришлось бы использовать рекурсию для этого. По моему мнению, функции в шейдере не могут быть рекурсивными, потому что все функции GLSL похожи на встроенные функции на C ++ из-за проблем с совместимостью со старым оборудованием GPU.

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

Мне удается преобразовать обратное трассирование в итеративный процесс, подходящий для GLSL, с помощью метода, предложенного в моем комментарии. Это далеко не оптимизировано, и у меня нет всех физических вещей, реализованных (закон Снелла и т. Д.), Но, как доказательство концепции, он работает уже. Я делаю весь материал в fragmentах шейдера и кода на стороне процессора, просто отправляю константы и сцену в форме 32-битной нефиксированной поплавковой текстуры GL_LUMINANCE32F_ARB . Отрисовка – это всего лишь один QUAD охватывающий весь экран.

  1. прохождение сцены

    Я решил сохранить сцену в текстуре, чтобы каждый луч / fragment имел прямой доступ ко всей сцене. Текстурой является 2D, но она используется как линейный список 32-битных поплавков. Я решил этот формат:

     enum _fac_type_enum { _fac_triangles=0, // r,g,b,a, n, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 } _fac_spheres, // r,g,b,a, n, sphere count, { x,y,z,r } }; const GLfloat _n_glass=1.561; const GLfloat _n_vacuum=1.0; GLfloat data[]= { // r, g, b, a, n, type,count 0.2,0.3,0.5,0.5,_n_glass,_fac_triangles, 4, // tetrahedron // px, py, pz, r, g, b -0.5,-0.5,+1.0, 0.0,+0.5,+1.0, +0.5,-0.5,+1.0, 0.0, 0.0,+0.5, -0.5,-0.5,+1.0, 0.0,+0.5,+1.0, 0.0, 0.0,+0.5, 0.0,+0.5,+1.0, +0.5,-0.5,+1.0, 0.0, 0.0,+0.5, +0.5,-0.5,+1.0, -0.5,-0.5,+1.0, }; 

    Вы можете добавить / изменить любой тип объекта. Этот пример содержит только один полупрозрачный голубой сине-коричневый тетраэдр. Вы также можете добавить матрицы преобразования больше коэффициентов для свойств материала и т. Д. …

  2. Архитектура

    Vertex shader просто инициализирует угловые лучи зрения (начальное положение и направление), которое интерполируется, поэтому каждый fragment представляет собой начальный луч процесса отслеживания лучей.

Итеративное отслеживание лучей

Поэтому я создал «статический» список лучей и запустил его с помощью луча начала. Итерация выполняется в два этапа: сначала трассировка лучей:

  1. Прокрутите все лучи в списке с первого
  2. Найти ближайший пересечение со сценой …

    сохранить положение, свойства поверхности и свойства материала в struct луча

  3. Если пересечение найдено, а не последний слой «рекурсии», добавьте reflection / преломление лучей в список в конце.

    также сохраняют свои индексы в обработанной struct

Теперь ваши лучи должны содержать всю информацию о пересечении, необходимую для восстановления цвета. Для этого:

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

    поэтому используйте уравнения освещения, которые вы хотите. Если луч содержит детей, добавьте их цвет к результату, основанному на свойствах материала (коэффициенты отражения и преломления …)

Теперь первый луч должен содержать цвет, который вы хотите вывести.

Используемая форма:

matrix матрицы просмотра tm_eye
aspect соотношение сторон ys / xs
n0 показатель преломления n0 пространства (еще не используется)
Фокусное расстояние камеры фокальной длины
fac_siz разрешение квадратной текстуры сцены
fac_num число поплавков, фактически используемых в текстуре сцены
fac_txr текстурная единица для текстуры сцены

Предварительный просмотр:

предварительный просмотр

Фрагментный шейдер содержит отладочные отпечатки, поэтому вам понадобится текстура, если она используется в QA:

  • Отладочные отпечатки GLSL

Делать:

добавлять матрицы для объектов, камеры и т. д.
добавлять свойства материала (блеск, коэффициент отражения / рефракции)
Закон Снелла прямо сейчас направляет новые лучи …
могут быть отдельными R, G, B- 3 пусковыми лучами и объединяться в конце
поддельное SSS Поверхностное рассеяние на основе длин лучей
лучше реализовать огни (прямо сейчас они являются константами в коде)
реализовать больше примитивов (сейчас поддерживаются только треугольники)

Отладка и обновление кода [Edit1]

Я удалил старый исходный код, чтобы он соответствовал пределу 30KB. Если вам это нужно, то выкопайте его из истории изменений. Было время для более продвинутой отладки для этого, и вот результат:

предварительный просмотр

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

просмотр отладки

В представлении отладки только куб прозрачен, а последний луч, который ничего не ударяет, игнорируется. Таким образом, вы можете увидеть раскол … Луч закончился внутри куба из-за полного угла отражения И я отключил все отражения внутри объектов по соображениям скорости.

32-битные floats для обнаружения пересечения немного шумны с расстояниями, поэтому вы можете использовать 64-битные doubles вместо этого, но в этом случае скорость значительно падает. Другой вариант – переписать уравнение для использования относительных координат, которые более точны в этом случае использования.

Здесь источник float шейдеров:

Vertex:

 //------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ uniform float aspect; uniform float focal_length; uniform mat4x4 tm_eye; layout(location=0) in vec2 pos; out smooth vec2 txt_pos; // frag position on screen <-1,+1> for debug prints out smooth vec3 ray_pos; // ray start position out smooth vec3 ray_dir; // ray start direction //------------------------------------------------------------------ void main(void) { vec4 p; txt_pos=pos; // perspective projection p=tm_eye*vec4(pos.x/aspect,pos.y,0.0,1.0); ray_pos=p.xyz; p-=tm_eye*vec4(0.0,0.0,-focal_length,1.0); ray_dir=normalize(p.xyz); gl_Position=vec4(pos,0.0,1.0); } //------------------------------------------------------------------ 

Фрагмент:

 //------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ // Ray tracer ver: 1.000 //------------------------------------------------------------------ in smooth vec3 ray_pos; // ray start position in smooth vec3 ray_dir; // ray start direction uniform float n0; // refractive index of camera origin uniform int fac_siz; // square texture x,y resolution size uniform int fac_num; // number of valid floats in texture uniform sampler2D fac_txr; // scene mesh data texture out layout(location=0) vec4 frag_col; //--------------------------------------------------------------------------- //#define _debug_print #define _reflect #define _refract //--------------------------------------------------------------------------- #ifdef _debug_print in vec2 txt_pos; // frag screen position <-1,+1> uniform sampler2D txr_font; // ASCII 32x8 characters font texture unit uniform float txt_fxs,txt_fys; // font/screen resolution ratio const int _txtsiz=64; // text buffer size int txt[_txtsiz],txtsiz; // text buffer and its actual size vec4 txt_col=vec4(0.0,0.0,0.0,1.0); // color interface for txt_print() bool _txt_col=false; // is txt_col active? void txt_decimal(vec2 v); // print vec3 into txt void txt_decimal(vec3 v); // print vec3 into txt void txt_decimal(vec4 v); // print vec3 into txt void txt_decimal(float x); // print float x into txt void txt_decimal(int x); // print int x into txt void txt_print(float x0,float y0); // print txt at x0,y0 [chars] #endif //--------------------------------------------------------------------------- void main(void) { const vec3 light_dir=normalize(vec3(0.1,0.1,1.0)); const float light_iamb=0.1; // dot offset const float light_idir=0.5; // directional light amplitude const vec3 back_col=vec3(0.2,0.2,0.2); // background color const float _zero=1e-6; // to avoid intrsection with start point of ray const int _fac_triangles=0; // r,g,b, refl,refr,n, type, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 } const int _fac_spheres =1; // r,g,b, refl,refr,n, type, sphere count, { x,y,z,r } // ray scene intersection struct _ray { vec3 pos,dir,nor; vec3 col; float refl,refr;// reflection,refraction intensity coeficients float n0,n1,l; // refaction index (start,end) , ray length int lvl,i0,i1; // recursion level, reflect, refract }; const int _lvls=5; const int _rays=(1<<_lvls)-1; _ray ray[_rays]; int rays; vec3 v0,v1,v2,pos; vec3 c,col; float refr,refl; float tt,t,n1,a; int i0,ii,num,id; // fac texture access vec2 st; int i,j; float ds=1.0/float(fac_siz-1); #define fac_get texture(fac_txr,st).r; st.s+=ds; i++; j++; if (j==fac_siz) { j=0; st.s=0.0; st.t+=ds; } // enque start ray ray[0].pos=ray_pos; ray[0].dir=normalize(ray_dir); ray[0].nor=vec3(0.0,0.0,0.0); ray[0].refl=0.0; ray[0].refr=0.0; ray[0].n0=n0; ray[0].n1=1.0; ray[0].l =0.0; ray[0].lvl=0; ray[0].i0=-1; ray[0].i1=-1; rays=1; // debug print area #ifdef _debug_print bool _dbg=false; float dbg_x0=45.0; float dbg_y0= 1.0; float dbg_xs=12.0; float dbg_ys=_rays+1.0; dbg_xs=40.0; dbg_ys=10; float x=0.5*(1.0+txt_pos.x)/txt_fxs; x-=dbg_x0; float y=0.5*(1.0-txt_pos.y)/txt_fys; y-=dbg_y0; // inside bbox? if ((x>=0.0)&&(x<=dbg_xs) &&(y>=0.0)&&(y<=dbg_ys)) { // prints on _dbg=true; // preset debug ray ray[0].pos=vec3(0.0,0.0,0.0)*2.5; ray[0].dir=vec3(0.0,0.0,1.0); } #endif // loop all enqued rays for (i0=0;i00;num--) { v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; v1.x=fac_get; v1.y=fac_get; v1.z=fac_get; v2.x=fac_get; v2.y=fac_get; v2.z=fac_get; vec3 e1,e2,n,p,q,r; float t,u,v,det,idet; //compute ray triangle intersection e1=v1-v0; e2=v2-v0; // Calculate planes normal vector p=cross(ray[i0].dir,e2); det=dot(e1,p); // Ray is parallel to plane if (abs(det)<1e-8) continue; idet=1.0/det; r=ray[i0].pos-v0; u=dot(r,p)*idet; if ((u<0.0)||(u>1.0)) continue; q=cross(r,e1); v=dot(ray[i0].dir,q)*idet; if ((v<0.0)||(u+v>1.0)) continue; t=dot(e2,q)*idet; if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // barycentric interpolate position t=1.0-uv; pos=(v0*t)+(v1*u)+(v2*v); // compute normal (store as dir for now) e1=v1-v0; e2=v2-v1; ray[i0].nor=cross(e1,e2); } } if (id==_fac_spheres) for (;num>0;num--) { float r; v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; r=fac_get; // compute l0 length of ray(p0,dp) to intersection with sphere(v0,r) // where rr= r^-2 float aa,bb,cc,dd,l0,l1,rr; vec3 p0,dp; p0=ray[i0].pos-v0; // set sphere center to (0,0,0) dp=ray[i0].dir; rr = 1.0/(r*r); aa=2.0*rr*dot(dp,dp); bb=2.0*rr*dot(p0,dp); cc= rr*dot(p0,p0)-1.0; dd=((bb*bb)-(2.0*aa*cc)); if (dd<0.0) continue; dd=sqrt(dd); l0=(-bb+dd)/aa; l1=(-bb-dd)/aa; if (l0<0.0) l0=l1; if (l1<0.0) l1=l0; t=min(l0,l1); if (t<=_zero) t=max(l0,l1); if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // position,normal pos=ray[i0].pos+(ray[i0].dir*t); ray[i0].nor=pos-v0; } } } ray[i0].l=tt; ray[i0].nor=normalize(ray[i0].nor); // split ray from pos and ray[i0].nor if ((ii==0)&&(ray[i0].lvl<_lvls-1)) { t=dot(ray[i0].dir,ray[i0].nor); // reflect #ifdef _reflect if ((ray[i0].refl>_zero)&&(t<_zero)) // do not reflect inside objects { ray[i0].i0=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; ray[rays].dir=ray[rays].dir-(2.0*t*ray[rays].nor); ray[rays].n0=ray[i0].n0; ray[rays].n1=ray[i0].n0; rays++; } #endif // refract #ifdef _refract if (ray[i0].refr>_zero) { ray[i0].i1=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; t=dot(ray[i0].dir,ray[i0].nor); if (t>0.0) // exit object { ray[rays].n0=ray[i0].n0; ray[rays].n1=n0; v0=-ray[i0].nor; t=-t; } else{ // enter object ray[rays].n0=n1; ray[rays].n1=ray[i0].n0; ray[i0 ].n1=n1; v0=ray[i0].nor; } n1=ray[i0].n0/ray[i0].n1; tt=1.0-(n1*n1*(1.0-t*t)); if (tt>=0.0) { ray[rays].dir=(ray[i0].dir*n1)-(v0*((n1*t)+sqrt(tt))); rays++; } } #endif } else if (i0>0) // ignore last ray if nothing hit { ray[i0]=ray[rays-1]; rays--; i0--; } } // back track ray intersections and compute output color col // lvl is sorted ascending so backtrack from end for (i0=rays-1;i0>=0;i0--) { // directional + ambient light t=abs(dot(ray[i0].nor,light_dir)*light_idir)+light_iamb; t*=1.0-ray[i0].refl-ray[i0].refr; ray[i0].col.rgb*=t; // reflect ii=ray[i0].i0; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refl; // refract ii=ray[i0].i1; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refr; } col=ray[0].col; // debug prints #ifdef _debug_print /* if (_dbg) { txtsiz=0; txt_decimal(_lvls); txt[txtsiz]=' '; txtsiz++; txt_decimal(rays); txt[txtsiz]=' '; txtsiz++; txt_decimal(_rays); txt_print(dbg_x0,dbg_y0); for (ii=0;iifloat(txtsiz))||(y<0.0)||(y>1.0)) return; // get font texture position for target ASCII i=int(x); // char index in txt x-=float(i); i=txt[i]; x+=float(int(i&31)); y+=float(int(i>>5)); x/=32.0; y/=8.0; // offset in char texture txt_col=texture(txr_font,vec2(x,y)); _txt_col=true; } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- - //------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ // Ray tracer ver: 1.000 //------------------------------------------------------------------ in smooth vec3 ray_pos; // ray start position in smooth vec3 ray_dir; // ray start direction uniform float n0; // refractive index of camera origin uniform int fac_siz; // square texture x,y resolution size uniform int fac_num; // number of valid floats in texture uniform sampler2D fac_txr; // scene mesh data texture out layout(location=0) vec4 frag_col; //--------------------------------------------------------------------------- //#define _debug_print #define _reflect #define _refract //--------------------------------------------------------------------------- #ifdef _debug_print in vec2 txt_pos; // frag screen position <-1,+1> uniform sampler2D txr_font; // ASCII 32x8 characters font texture unit uniform float txt_fxs,txt_fys; // font/screen resolution ratio const int _txtsiz=64; // text buffer size int txt[_txtsiz],txtsiz; // text buffer and its actual size vec4 txt_col=vec4(0.0,0.0,0.0,1.0); // color interface for txt_print() bool _txt_col=false; // is txt_col active? void txt_decimal(vec2 v); // print vec3 into txt void txt_decimal(vec3 v); // print vec3 into txt void txt_decimal(vec4 v); // print vec3 into txt void txt_decimal(float x); // print float x into txt void txt_decimal(int x); // print int x into txt void txt_print(float x0,float y0); // print txt at x0,y0 [chars] #endif //--------------------------------------------------------------------------- void main(void) { const vec3 light_dir=normalize(vec3(0.1,0.1,1.0)); const float light_iamb=0.1; // dot offset const float light_idir=0.5; // directional light amplitude const vec3 back_col=vec3(0.2,0.2,0.2); // background color const float _zero=1e-6; // to avoid intrsection with start point of ray const int _fac_triangles=0; // r,g,b, refl,refr,n, type, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 } const int _fac_spheres =1; // r,g,b, refl,refr,n, type, sphere count, { x,y,z,r } // ray scene intersection struct _ray { vec3 pos,dir,nor; vec3 col; float refl,refr;// reflection,refraction intensity coeficients float n0,n1,l; // refaction index (start,end) , ray length int lvl,i0,i1; // recursion level, reflect, refract }; const int _lvls=5; const int _rays=(1<<_lvls)-1; _ray ray[_rays]; int rays; vec3 v0,v1,v2,pos; vec3 c,col; float refr,refl; float tt,t,n1,a; int i0,ii,num,id; // fac texture access vec2 st; int i,j; float ds=1.0/float(fac_siz-1); #define fac_get texture(fac_txr,st).r; st.s+=ds; i++; j++; if (j==fac_siz) { j=0; st.s=0.0; st.t+=ds; } // enque start ray ray[0].pos=ray_pos; ray[0].dir=normalize(ray_dir); ray[0].nor=vec3(0.0,0.0,0.0); ray[0].refl=0.0; ray[0].refr=0.0; ray[0].n0=n0; ray[0].n1=1.0; ray[0].l =0.0; ray[0].lvl=0; ray[0].i0=-1; ray[0].i1=-1; rays=1; // debug print area #ifdef _debug_print bool _dbg=false; float dbg_x0=45.0; float dbg_y0= 1.0; float dbg_xs=12.0; float dbg_ys=_rays+1.0; dbg_xs=40.0; dbg_ys=10; float x=0.5*(1.0+txt_pos.x)/txt_fxs; x-=dbg_x0; float y=0.5*(1.0-txt_pos.y)/txt_fys; y-=dbg_y0; // inside bbox? if ((x>=0.0)&&(x<=dbg_xs) &&(y>=0.0)&&(y<=dbg_ys)) { // prints on _dbg=true; // preset debug ray ray[0].pos=vec3(0.0,0.0,0.0)*2.5; ray[0].dir=vec3(0.0,0.0,1.0); } #endif // loop all enqued rays for (i0=0;i00;num--) { v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; v1.x=fac_get; v1.y=fac_get; v1.z=fac_get; v2.x=fac_get; v2.y=fac_get; v2.z=fac_get; vec3 e1,e2,n,p,q,r; float t,u,v,det,idet; //compute ray triangle intersection e1=v1-v0; e2=v2-v0; // Calculate planes normal vector p=cross(ray[i0].dir,e2); det=dot(e1,p); // Ray is parallel to plane if (abs(det)<1e-8) continue; idet=1.0/det; r=ray[i0].pos-v0; u=dot(r,p)*idet; if ((u<0.0)||(u>1.0)) continue; q=cross(r,e1); v=dot(ray[i0].dir,q)*idet; if ((v<0.0)||(u+v>1.0)) continue; t=dot(e2,q)*idet; if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // barycentric interpolate position t=1.0-uv; pos=(v0*t)+(v1*u)+(v2*v); // compute normal (store as dir for now) e1=v1-v0; e2=v2-v1; ray[i0].nor=cross(e1,e2); } } if (id==_fac_spheres) for (;num>0;num--) { float r; v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; r=fac_get; // compute l0 length of ray(p0,dp) to intersection with sphere(v0,r) // where rr= r^-2 float aa,bb,cc,dd,l0,l1,rr; vec3 p0,dp; p0=ray[i0].pos-v0; // set sphere center to (0,0,0) dp=ray[i0].dir; rr = 1.0/(r*r); aa=2.0*rr*dot(dp,dp); bb=2.0*rr*dot(p0,dp); cc= rr*dot(p0,p0)-1.0; dd=((bb*bb)-(2.0*aa*cc)); if (dd<0.0) continue; dd=sqrt(dd); l0=(-bb+dd)/aa; l1=(-bb-dd)/aa; if (l0<0.0) l0=l1; if (l1<0.0) l1=l0; t=min(l0,l1); if (t<=_zero) t=max(l0,l1); if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // position,normal pos=ray[i0].pos+(ray[i0].dir*t); ray[i0].nor=pos-v0; } } } ray[i0].l=tt; ray[i0].nor=normalize(ray[i0].nor); // split ray from pos and ray[i0].nor if ((ii==0)&&(ray[i0].lvl<_lvls-1)) { t=dot(ray[i0].dir,ray[i0].nor); // reflect #ifdef _reflect if ((ray[i0].refl>_zero)&&(t<_zero)) // do not reflect inside objects { ray[i0].i0=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; ray[rays].dir=ray[rays].dir-(2.0*t*ray[rays].nor); ray[rays].n0=ray[i0].n0; ray[rays].n1=ray[i0].n0; rays++; } #endif // refract #ifdef _refract if (ray[i0].refr>_zero) { ray[i0].i1=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; t=dot(ray[i0].dir,ray[i0].nor); if (t>0.0) // exit object { ray[rays].n0=ray[i0].n0; ray[rays].n1=n0; v0=-ray[i0].nor; t=-t; } else{ // enter object ray[rays].n0=n1; ray[rays].n1=ray[i0].n0; ray[i0 ].n1=n1; v0=ray[i0].nor; } n1=ray[i0].n0/ray[i0].n1; tt=1.0-(n1*n1*(1.0-t*t)); if (tt>=0.0) { ray[rays].dir=(ray[i0].dir*n1)-(v0*((n1*t)+sqrt(tt))); rays++; } } #endif } else if (i0>0) // ignore last ray if nothing hit { ray[i0]=ray[rays-1]; rays--; i0--; } } // back track ray intersections and compute output color col // lvl is sorted ascending so backtrack from end for (i0=rays-1;i0>=0;i0--) { // directional + ambient light t=abs(dot(ray[i0].nor,light_dir)*light_idir)+light_iamb; t*=1.0-ray[i0].refl-ray[i0].refr; ray[i0].col.rgb*=t; // reflect ii=ray[i0].i0; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refl; // refract ii=ray[i0].i1; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refr; } col=ray[0].col; // debug prints #ifdef _debug_print /* if (_dbg) { txtsiz=0; txt_decimal(_lvls); txt[txtsiz]=' '; txtsiz++; txt_decimal(rays); txt[txtsiz]=' '; txtsiz++; txt_decimal(_rays); txt_print(dbg_x0,dbg_y0); for (ii=0;iifloat(txtsiz))||(y<0.0)||(y>1.0)) return; // get font texture position for target ASCII i=int(x); // char index in txt x-=float(i); i=txt[i]; x+=float(int(i&31)); y+=float(int(i>>5)); x/=32.0; y/=8.0; // offset in char texture txt_col=texture(txr_font,vec2(x,y)); _txt_col=true; } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- - //------------------------------------------------------------------ #version 420 core //------------------------------------------------------------------ // Ray tracer ver: 1.000 //------------------------------------------------------------------ in smooth vec3 ray_pos; // ray start position in smooth vec3 ray_dir; // ray start direction uniform float n0; // refractive index of camera origin uniform int fac_siz; // square texture x,y resolution size uniform int fac_num; // number of valid floats in texture uniform sampler2D fac_txr; // scene mesh data texture out layout(location=0) vec4 frag_col; //--------------------------------------------------------------------------- //#define _debug_print #define _reflect #define _refract //--------------------------------------------------------------------------- #ifdef _debug_print in vec2 txt_pos; // frag screen position <-1,+1> uniform sampler2D txr_font; // ASCII 32x8 characters font texture unit uniform float txt_fxs,txt_fys; // font/screen resolution ratio const int _txtsiz=64; // text buffer size int txt[_txtsiz],txtsiz; // text buffer and its actual size vec4 txt_col=vec4(0.0,0.0,0.0,1.0); // color interface for txt_print() bool _txt_col=false; // is txt_col active? void txt_decimal(vec2 v); // print vec3 into txt void txt_decimal(vec3 v); // print vec3 into txt void txt_decimal(vec4 v); // print vec3 into txt void txt_decimal(float x); // print float x into txt void txt_decimal(int x); // print int x into txt void txt_print(float x0,float y0); // print txt at x0,y0 [chars] #endif //--------------------------------------------------------------------------- void main(void) { const vec3 light_dir=normalize(vec3(0.1,0.1,1.0)); const float light_iamb=0.1; // dot offset const float light_idir=0.5; // directional light amplitude const vec3 back_col=vec3(0.2,0.2,0.2); // background color const float _zero=1e-6; // to avoid intrsection with start point of ray const int _fac_triangles=0; // r,g,b, refl,refr,n, type, triangle count, { x0,y0,z0,x1,y1,z1,x2,y2,z2 } const int _fac_spheres =1; // r,g,b, refl,refr,n, type, sphere count, { x,y,z,r } // ray scene intersection struct _ray { vec3 pos,dir,nor; vec3 col; float refl,refr;// reflection,refraction intensity coeficients float n0,n1,l; // refaction index (start,end) , ray length int lvl,i0,i1; // recursion level, reflect, refract }; const int _lvls=5; const int _rays=(1<<_lvls)-1; _ray ray[_rays]; int rays; vec3 v0,v1,v2,pos; vec3 c,col; float refr,refl; float tt,t,n1,a; int i0,ii,num,id; // fac texture access vec2 st; int i,j; float ds=1.0/float(fac_siz-1); #define fac_get texture(fac_txr,st).r; st.s+=ds; i++; j++; if (j==fac_siz) { j=0; st.s=0.0; st.t+=ds; } // enque start ray ray[0].pos=ray_pos; ray[0].dir=normalize(ray_dir); ray[0].nor=vec3(0.0,0.0,0.0); ray[0].refl=0.0; ray[0].refr=0.0; ray[0].n0=n0; ray[0].n1=1.0; ray[0].l =0.0; ray[0].lvl=0; ray[0].i0=-1; ray[0].i1=-1; rays=1; // debug print area #ifdef _debug_print bool _dbg=false; float dbg_x0=45.0; float dbg_y0= 1.0; float dbg_xs=12.0; float dbg_ys=_rays+1.0; dbg_xs=40.0; dbg_ys=10; float x=0.5*(1.0+txt_pos.x)/txt_fxs; x-=dbg_x0; float y=0.5*(1.0-txt_pos.y)/txt_fys; y-=dbg_y0; // inside bbox? if ((x>=0.0)&&(x<=dbg_xs) &&(y>=0.0)&&(y<=dbg_ys)) { // prints on _dbg=true; // preset debug ray ray[0].pos=vec3(0.0,0.0,0.0)*2.5; ray[0].dir=vec3(0.0,0.0,1.0); } #endif // loop all enqued rays for (i0=0;i00;num--) { v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; v1.x=fac_get; v1.y=fac_get; v1.z=fac_get; v2.x=fac_get; v2.y=fac_get; v2.z=fac_get; vec3 e1,e2,n,p,q,r; float t,u,v,det,idet; //compute ray triangle intersection e1=v1-v0; e2=v2-v0; // Calculate planes normal vector p=cross(ray[i0].dir,e2); det=dot(e1,p); // Ray is parallel to plane if (abs(det)<1e-8) continue; idet=1.0/det; r=ray[i0].pos-v0; u=dot(r,p)*idet; if ((u<0.0)||(u>1.0)) continue; q=cross(r,e1); v=dot(ray[i0].dir,q)*idet; if ((v<0.0)||(u+v>1.0)) continue; t=dot(e2,q)*idet; if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // barycentric interpolate position t=1.0-uv; pos=(v0*t)+(v1*u)+(v2*v); // compute normal (store as dir for now) e1=v1-v0; e2=v2-v1; ray[i0].nor=cross(e1,e2); } } if (id==_fac_spheres) for (;num>0;num--) { float r; v0.x=fac_get; v0.y=fac_get; v0.z=fac_get; r=fac_get; // compute l0 length of ray(p0,dp) to intersection with sphere(v0,r) // where rr= r^-2 float aa,bb,cc,dd,l0,l1,rr; vec3 p0,dp; p0=ray[i0].pos-v0; // set sphere center to (0,0,0) dp=ray[i0].dir; rr = 1.0/(r*r); aa=2.0*rr*dot(dp,dp); bb=2.0*rr*dot(p0,dp); cc= rr*dot(p0,p0)-1.0; dd=((bb*bb)-(2.0*aa*cc)); if (dd<0.0) continue; dd=sqrt(dd); l0=(-bb+dd)/aa; l1=(-bb-dd)/aa; if (l0<0.0) l0=l1; if (l1<0.0) l1=l0; t=min(l0,l1); if (t<=_zero) t=max(l0,l1); if ((t>_zero)&&((t<=tt)||(ii!=0))) { ii=0; tt=t; // store color,n ... ray[i0].col=c; ray[i0].refl=refl; ray[i0].refr=refr; // position,normal pos=ray[i0].pos+(ray[i0].dir*t); ray[i0].nor=pos-v0; } } } ray[i0].l=tt; ray[i0].nor=normalize(ray[i0].nor); // split ray from pos and ray[i0].nor if ((ii==0)&&(ray[i0].lvl<_lvls-1)) { t=dot(ray[i0].dir,ray[i0].nor); // reflect #ifdef _reflect if ((ray[i0].refl>_zero)&&(t<_zero)) // do not reflect inside objects { ray[i0].i0=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; ray[rays].dir=ray[rays].dir-(2.0*t*ray[rays].nor); ray[rays].n0=ray[i0].n0; ray[rays].n1=ray[i0].n0; rays++; } #endif // refract #ifdef _refract if (ray[i0].refr>_zero) { ray[i0].i1=rays; ray[rays]=ray[i0]; ray[rays].lvl++; ray[rays].i0=-1; ray[rays].i1=-1; ray[rays].pos=pos; t=dot(ray[i0].dir,ray[i0].nor); if (t>0.0) // exit object { ray[rays].n0=ray[i0].n0; ray[rays].n1=n0; v0=-ray[i0].nor; t=-t; } else{ // enter object ray[rays].n0=n1; ray[rays].n1=ray[i0].n0; ray[i0 ].n1=n1; v0=ray[i0].nor; } n1=ray[i0].n0/ray[i0].n1; tt=1.0-(n1*n1*(1.0-t*t)); if (tt>=0.0) { ray[rays].dir=(ray[i0].dir*n1)-(v0*((n1*t)+sqrt(tt))); rays++; } } #endif } else if (i0>0) // ignore last ray if nothing hit { ray[i0]=ray[rays-1]; rays--; i0--; } } // back track ray intersections and compute output color col // lvl is sorted ascending so backtrack from end for (i0=rays-1;i0>=0;i0--) { // directional + ambient light t=abs(dot(ray[i0].nor,light_dir)*light_idir)+light_iamb; t*=1.0-ray[i0].refl-ray[i0].refr; ray[i0].col.rgb*=t; // reflect ii=ray[i0].i0; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refl; // refract ii=ray[i0].i1; if (ii>=0) ray[i0].col.rgb+=ray[ii].col.rgb*ray[i0].refr; } col=ray[0].col; // debug prints #ifdef _debug_print /* if (_dbg) { txtsiz=0; txt_decimal(_lvls); txt[txtsiz]=' '; txtsiz++; txt_decimal(rays); txt[txtsiz]=' '; txtsiz++; txt_decimal(_rays); txt_print(dbg_x0,dbg_y0); for (ii=0;iifloat(txtsiz))||(y<0.0)||(y>1.0)) return; // get font texture position for target ASCII i=int(x); // char index in txt x-=float(i); i=txt[i]; x+=float(int(i&31)); y+=float(int(i>>5)); x/=32.0; y/=8.0; // offset in char texture txt_col=texture(txr_font,vec2(x,y)); _txt_col=true; } //--------------------------------------------------------------------------- #endif //--------------------------------------------------------------------------- 

Код еще не оптимизирован, но я хотел, чтобы физика работала правильно. По-прежнему не реализовано Fresnells, но вместо этого используются refl,refr коэффициенты материала.

Также вы можете игнорировать материал отладочных отпечатков (они инкапсулированы #define ).

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

 ray.beg(); // rgb rfl rfr n ray.add_material(1.0,1.0,1.0,0.3,0.0,_n_glass); ray.add_box ( 0.0, 0.0, 6.0,9.0,9.0,0.1); ray.add_material(1.0,1.0,1.0,0.1,0.8,_n_glass); ray.add_sphere( 0.0, 0.0, 0.5,0.5); ray.add_material(1.0,0.1,0.1,0.3,0.0,_n_glass); ray.add_sphere( +2.0, 0.0, 2.0,0.5); ray.add_material(0.1,1.0,0.1,0.3,0.0,_n_glass); ray.add_box ( -2.0, 0.0, 2.0,0.5,0.5,0.5); ray.add_material(0.1,0.1,1.0,0.3,0.0,_n_glass); ray.add_tetrahedron ( 0.0, 0.0, 3.0, -1.0,-1.0, 4.0, +1.0,-1.0, 4.0, 0.0,+1.0, 4.0 ); ray.end(); 

Важно, чтобы вычисленные нормали были обращены к объектам, потому что они используются для обнаружения внутренних / внешних пересечений объектов.

PS

Если вам интересно, это мой объемный трассировщик 3D-лучей:

  • Как лучше всего написать движок voxel в C с учетом производительности
  • Что такое нерекурсивное решение для последовательности, подобной Фибоначчи, в Java?
  • Реверсирование строки с рекурсией в Java
  • Рекурсивный вызов ConcurrentHashMap.computeIfAbsent () никогда не завершается. Ошибка или «функция»?
  • Как работает recursion в C
  • Нарушение рекурсии в java
  • Рекурсивная последовательность Фибоначчи
  • Понимание рекурсии для генерации перестановок
  • Как работает «рекурсивная функция» фибоначчи?
  • Примеры рекурсивных функций
  • Рекурсия или итерация?
  • Рекурсивное TreeView в ASP.NET
  • Давайте будем гением компьютера.