Skip to content

Instantly share code, notes, and snippets.

@Akiyah
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Akiyah/1ddf2fac50f4bcebea45 to your computer and use it in GitHub Desktop.
Save Akiyah/1ddf2fac50f4bcebea45 to your computer and use it in GitHub Desktop.
forked: レイトレーシング練習
todo:
* -影をぼんやりさせる-
* 押しているモノも表示する
* へこみ関数の最適化(押した感じにする)
* シワをつける(丸くする)
* -倍精度で計算する-
html {
height:100%;
}
body {
background: gray;
width:100%;
height:100%;
overflow : hidden;
}
<span class="building">準備中</span><br/>
<img id="image" src="http://jsrun.it/assets/z/M/0/K/zM0Kl.jpg" />
<br/>
<div id="shadow_div" style="position:absolute; display:none; border:solid 0px red; overflow:hidden; width:200px; height:200px;">
<canvas id="shadow" style="width:200px; height:2000px;" width="200px" height="2000px"></canvas>
</div>
var HOLE_SIZE = 50/2;
var HOLE_DEPTH = 50*2;
var SHADOW_BOX_SIZE = 100;
var light = [-1,1,-1];
function innerProduct(p, q) {
return p[0]*q[0] + p[1]*q[1] + p[2]*q[2];
}
function multi(p, a) {
return [p[0]*a, p[1]*a, p[2]*a];
}
function norm(p) {
return Math.sqrt(innerProduct(p, p));
}
function cos(v, w) {
return innerProduct(v, w)/norm(v)/norm(w);
}
function diffuseReflection(normal, light) {
return Math.max(cos(multi(light, -1), normal), 0);
}
function atan(x, y) { // 0 ~ Math.PI * 2
var c;
if (x == 0) {
c = Math.PI / 2;
} else if (x > 0) {
if (y > 0) {
c = Math.atan(y/x);
} else {
c = Math.atan(y/x) + Math.PI * 2;
}
} else {
c = Math.atan(y/x) + Math.PI;
}
//if (c < 0 || Math.PI * 2 < c) {console.log(x, y, c);}
return c;
}
// t = 0 .. 9
function fxy(x, y, t) {
var z;
var r = Math.sqrt(x*x + y*y);
var l = HOLE_SIZE * (t + 1) / 10;
var l3 = l/3;
var d = HOLE_DEPTH * (t + 1) / 10;
if (l < r) {
z = 0;
}
if (l3 < r) {
z = -l*l3/r + l3;
} else {
z = -(l - l3) - Math.sqrt(l3*l3 - r*r);
}
// シワ
var c = atan(x, y);
while (Math.PI * 2 / 5 < c) {
c -= Math.PI * 2 / 5;
}
c = Math.abs(c - Math.PI * 2 / 5 / 2);
if (c < Math.PI * 2 / 5 / 2 / 2) {
z += (Math.PI * 2 / 5 / 2 / 2 - c) * 10;
}
return z;
}
function updateData(data, x, y, value) {
var l = SHADOW_BOX_SIZE;
var x1 = x + l; // 右が大きくなる方向
var y1 = y + l; // 上が大きくなる方向
var i = (y1 * l*2 + x1) * 4;
if (x1 < 0 || l*2 <= x1) { return; }
if (y1 < 0 || l*2 <= y1) { return; }
var d = 255*2/3/2;
if (value-0.5 < 0) {
data[i+0] = 0;
data[i+1] = 0;
data[i+2] = 0;
data[i+3] = -(value-0.5)*d*2;
} else {
data[i+0] = 255;
data[i+1] = 255;
data[i+2] = 255;
data[i+3] = (value-0.5)*d*2;
}
}
var c10 = diffuseReflection([0,0,1], light);
function calcShadowValue(x, y, z, t) {
function shadowValue(d) {
var z1 = fxy(x - d*light[0], y - d*light[1], t);
var z2 = z - d*light[2];
return z2 - z1; // z2(light直線上) より z1(fxy表面上) が大きい(こちら側)なら影(s<0)になる
}
var s = 1;
for (var d = 1; d < SHADOW_BOX_SIZE; d += 1) {
s = Math.min(s, shadowValue(d / Math.abs(light[0]))); // x方向
s = Math.min(s, shadowValue(d / Math.abs(light[1]))); // y方向
}
return s;
}
function calcLightValue(x, y, t) {
var z = fxy(x, y, t);
var s = calcShadowValue(x, y, z, t);
var c = (s <= 0) ? 0 : 1;
var d = 0.1;
var dz_x = fxy(x+d,y,t) - fxy(x-d,y,t);
var dz_y = fxy(x,y+d,t) - fxy(x,y-d,t);
var normal = [-dz_x, -dz_y, d*2];
var c1 = diffuseReflection(normal, light) / c10; // 0 ~ 1(平らな面) ~ ?(一番明るい面。2程度を想定している)
return c*c1 - 0.5;
}
function draw_light_shadow(canvas, context) {
var l = SHADOW_BOX_SIZE;
var imageData = context.createImageData(l*2, l*2);
var data = imageData.data;
for (var t = 0; t < 1; t += 1) {
for (var y = -l; y < l; y += 1) {
for (var x = -l; x < l; x += 1) {
var c1 = calcLightValue(x, y, t);
var c2 = c1;//calcLightValue(x + .5, y, t);
var c3 = c1;//calcLightValue(x, y + .5, t);
var c4 = c1;//calcLightValue(x + .5, y + .5, t);
updateData(data, x, y, (c1+c2+c3+c4)/4);
}
}
context.putImageData(imageData, 0, t*l*2);
}
}
$(function() {
var canvas = $('#shadow').get(0);
var context = canvas.getContext("2d");
var touching = false;
var time = 0;
var x = 0;
var y = 0;
draw_light_shadow(canvas, context);
$(".building").hide();
var r = SHADOW_BOX_SIZE;
$("#image, #shadow_div").mousedown(function(e){
touching = true;
x = e.pageX;
y = e.pageY;
});
$("#image, #shadow_div").mousemove(function(e){
x = e.pageX;
y = e.pageY;
});
$("#image, #shadow_div").mouseup(function(){
touching = false;
});
setInterval(function(){
if (touching) {
time++;
time = Math.min(time, 9);
} else {
time--;
time = Math.max(time, -1);
}
if (touching || 0 <= time) {
$("#shadow_div").css({left:x-r, top:y-r});
$("#shadow").css({marginTop:-time*2*r});
$("#shadow_div").show();
} else {
$("#shadow_div").hide();
}
},10);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment