Skip to content

Instantly share code, notes, and snippets.

@diska
Created August 21, 2018 15:13
Show Gist options
  • Save diska/cbd01ce8f7f88e6a4b0abe29fdd08fbf to your computer and use it in GitHub Desktop.
Save diska/cbd01ce8f7f88e6a4b0abe29fdd08fbf to your computer and use it in GitHub Desktop.
三葉レイ動画の25:10時点のコードのGLSL化。
<meta charset="utf-8"><br/>
<canvas id="CNVS"></canvas><hr/>
<input id="CAM" type="range" step="0.02" min="0" max="6.3">
<script>
const vsrc=`attribute vec4 p;void main(){gl_Position=p;}`;
const fsrc=`precision mediump float;
struct Ray{ vec3 o, d;};
struct Sph{ vec3 o; float r; vec3 ref; vec3 le;};
const float width=120., height=80.;
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.;
}
vec3 tonemap(vec3 c){
float r=pow(c.r, 1./2.2);
float g=pow(c.g, 1./2.2);
float b=pow(c.b, 1./2.2);
return vec3(r,g,b);
}
void getSphs1(out Sph[2] sphs){// "2 spheres" scene
sphs[0].o=vec3( .5,0,0), sphs[0].r=1., sphs[0].ref=vec3(1,0,0);
sphs[1].o=vec3(-.5,0,0), sphs[1].r=1., sphs[1].ref=vec3(0,1,0);
}
void getSphs2(out Sph[8] sphs){// Cornell Box scene
const float bnum=1e5;
sphs[0].o=vec3( bnum+1., 40.8, 81.6), sphs[0].r=bnum, sphs[0].ref=vec3(.75,.25,.25);
sphs[1].o=vec3(-bnum+99., 40.8, 81.6), sphs[1].r=bnum, sphs[1].ref=vec3(.25,.25,.75);
sphs[2].o=vec3(50., 40.8, bnum), sphs[2].r=bnum, sphs[2].ref=vec3(.75);
sphs[3].o=vec3(50., bnum, 81.6), sphs[3].r=bnum, sphs[3].ref=vec3(.75);
sphs[4].o=vec3(50., -bnum+81.6, 81.6), sphs[4].r=bnum, sphs[4].ref=vec3(.75);
sphs[5].o=vec3(27., 16.5, 47.), sphs[5].r=16.5, sphs[5].ref=vec3(.999);
sphs[6].o=vec3(73., 16.5, 78.), sphs[6].r=16.5, sphs[6].ref=vec3(.999);
sphs[7].o=vec3(50., 681.6-.27, 81.6), sphs[7].r=600., sphs[7].ref=vec3(0);
sphs[7].le=vec3(12);
}
uniform vec3 uDir;
void main(){
const float PI=3.141592;
float fov=30.*PI/180., aspect=width/height;
// camera parameters for "2 spheres" scene
// vec3 eye=vec3(5,5,5), center=vec3(0,0,0);
// camera parameters for Cornell Box scene
// vec3 eye=vec3(50.,52.,295.6), center=eye+vec3(0.,-0.042612,-1.);
vec3 eye=vec3(50.,52.,295.6), center=eye+uDir;
vec3 up=vec3(0,1,0);
// basis vectors for camera coordinates
vec3 wE=normalize(eye-center);
vec3 uE=normalize(cross(up, wE));
vec3 vE=cross(wE, uE);
//
float tf=tan(fov*.5);
vec2 rp=vec2(2.*gl_FragCoord.x/width-1., 2.*gl_FragCoord.y/height-1.);
vec3 wo=normalize(vec3(aspect*tf*rp.x, tf*rp.y, -1));
Ray ray;
// ray.o=vec3(vec2(rp), 5); ray.d=vec3(0,0,-1);
ray.o=eye;
ray.d=uE*wo.x+vE*wo.y+wE*wo.z;
//
vec3 color=vec3(1,0,1);
Sph sph;
// Sph sphs[2]; getSphs1(sphs);
Sph sphs[8]; getSphs2(sphs);
float h, minh=-1., tmax=1000.;
for(int i=0; i<8; i++){
h=intersect(sphs[i], ray, 0., tmax);
if(h<0.){continue;}
sph=sphs[i];
minh=h;
tmax=h;
}
vec3 p, n, c;
if(minh>0.){
p=ray.o+ray.d*minh; // 交点
n=(p-sph.o)/sph.r; // 法線
c=sph.ref; // 球の色
}
if(minh>0.){
c*=dot(n, -ray.d); // カメラ向いてる面はよく反射する。
gl_FragColor=vec4(color,1);
gl_FragColor=vec4(tonemap(abs(c)),1);
}else{
gl_FragColor=vec4(0,0,0,1);
}
}`;
// ポリゴン描画するだけであとの処理はFragmentShaderに任せる。
var cx, uDir;
function draw(){
const width=120, height=80; // Image size
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,fsrc);
var 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)}\nfs:${cx.getShaderInfoLog(fs)}\n`);
console.log(`pg:${cx.getProgramInfoLog(pg)}\n`);
cx.enableVertexAttribArray(0);
uDir=cx.getUniformLocation(pg, "uDir");
cx.useProgram(pg);
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);
}; draw();
function update(){
var x=0,y=-0.042612,z=-1;
x=Math.sin(CAM.value), z=Math.cos(CAM.value);
cx.uniform3fv(uDir, [x,y,z]);
cx.drawArrays(cx.TRIANGLE_STRIP,0,4);
} update();
CAM.addEventListener("input", update);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment