Created
October 21, 2018 04:14
-
-
Save diska/9cbc2c5c28f08236da395786b9346da8 to your computer and use it in GitHub Desktop.
三葉レイ動画rt01の33:21時点(最終)相当のコードのGLSL化(Firefoxで軽く、Vivaldiで重い)。
This file contains 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/> | |
<input id="SPP" type="range" min="0" max="8" value="2"><input id="SPPT"><br/> | |
<input id="DPP" type="range" min="1" max="7" value="2"><input id="DPPT"><br/> | |
<canvas id="CNVS"></canvas><hr/> | |
<script> | |
const vsrc=`attribute vec4 p;void main(){gl_Position=p;}`; | |
const fsrc0c=`precision highp float; | |
struct Ray{ vec3 o, d;}; | |
struct Sph{ vec3 o; float r; vec3 ref; vec3 le; int m;float ior;}; | |
const float PI=3.14; | |
const float fov=30.*PI/180.; | |
uniform Sph sphs[8]; | |
// camera parameters for Cornell Box scene | |
uniform vec3 eye, center, up; | |
uniform int uIter; | |
uniform int uDepth; | |
uniform float uSeed; | |
vec3 tonemap(vec3 c){ | |
return clamp(pow(c, vec3(1./2.2)), 0.,1.); | |
} | |
float rand(vec2 st){ | |
return fract(sin(dot(st.xy, vec2(1234,5678))+uSeed)*1234.5678); | |
} | |
void tangentSpace(in vec3 n, out vec3 ps, out vec3 pt) { | |
float s= 1.*sign(n.z); | |
float a=-1./(s + n.z); | |
float b=n.x*n.y*a; | |
ps=vec3(1.+s*n.x*n.x*a, s*b, -s*n.x); | |
pt=vec3(b, s+n.y*n.y*a, -n.y); | |
return; | |
} | |
// basis vectors for camera coordinates | |
mat3 getUvw(){ | |
vec3 wE=normalize(eye-center); | |
vec3 uE=normalize(cross(up, wE)); | |
vec3 vE=cross(wE, uE); | |
return mat3(uE,vE,wE); | |
} | |
const float tf=tan(fov*.5); | |
`; | |
const fsrc1=fsrc0c+` | |
float intersect(Sph sph, Ray ray, float tmin, float tmax){ | |
vec3 op=sph.o-ray.o; | |
float bo=dot(op, ray.d); | |
float det=bo*bo-dot(op,op)+sph.r*sph.r; | |
if(det<0.){return -1.;} | |
float t1=bo-sqrt(det); if(tmin<t1&&t1<tmax){return t1;} | |
float t2=bo+sqrt(det); if(tmin<t2&&t2<tmax){return t2;} | |
return -1.; | |
} | |
Ray mode0(Ray ray, Sph sph, vec3 n, vec3 p, vec2 rp, int depth){ | |
ray.o=p; | |
float sd=float(depth)/11.11; | |
vec3 na=dot(n, -ray.d)>0.?n:-n; | |
vec3 u,v; tangentSpace(na, u, v); | |
float r=sqrt(rand(rp+vec2(sd,tan(sd)))); | |
float ta=(2.*PI)*rand(rp+vec2(sd,tan(sd)+.1)); | |
float xa=r*cos(ta), ya=r*sin(ta); | |
vec3 d=vec3(xa, ya, sqrt(max(.0, 1.-xa*xa-ya*ya))); | |
ray.d=u*d.x+v*d.y+na*d.z; | |
return ray; | |
} | |
Ray mode1(Ray ray, Sph sph, vec3 n, vec3 p){ | |
ray.o=p; | |
// ray.d=reflect(ray.d, n); | |
vec3 wi=-ray.d; | |
ray.d=2.*dot(wi, n)*n-wi; | |
return ray; | |
} | |
Ray mode2(Ray ray, Sph sph, vec3 n, vec3 p){ | |
ray.o=p; | |
vec3 wi=-ray.d; | |
bool into=dot(wi, n)>0.; | |
vec3 na=into?n:-n; | |
float ior=sph.ior; | |
float eta=into?1./ior:ior; | |
// ray.d=-refract(ray.d, na, eta); | |
float t=dot(wi, na); | |
float t2=1.-eta*eta*(1.-t*t); | |
if(t2<0.){ | |
// Total internal reflection | |
ray.d=2.*dot(wi, n)*n-wi; | |
}else{ | |
vec3 wt=eta*(na*t-wi)-n*sqrt(t2); | |
// Schlick's approximation | |
float cos=into?dot(wi, n):dot(wt, n); | |
float r=(1.-ior)/(1.+ior); | |
float Fr=r*r+(1.-r*r)*pow(1.-cos,5.); | |
ray.d=(rand(vec2(.1))<Fr)?(2.*dot(wi, n)*n-wi):wt; | |
} | |
return ray; | |
} | |
void main(){ | |
const float width=800., height=600.; | |
const float aspect=width/height; | |
int spp=(uIter==0)?4:uIter; | |
int dpp=(uDepth==0)?2:uDepth; | |
vec2 rpxy=vec2(gl_FragCoord.x/width, gl_FragCoord.y/height)*2.-1.; | |
vec3 I=vec3(0); | |
Sph sph; | |
for(int j=0; j<512; j++){ | |
if(j>=spp)break; | |
vec2 rp=rpxy; | |
rp+=vec2(rand(vec2(j))/width,rand(vec2(j))/height); | |
vec3 wo=normalize(vec3(aspect*tf*rp.x, tf*rp.y, -1)); | |
Ray ray; ray.o=eye; ray.d=getUvw()*wo; | |
vec3 L=vec3(0), th=vec3(1); | |
for(int depth=0; depth<10; depth++){ | |
if(depth>=dpp)break; | |
float minh=1e6; | |
for(int i=0; i<8; i++){ | |
float h=intersect(sphs[i], ray, 1e-1, minh); | |
if(h<0.)continue; // ハズレ。次。 | |
sph=sphs[i]; // 今見てる球 | |
minh=h; // が一番近い。 | |
} | |
if(minh>=1e6){break;} // 全部ハズレ。次。 | |
L+=th*sph.le; | |
vec3 p=ray.o+ray.d*minh; // 球上の交点 | |
vec3 n=(p-sph.o)/sph.r; // 球上の法線 | |
if(sph.m==0){ | |
ray=mode0(ray, sph, n, p, rp,depth); | |
th*=sph.ref; | |
if(max(max(th.x, th.y),th.z)==0.)break; | |
} | |
else if(sph.m==1){ | |
ray=mode1(ray, sph, n, p); | |
}else if(sph.m==2){ | |
ray=mode2(ray, sph, n, p); | |
} | |
} | |
I+=L/float(spp); | |
} | |
gl_FragColor=vec4(tonemap(abs(I)),1); | |
}`; | |
// ポリゴン描画するだけであとの処理はFragmentShaderに任せる。 | |
var cx, pg; | |
const width=800, height=600; // Image size | |
function getCx(){ | |
CNVS.width=width, CNVS.height=height; | |
cx=CNVS.getContext("webgl"); | |
var vs=cx.createShader(cx.VERTEX_SHADER);cx.shaderSource(vs,vsrc); | |
var fs=cx.createShader(cx.FRAGMENT_SHADER);cx.shaderSource(fs,fsrc1); | |
pg=cx.createProgram();cx.attachShader(pg,vs);cx.attachShader(pg,fs); | |
cx.compileShader(vs);cx.compileShader(fs);cx.linkProgram(pg); | |
console.log(`vs:${cx.getShaderInfoLog(vs)}\n`); | |
console.log(`fs:${cx.getShaderInfoLog(fs)}\n`); | |
console.log(`pg:${cx.getProgramInfoLog(pg)}\n`); | |
cx.enableVertexAttribArray(0); | |
var abuf=cx.createBuffer(cx.ARRAY_BUFFER); | |
var data=new Float32Array([-1,-1, 1,-1, -1,1, 1,1]); | |
cx.bindBuffer(cx.ARRAY_BUFFER,abuf); | |
cx.bufferData(cx.ARRAY_BUFFER, data, cx.STATIC_DRAW); | |
cx.vertexAttribPointer(0, 2,cx.FLOAT, false, 0,0); | |
cx.bindBuffer(cx.ARRAY_BUFFER,null); | |
cx.useProgram(pg); | |
}; getCx(); | |
function setScene(cx, pg){// Cornell Box scene | |
// struct Sph{ vec3 o; float r; vec3 ref; vec3 le; int m;float ior;}; | |
const bnum=1e5; // =1e5; | |
var sphs={}, F=3+1+3+3+1+1; | |
const dat=([ | |
bnum+1., 40.8, 81.6, bnum, .75,.25,.25, 0,0,0, 0,0, | |
-bnum+99., 40.8, 81.6, bnum, .25,.25,.75, 0,0,0, 0,0, | |
50., 40.8, bnum, bnum, .75,.75,.75, 0,0,0, 0,0, | |
50., bnum, 81.6, bnum, .75,.75,.75, 0,0,0, 0,0, | |
50., -bnum+81.6, 81.6, bnum, .75,.75,.75, 0,0,0, 0,0, | |
27., 16.5, 47., 16.5, .99,.99,.99, 0,0,0, 0,0, | |
73., 16.5, 78., 16.5, .99,.99,.99, 0,0,0, 0,1.5168, | |
50., 681.6-.27, 81.6, 600, .0,.0,.0, 12,12,12, 0,0, | |
]); | |
for(i=0; i<8; i++){ | |
sphs.o=cx.getUniformLocation(pg, `sphs[${i}].o`); | |
cx.uniform3f(sphs.o, dat[i*F+0], dat[i*F+1], dat[i*F+2]); | |
sphs.r=cx.getUniformLocation(pg, `sphs[${i}].r`); | |
cx.uniform1f(sphs.r, dat[i*F+3]); | |
sphs.ref=cx.getUniformLocation(pg, `sphs[${i}].ref`); | |
cx.uniform3f(sphs.ref, dat[i*F+4], dat[i*F+5], dat[i*F+6]); | |
sphs.le=cx.getUniformLocation(pg, `sphs[${i}].le`); | |
cx.uniform3f(sphs.le, dat[i*F+7], dat[i*F+8], dat[i*F+9]); | |
sphs.m=cx.getUniformLocation(pg, `sphs[${i}].m`); | |
cx.uniform1i(sphs.m, dat[i*F+10]); | |
sphs.ior=cx.getUniformLocation(pg, `sphs[${i}].ior`); | |
cx.uniform1f(sphs.ior, dat[i*F+11]); | |
} | |
var view={}; | |
view.eye=cx.getUniformLocation(pg, "eye"); | |
cx.uniform3f(view.eye, 50.,52.,295.6); | |
view.center=cx.getUniformLocation(pg, "center"); | |
cx.uniform3f(view.center, 50.+0., 52.-0.042612, 295.6-1.); | |
view.up=cx.getUniformLocation(pg, "up"); | |
cx.uniform3f(view.up, 0,1,0); | |
} | |
setScene(cx, pg); | |
function setParam(cx,pg){ | |
SPPT.value=`spp=${Math.pow(2,SPP.value)}`; | |
uIter=cx.getUniformLocation(pg, "uIter"); | |
cx.uniform1i(uIter, Math.pow(2,SPP.value)); | |
DPPT.value=`dpp=${DPP.value}`; | |
uDepth=cx.getUniformLocation(pg, "uDepth"); | |
cx.uniform1i(uDepth, parseInt(DPP.value)); | |
// uSeed=cx.getUniformLocation(pg, "uSeed"); | |
// cx.uniform1f(uSeed, Math.random()); | |
} | |
function draw(){ | |
setParam(cx,pg); | |
// cx.clearColor(.5,.5,.5,1); | |
// cx.clear(0x4000); | |
cx.drawArrays(cx.TRIANGLE_STRIP,0,4); | |
// requestAnimationFrame(draw); | |
}; draw(); | |
SPP.addEventListener("input", draw); | |
DPP.addEventListener("input", draw); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment