// 視点
PVector eyePos = new PVector(0, 0, -5);
// 物体
ArrayList<Shape> shapes = new ArrayList<>() {{
add(new Sphere(new PVector( 3, 0, 25), 1));
add(new Sphere(new PVector( 2, 0, 20), 1));
add(new Sphere(new PVector( 1, 0, 15), 1));
add(new Sphere(new PVector( 0, 0, 10), 1));
add(new Sphere(new PVector(-1, 0, 5), 1));
add(new Plane(new PVector(0, 1, 0), new PVector(0, -1, 0)));
}};
// 光源
PointLightSource lightSource = new PointLightSource() {{
position = new PVector(-5,5,-5);
}};
// 環境光強度
FColor ambientIntensity = new FColor(0.10, 0.10, 0.10);
// 環境光反射係数
FColor kAmb = new FColor(0.01, 0.01, 0.01);
// 拡散反射係数
FColor kDif = new FColor(0.69, 0.00, 0.00);
// 鏡面反射係数
FColor kSpe = new FColor(0.30, 0.30, 0.30);
// 光沢度
float shininess = 8;
void setup()
{
size(512, 512);
background(color(0, 0, 0));
noSmooth();
noLoop();
}// void setup()
void draw()
{
PVector pw = new PVector();
pw.z = 0;
for (int y = 0; y < height; ++y)
{
pw.y = map(y, 0, height-1, 1, -1);
for (int x = 0; x < width; ++x)
{
pw.x = map(x, 0, width-1, -1, 1);
// レイ
Ray eyeRay = new Ray(eyePos, PVector.sub(pw, eyePos));
// レイ上にある最近傍の物体を調べる
Shape nearestShape = null;
IntersectionPoint nearestIp = null;
for (Shape sh : shapes)
{
// レイと物体との交点を計算する
IntersectionPoint ip = sh.testIntersection(eyeRay);
// レイ上に物体がない場合
if (ip == null)
{
// 次の物体へ
continue;
}
// 初めて発見された物体である場合
if (nearestShape == null)
{
// 最近傍の物体として更新
nearestShape = sh;
nearestIp = ip;
// 次の物体へ
continue;
}
// 最近傍の物体ではなかった場合
if (ip.distance > nearestIp.distance)
{
// 次の物体へ
continue;
}
// 最近傍の物体として更新
nearestShape = sh;
nearestIp = ip;
}// for
// レイ上に物体がない場合
if (nearestShape == null)
{
// 背景色をプロットする
stroke(color(100,149, 237));
point(x, y);
// 次の (x, y) へ
continue;
}
// 放射輝度
FColor col = new FColor();
// 環境光の反射光の放射輝度を加算する
col.add(colorPi(kAmb, ambientIntensity));
// 法線ベクトルと入射ベクトルの内積を計算する.
Lighting lighting = lightSource.lightingAt(nearestIp.position);
float nlDot = constrain(nearestIp.normal.dot(lighting.direction), 0, 1);
// 拡散反射光の放射輝度を加算する
col.add(colorPi(kDif, lightSource.intensity, new FColor(nlDot)));
// 鏡面反射が無い場合
if (nlDot <= 0)
{
// プロットする
stroke(col.toColor());
point(x, y);
// 次の (x, y) へ
continue;
}
// 視線ベクトルの逆ベクトルと正反射ベクトルの内積を計算する
PVector invEyeDir = vectorSigma(-1, eyeRay.direction).normalize();
PVector refDir = vectorSigma(2 * nlDot, nearestIp.normal, -1, lighting.direction);
float vrDot = constrain(invEyeDir.dot(refDir), 0, 1);
// 鏡面反射光の放射輝度を加算する
col.add(colorPi(kSpe, lightSource.intensity, new FColor(pow(vrDot, shininess))));
// プロットする
stroke(col.toColor());
point(x, y);
}//for
}//for
}// void draw()
Ci8vIOimlueCuQpQVmVjdG9yIGV5ZVBvcyA9IG5ldyBQVmVjdG9yKDAsIDAsIC01KTsKCi8vIOeJqeS9kwpBcnJheUxpc3Q8U2hhcGU+IHNoYXBlcyA9IG5ldyBBcnJheUxpc3Q8PigpIHt7CiAgYWRkKG5ldyBTcGhlcmUobmV3IFBWZWN0b3IoIDMsIDAsIDI1KSwgMSkpOwogIGFkZChuZXcgU3BoZXJlKG5ldyBQVmVjdG9yKCAyLCAwLCAyMCksIDEpKTsKICBhZGQobmV3IFNwaGVyZShuZXcgUFZlY3RvciggMSwgMCwgMTUpLCAxKSk7CiAgYWRkKG5ldyBTcGhlcmUobmV3IFBWZWN0b3IoIDAsIDAsIDEwKSwgMSkpOwogIGFkZChuZXcgU3BoZXJlKG5ldyBQVmVjdG9yKC0xLCAwLCAgNSksIDEpKTsKICBhZGQobmV3IFBsYW5lKG5ldyBQVmVjdG9yKDAsIDEsIDApLCBuZXcgUFZlY3RvcigwLCAtMSwgMCkpKTsKfX07CgovLyDlhYnmupAKUG9pbnRMaWdodFNvdXJjZSBsaWdodFNvdXJjZSA9IG5ldyBQb2ludExpZ2h0U291cmNlKCkge3sKICBwb3NpdGlvbiA9IG5ldyBQVmVjdG9yKC01LDUsLTUpOwp9fTsKCi8vIOeSsOWig+WFieW8t+W6pgpGQ29sb3IgYW1iaWVudEludGVuc2l0eSA9IG5ldyBGQ29sb3IoMC4xMCwgMC4xMCwgMC4xMCk7CgovLyDnkrDlooPlhYnlj43lsITkv4LmlbAKRkNvbG9yIGtBbWIgPSBuZXcgRkNvbG9yKDAuMDEsIDAuMDEsIDAuMDEpOwovLyDmi6HmlaPlj43lsITkv4LmlbAKRkNvbG9yIGtEaWYgPSBuZXcgRkNvbG9yKDAuNjksIDAuMDAsIDAuMDApOwovLyDpj6HpnaLlj43lsITkv4LmlbAKRkNvbG9yIGtTcGUgPSBuZXcgRkNvbG9yKDAuMzAsIDAuMzAsIDAuMzApOwovLyDlhYnmsqLluqYKZmxvYXQgc2hpbmluZXNzID0gODsKCnZvaWQgc2V0dXAoKQp7CiAgc2l6ZSg1MTIsIDUxMik7CiAgYmFja2dyb3VuZChjb2xvcigwLCAwLCAwKSk7CgogIG5vU21vb3RoKCk7CiAgbm9Mb29wKCk7Cn0vLyB2b2lkIHNldHVwKCkKCnZvaWQgZHJhdygpCnsKICBQVmVjdG9yIHB3ID0gbmV3IFBWZWN0b3IoKTsKICBwdy56ID0gMDsKCiAgZm9yIChpbnQgeSA9IDA7IHkgPCBoZWlnaHQ7ICsreSkKICB7CiAgICBwdy55ID0gbWFwKHksIDAsIGhlaWdodC0xLCAxLCAtMSk7CiAgICBmb3IgKGludCB4ID0gMDsgeCA8IHdpZHRoOyArK3gpCiAgICB7CiAgICAgIHB3LnggPSBtYXAoeCwgMCwgd2lkdGgtMSwgLTEsIDEpOwoKICAgICAgLy8g44Os44KkCiAgICAgIFJheSBleWVSYXkgPSBuZXcgUmF5KGV5ZVBvcywgUFZlY3Rvci5zdWIocHcsIGV5ZVBvcykpOwoKICAgICAgLy8g44Os44Kk5LiK44Gr44GC44KL5pyA6L+R5YKN44Gu54mp5L2T44KS6Kq/44G544KLCiAgICAgIFNoYXBlIG5lYXJlc3RTaGFwZSA9IG51bGw7CiAgICAgIEludGVyc2VjdGlvblBvaW50IG5lYXJlc3RJcCA9IG51bGw7CgogICAgICBmb3IgKFNoYXBlIHNoIDogc2hhcGVzKQogICAgICB7CiAgICAgICAgLy8g44Os44Kk44Go54mp5L2T44Go44Gu5Lqk54K544KS6KiI566X44GZ44KLCiAgICAgICAgSW50ZXJzZWN0aW9uUG9pbnQgaXAgPSBzaC50ZXN0SW50ZXJzZWN0aW9uKGV5ZVJheSk7CgogICAgICAgIC8vIOODrOOCpOS4iuOBq+eJqeS9k+OBjOOBquOBhOWgtOWQiAogICAgICAgIGlmIChpcCA9PSBudWxsKQogICAgICAgIHsKICAgICAgICAgIC8vIOasoeOBrueJqeS9k+OBuAogICAgICAgICAgY29udGludWU7CiAgICAgICAgfQoKICAgICAgICAvLyDliJ3jgoHjgabnmbropovjgZXjgozjgZ/niankvZPjgafjgYLjgovloLTlkIgKICAgICAgICBpZiAobmVhcmVzdFNoYXBlID09IG51bGwpCiAgICAgICAgewogICAgICAgICAgLy8g5pyA6L+R5YKN44Gu54mp5L2T44Go44GX44Gm5pu05pawCiAgICAgICAgICBuZWFyZXN0U2hhcGUgPSBzaDsKICAgICAgICAgIG5lYXJlc3RJcCAgICA9IGlwOwoKICAgICAgICAgIC8vIOasoeOBrueJqeS9k+OBuAogICAgICAgICAgY29udGludWU7CiAgICAgICAgfQoKICAgICAgICAvLyDmnIDov5Hlgo3jga7niankvZPjgafjga/jgarjgYvjgaPjgZ/loLTlkIgKICAgICAgICBpZiAoaXAuZGlzdGFuY2UgPiBuZWFyZXN0SXAuZGlzdGFuY2UpCiAgICAgICAgewogICAgICAgICAgLy8g5qyh44Gu54mp5L2T44G4CiAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CgogICAgICAgIC8vIOacgOi/keWCjeOBrueJqeS9k+OBqOOBl+OBpuabtOaWsAogICAgICAgIG5lYXJlc3RTaGFwZSA9IHNoOwogICAgICAgIG5lYXJlc3RJcCAgICA9IGlwOwoKICAgICAgfS8vIGZvcgoKICAgICAgLy8g44Os44Kk5LiK44Gr54mp5L2T44GM44Gq44GE5aC05ZCICiAgICAgIGlmIChuZWFyZXN0U2hhcGUgPT0gbnVsbCkKICAgICAgewogICAgICAgIC8vIOiDjOaZr+iJsuOCkuODl+ODreODg+ODiOOBmeOCiwogICAgICAgIHN0cm9rZShjb2xvcigxMDAsMTQ5LCAyMzcpKTsKICAgICAgICBwb2ludCh4LCB5KTsKCiAgICAgICAgLy8g5qyh44GuICh4LCB5KSDjgbgKICAgICAgICBjb250aW51ZTsKICAgICAgfQoKICAgICAgLy8g5pS+5bCE6Lyd5bqmCiAgICAgIEZDb2xvciBjb2wgPSBuZXcgRkNvbG9yKCk7CgogICAgICAvLyDnkrDlooPlhYnjga7lj43lsITlhYnjga7mlL7lsITovJ3luqbjgpLliqDnrpfjgZnjgosKICAgICAgY29sLmFkZChjb2xvclBpKGtBbWIsIGFtYmllbnRJbnRlbnNpdHkpKTsKCiAgICAgIC8vIOazlee3muODmeOCr+ODiOODq+OBqOWFpeWwhOODmeOCr+ODiOODq+OBruWGheepjeOCkuioiOeul+OBmeOCi++8jgogICAgICBMaWdodGluZyBsaWdodGluZyA9IGxpZ2h0U291cmNlLmxpZ2h0aW5nQXQobmVhcmVzdElwLnBvc2l0aW9uKTsKICAgICAgZmxvYXQgbmxEb3QgPSBjb25zdHJhaW4obmVhcmVzdElwLm5vcm1hbC5kb3QobGlnaHRpbmcuZGlyZWN0aW9uKSwgMCwgMSk7CgogICAgICAvLyDmi6HmlaPlj43lsITlhYnjga7mlL7lsITovJ3luqbjgpLliqDnrpfjgZnjgosKICAgICAgY29sLmFkZChjb2xvclBpKGtEaWYsIGxpZ2h0U291cmNlLmludGVuc2l0eSwgbmV3IEZDb2xvcihubERvdCkpKTsKCiAgICAgIC8vIOmPoemdouWPjeWwhOOBjOeEoeOBhOWgtOWQiAogICAgICBpZiAobmxEb3QgPD0gMCkKICAgICAgewogICAgICAgIC8vIOODl+ODreODg+ODiOOBmeOCiwogICAgICAgIHN0cm9rZShjb2wudG9Db2xvcigpKTsKICAgICAgICBwb2ludCh4LCB5KTsKCiAgICAgICAgLy8g5qyh44GuICh4LCB5KSDjgbgKICAgICAgICBjb250aW51ZTsKICAgICAgfQoKICAgICAgLy8g6KaW57ea44OZ44Kv44OI44Or44Gu6YCG44OZ44Kv44OI44Or44Go5q2j5Y+N5bCE44OZ44Kv44OI44Or44Gu5YaF56mN44KS6KiI566X44GZ44KLCiAgICAgIFBWZWN0b3IgaW52RXllRGlyID0gdmVjdG9yU2lnbWEoLTEsIGV5ZVJheS5kaXJlY3Rpb24pLm5vcm1hbGl6ZSgpOwogICAgICBQVmVjdG9yIHJlZkRpciA9IHZlY3RvclNpZ21hKDIgKiBubERvdCwgbmVhcmVzdElwLm5vcm1hbCwgLTEsIGxpZ2h0aW5nLmRpcmVjdGlvbik7CiAgICAgIGZsb2F0IHZyRG90ID0gY29uc3RyYWluKGludkV5ZURpci5kb3QocmVmRGlyKSwgMCwgMSk7CgogICAgICAvLyDpj6HpnaLlj43lsITlhYnjga7mlL7lsITovJ3luqbjgpLliqDnrpfjgZnjgosKICAgICAgY29sLmFkZChjb2xvclBpKGtTcGUsIGxpZ2h0U291cmNlLmludGVuc2l0eSwgbmV3IEZDb2xvcihwb3codnJEb3QsIHNoaW5pbmVzcykpKSk7CgogICAgICAvLyDjg5fjg63jg4Pjg4jjgZnjgosKICAgICAgc3Ryb2tlKGNvbC50b0NvbG9yKCkpOwogICAgICBwb2ludCh4LCB5KTsKCiAgICB9Ly9mb3IKICB9Ly9mb3IKfS8vIHZvaWQgZHJhdygpCg==