Last active
August 20, 2018 07:56
-
-
Save diska/5b64e712e9df7c35702c2bf8784c94fa to your computer and use it in GitHub Desktop.
三葉レイ動画の最初のやつのWebGL実装。途中。暫定。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <meta charset="utf-8"><br/> | |
| <canvas id="CNVS" width="256" height="160"></canvas><hr/> | |
| <input id="SLID" type="range" max="50"> | |
| <script> | |
| const width=256, height=160; // Image size | |
| const bignum=1e5; | |
| function vec3(x=0,y=0,z=0){this.x=x, this.y=y, this.z=z;} | |
| add=function(a,b){return new vec3(a.x+b.x, a.y+b.y, a.z+b.z);} | |
| sub=function(a,b){return new vec3(a.x-b.x, a.y-b.y, a.z-b.z);} | |
| //mul=function(a,b){return new vec3(a.x*b.x, a.y*b.y, a.z*b.z);} | |
| muls=function(a,b){return new vec3(a.x*b, a.y*b, a.z*b);} | |
| //div=function(a,b){return new vec3(a.x/b.x, a.y/b.y, a.z/b.z);} | |
| divs=function(a,b){return new vec3(a.x/b, a.y/b, a.z/b);} | |
| neg=function(a){return new vec3(-a.x, -a.y, -a.z);} | |
| dot=function(a,b){return a.x*b.x+a.y*b.y+a.z*b.z;} | |
| crs=function(a,b){return new vec3(a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x);} | |
| nor=function(a){return divs(a, Math.sqrt(dot(a,a)));} | |
| function tonemap(v){ | |
| var gamma=2.2; | |
| gamma=SLID.value/10; | |
| return Math.min(Math.max(parseInt(Math.pow(v,1/gamma)*0xff),0),0xff); | |
| } | |
| //p:中心位置, r:半径, c:色? | |
| var scene1={spheres:[ | |
| {p:new vec3(-.5,0,0),r:1,c:new vec3(1,0,0)}, | |
| {p:new vec3(0.5,0,0),r:1,c:new vec3(0,1,0)}, | |
| // {p:new vec3(0,0,0),r:1,c:new vec3(.2,.5,.2)}, | |
| ]}; | |
| var scene2={spheres:[ | |
| {p:new vec3( bignum+ 1, 40.8, 81.6), r:bignum, c:new vec3(.75,.25,.25)},// 左 | |
| {p:new vec3(-bignum+99, 40.8, 81.6), r:bignum, c:new vec3(.25,.25,.75)},// 右 | |
| {p:new vec3(50, 40.8, bignum), r:bignum, c:new vec3(.75,.75,.75)},// 奥 | |
| {p:new vec3(50, bignum, 81.6), r:bignum, c:new vec3(.75,.75,.75)},// 上 | |
| {p:new vec3(50, -bignum+81.6, 81.6), r:bignum, c:new vec3(.75,.75,.75)},// 下 | |
| {p:new vec3(27, 16.5, 47), r:16.5, c:new vec3(.99,.99,.99)},// 左玉 | |
| {p:new vec3(73, 16.5, 78), r:16.5, c:new vec3(.99,.99,.99)},// 右玉 | |
| {p:new vec3(50, 681.6-.27, 81.6), r:600, c:new vec3(.99,.99,.99),l:new vec3(12)}, | |
| ]}; | |
| // Hit | |
| // t:レイの原点から交差した点までの距離, sphere:当たった球 | |
| // p:当たった点 n:当たった点pでの法線 | |
| function intersect(scene, ray, tmin, tmax){// tmin,tmax:レイの存在範囲 | |
| var n, c,minh; | |
| for(i=0;i<scene.spheres.length;i++){ | |
| sph=scene.spheres[i]; // current sphere. | |
| var hit=sintersect(sph, ray,tmin,tmax); | |
| if(hit==null){continue;} | |
| minh=hit; | |
| tmax=minh; | |
| n=divs(sub(add(ray.o,muls(ray.d,hit)), sph.p), sph.r); | |
| c=sph.c; | |
| } | |
| return {t:minh,n:n,c:c}; | |
| } | |
| function sintersect(sph, ray,tmin,tmax){ | |
| var op =sub(sph.p, ray.o); // op=球の中心-視点 | |
| var b =dot(op, ray.d); // b=視線と光線のdot。 | |
| var det=b*b-dot(op,op)+sph.r*sph.r; | |
| if(det<0)return null; | |
| var t1=b-Math.sqrt(det); | |
| if(tmin<t1&&t1<tmax) { return t1; } | |
| var t2=b+Math.sqrt(det); | |
| if(tmin<t2&&t2<tmax) { return t2; } | |
| return null; | |
| } | |
| // Uint8Arrayのabufを設定する。 | |
| function main(){ | |
| var r,g,b,a,x,y; | |
| // var scene=scene1, rayf=getRay1; | |
| var scene=scene2, rayf=getRay2; | |
| var abuf=new Uint8Array(width*height*4); | |
| for(y=0; y<height; y++)for(x=0; x<width; x++){ | |
| var rpx=2*x/width-1,rpy=2*y/height-1; | |
| var ray=rayf(rpx, rpy); | |
| var hit=intersect(scene, ray,0,1e+10); | |
| r=0,g=0,b=0,a=0xff; | |
| if(hit!=null&&hit.c!=null&&hit.n!=null){ | |
| var c=muls(hit.c, dot(hit.n, neg(ray.d))); | |
| if(x==80&&y==128)console.log(`${c.x}`); | |
| r=tonemap(Math.abs(c.x)), g=tonemap(Math.abs(c.y)), b=tonemap(Math.abs(c.z)); | |
| } | |
| // バッファに命中判定結果の色の値をセット。 | |
| abuf.set([r,g,b,a], (y*width+x)*4+0); | |
| } | |
| return abuf; | |
| } | |
| // ray=(x, y, z=5):(x,y:-1to1)から-z方向への線の集合 | |
| function getRay1(rpx, rpy){ | |
| var ray={o:new vec3(rpx, rpy, 5), d:new vec3(0,0,-1)}; | |
| return ray; | |
| } | |
| // for cornel's box | |
| function getRay2(rpx,rpy){ | |
| var eye=new vec3(50,52,295.6), center=add(eye, new vec3(0,-.042612,-1)); | |
| var up=new vec3(0,1,0); | |
| var fov=30*Math.PI/180, aspect=width/height; | |
| var wE=nor(sub(eye,center)), uE=nor(crs(up,wE)), vE=crs(wE,uE); | |
| var tf=Math.tan(fov*.5); | |
| var wo=nor(new vec3(aspect*tf*rpx, tf*rpy, -1)); | |
| let ray={ | |
| o:eye, | |
| d:add(add(muls(uE,wo.x),muls(vE,wo.y)),muls(wE,wo.z)) | |
| } | |
| return ray; | |
| } | |
| // 描画。 | |
| function draw(){ | |
| var abuf=main(); | |
| // Uint8Arrayのabufを画面に表示する。 | |
| var cx=CNVS.getContext("webgl");cx.enable(cx.SCISSOR_TEST); | |
| function doNoGLSL(){ | |
| var r,g,b,a,x,y; | |
| for(y=0; y<height; y++)for(x=0; x<width; x++){ | |
| cx.scissor(x,y, 1,1); | |
| r=abuf[(y*width+x)*4+0]/0xff;g=abuf[(y*width+x)*4+1]/0xff; | |
| b=abuf[(y*width+x)*4+2]/0xff;a=abuf[(y*width+x)*4+3]/0xff; | |
| cx.clearColor(r,g,b,1);cx.clear(0x4000); | |
| } | |
| } | |
| doNoGLSL(cx); | |
| } | |
| SLID.value=22; | |
| draw(); | |
| SLID.addEventListener("input", draw); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment