用 Javascript 實現光線跟蹤演算法
阿新 • • 發佈:2020-12-13
光線跟蹤演算法的介紹
光線跟蹤是一種真實地顯示物體的方法,該方法由Appe在1968年提出。光線跟蹤方法沿著到達視點的光線的反方向跟蹤,經過螢幕上每一個象素,找出與視線相交的物體表麵點P0,並繼續跟蹤,找出影響P0點光強的所有光源,從而算出P0點上精確的光線強度,在材質編輯中經常用來表現鏡面效果。光線跟蹤或稱光跡追蹤是計算機圖形學的核心演算法之一。在演算法中,光線從光源被拋射出來,當他們經過物體表面的時候,對他們應用種種符合物理光學定律的變換。最終,光線進入虛擬的攝像機底片中,圖片被生成出來。
效果圖
基本思想
- 筒型照明器的傳輸方程有一個cos
- 可以寫出球形等各種照明器
- 場景中每個表面都有自己的輻射率,從照明器隨機位置發射隨機方向粒子,檢測碰撞
- 如果擊中則增加擊中表面的輻射率l,p控制粒子最終是否被表面吸收,並隨機決定是否反射
- 把資料儲存在物件的紋理檔案中
- 進行路徑跟蹤,類似光線跟蹤。
至此,我們來實現一個光線跟蹤的效果(實際上是懶得寫註釋 doge)
程式碼如下
<script> document.body.style.background="#000"; var C=document.getElementById("CANVAS").getContext("2d") document.onkeydown = function(event){ if(event.keyCode==38){//up cinemaZ+=10; } else if(event.keyCode==40){//down cinemaZ-=10; } else if(event.keyCode==37){//left q+=-.005 } else if(event.keyCode==39){//right q-=-.005 } } function sphere_hit(x,y,z,r,ex,ey,ez,sx,sy,sz){ m=Math a=(sx-ex)*(sx-ex)+(sy-ey)*(sy-ey)+(sz-ez)*(sz-ez) b=2*(sx-ex)*(ex-x)+2*(sy-ey)*(ey-y)+2*(sz-ez)*(ez-z) c=ex*ex-2*x*ex+x*x + ey*ey-2*y*ey+y*y + ez*ez-2*z*ez+z*z -r*r theta=b*b-4*a*c if(theta<0)return null; else { x1=(-b+m.sqrt(theta))/(2*a) x2=(-b-m.sqrt(theta))/(2*a) //alert("a:"+a+".b:"+b+".c:"+c+".x1="+x1+" x2="+x2) tt=(x1<x2)?x1:x2 if(tt<0)return null return { t:tt, nx:ex+sx*tt-x, ny:ey+sy*tt-y, nz:ez+sz*tt-z }; } } function unitization(vector) { // if(vector[0]==null || vector[1]==null || vector[2]==null) alert("In Function unitization:Not Acceptable Parameters") m=Math.sqrt(vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2]) return new Array(vector[0]/m,vector[1]/m,vector[2]/m) } function transvection(v1,v2){ // if(v1[0]==null || v1[1]==null || v[2]==null) alert("In Function transvection:Not Acceptable Parameters in 1") // if(v2[0]==null || v2[1]==null || v2[2]==null) alert("In Function transvection:Not Acceptable Parameters in 2") return v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2] } /* ************ obj List *********** 1.Sphere 2.Sphere 3.Sphere 4.Plate ************ obj List *********** */ plateN = unitization([0,-1,0]) var obj = new Array( new Array(new Array(-500,-300,250),300,[255,0,0]), new Array(new Array(-300,-300,700),300,[255,200,0]), new Array(new Array(300,-300,15),300,[0,0,255]) ) eye=[0,-300,-800] screenZ=-500 dem=[700,1000] directal_light=unitization([1/1.732,1/1.732,-1/1.732]) envir_col=[0.6,0.6,0.6] RAY_TRA=3 function detection(e,s){ // if(e[0]==null || e[1]==null || e[2]==null) alert("In Function detection:Not Acceptable Parameters in 1") // if(s[0]==null || s[1]==null || s[2]==null) alert("In Function detection:Not Acceptable Parameters in 2") var nearest={} nearest.t=9999 nearest.n=null for(io=0;io<obj.length;io++){ sinfor=sphere_hit(obj[io][0][0],obj[io][0][1],obj[io][0][2],obj[io][1],e[0],e[1],e[2],s[0],s[1],s[2]) if(sinfor!=null)if(sinfor.t<nearest.t){nearest.n=io;nearest.t=sinfor.t;} } if(nearest.t==9999) { /* the farthest locatial ,this value mean the ratio between screen-e and object position not hit all object,we have to test y=0 plate ,otherwise fill backgroundcolor */ if(s[1]!=e[1] && (pt= (-e[1]/(s[1]-e[1])) ) >0 ) {return null;} //nothing having been hited,meanwhile,out of the y=0 plate boundary else //y=0 plate had been hited,calculate corresponding coordinates { return new Array(e[0]+pt*(s[0]-e[0]),0,e[2]+pt*(s[2]-e[2])) }} else return nearest; } function reflect(l,n){ // if(l[0]==null || l[1]==null || l[2]==null) alert("In Function reflection:Not Acceptable Parameters in 1") // if(n[0]==null || n[1]==null || n[2]==null) alert("In Function reflection:Not Acceptable Parameters in 2") n=unitization(n) // plateN had been unitizated l=unitization(l) co=transvection(l,n) return new Array(l[0]-2*co*n[0],l[1]-2*co*n[1],l[2]-2*co*n[2]) } for(i=0;i<=1;i+=.0005) for(j=-.5;j<=.5;j+=.0005) { e=eye scr=s=[dem[1]*j,-dem[0]*i,screenZ] for(rt=RAY_TRA;rt>0;rt--){ point=detection(e,s) if(point==null) {C.fillStyle="rgb(0,0,0)"; //if(rt==RAY_TRA){ C.fillRect(s[0]+dem[1]/2,-s[1],1,1);}else {C.fillRect(scr[0]+dem[1]/2,-scr[1],1,1);} break;} if(rt==RAY_TRA) C.fillRect(s[0]+dem[1]/2,-s[1],1,1);break;} else{ l=[s[0]-e[0],s[1]-e[1],s[2]-e[2]] if(point[2]!=null){ //is plate aaa=Math.floor(Math.sin(point[0]*6.28/200)+1 ) bbb=Math.floor(Math.sin(point[2]*6.28/200)+1 ) if(aaa==bbb)C.fillStyle="rgb(0,0,0)"; else C.fillStyle="rgb(255,255,255)"; C.fillRect(scr[0]+dem[1]/2,-scr[1],1,1); rr=reflect(l,plateN) e=point s=[e[0]+rr[0],e[1]+rr[1],e[2]+rr[2]] }else{ //is object rgb=obj[point.n][2] //phong color model p0=obj[point.n][0] p1=[e[0]+point.t*(s[0]-e[0]) ,e[1]+point.t*(s[1]-e[1]) ,e[2]+point.t*(s[2]-e[2]) ] nvec=unitization([p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]]) co=nvec[0]*directal_light[0]+nvec[1]*directal_light[1]+nvec[2]*directal_light[2] co=Math.pow(co,31) //sur_col=[Math.round(co*rgb[0]),Math.round(co*rgb[1]),Math.round(co*rgb[2])] sur_col=[Math.min(255,Math.round((1-envir_col[0])*co*rgb[0])+envir_col[0]*rgb[0]), Math.min(255,Math.round((1-envir_col[1])*co*rgb[1])+envir_col[1]*rgb[1]), Math.min(255,Math.round((1-envir_col[2])*co*rgb[2])+envir_col[2]*rgb[2])] //C.fillStyle="rgb("+sur_col[0]+","+sur_col[1]+","+sur_col[2]+")"; C.fillStyle="rgba("+sur_col[0]+","+sur_col[1]+","+sur_col[2]+","+ rt/RAY_TRA +")"; C.fillRect(scr[0]+dem[1]/2,-scr[1],1,1); rr=reflect(l,nvec) e=p1 s=[e[0]+rr[0],e[1]+rr[1],e[2]+rr[2]] } } //C.fillRect(s[0]+dem[1]/2,-s[1],1,1); } } </script>