Skip to content

Instantly share code, notes, and snippets.

@diska
Last active August 20, 2018 07:56
Show Gist options
  • Select an option

  • Save diska/5b64e712e9df7c35702c2bf8784c94fa to your computer and use it in GitHub Desktop.

Select an option

Save diska/5b64e712e9df7c35702c2bf8784c94fa to your computer and use it in GitHub Desktop.
三葉レイ動画の最初のやつのWebGL実装。途中。暫定。
<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