3Dの基本的な知識を理解したくて、『3次元CGの基礎と応用[新訂版]( http://goo.gl/GKoQ4y )』という書籍を購入しました。薄くて読みやすそうな本なのですがサンプルソースがないので、勉強がてらサンプルを作りながら読み進めようと思います。誰でもすぐに追試ができるよう、HTML + Javascriptでサンプルを作っていきます。
完全なサンプルはこちら
https://github.com/nakaken0629/3dstudy
2.3. 3角形面を描く
一番基本的なポリゴンである3角形面を、2.1.で使った点を描く処理を使って描く処理です。2.2.で使ったブレゼンハムのアルゴリズムを使う方法もあるのですが、それはそれなりに後で置き換えられるだろうということで、今回は実数が出てくる割り算を使った素朴な処理で実装しています。実数が絡む誤差のせいか、描画されたポリゴンは少し透明な場所も出ています。
サンプルコード
session2_3.htmlより
/* 共通処理 */
var canvas;
var ctx;
window.onload = function() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
main();
};
var createRgb = function(red, green, blue) {
return "#"
+ ("0" + red.toString(16)).substr(-2)
+ ("0" + green.toString(16)).substr(-2)
+ ("0" + blue.toString(16)).substr(-2)
;
};
var getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
/* 個別処理 */
var main = function() {
for(var i = 0; i < 5; i++) {
x1 = getRandomInt(0, 319);
y1 = getRandomInt(0, 199);
x2 = getRandomInt(0, 319);
y2 = getRandomInt(0, 199);
x3 = getRandomInt(0, 319);
y3 = getRandomInt(0, 199);
red = getRandomInt(0, 255);
green = getRandomInt(0, 255);
blue = getRandomInt(0, 255);
fillTriangle(x1, y1, x2, y2, x3, y3, createRgb(red, green, blue));
}
};
var pset = function(x, y, rgb) {
/* 1pxでは見づらいため、2pxとしている */
ctx.fillStyle = rgb;
ctx.fillRect(x * 2, y * 2, 2, 2);
};
var fillTriangle = function(x1, y1, x2, y2, x3, y3, rgb) {
/* 素朴なアルゴリズム */
var xmin = Math.min(x1, x2, x3);
var ymin = Math.min(y1, y2, y3);
var xmax = Math.max(x2, x2, x3);
var ymax = Math.max(y1, y2, y3);
for(var y = ymin; y <= ymax; y++) {
/* y軸方向にスキャンする */
var xl = null; /* x left */
var xr = null; /* x right */
var pairs = [[x1, y1, x2, y2], [x2, y2, x3, y3], [x3, y3, x1, y1]];
for(var i = 0; i < pairs.length; i++) {
/* 3本の直線とスキャンラインの交点を求める */
var pair = pairs[i];
var _x1 = pair[0];
var _y1 = pair[1];
var _x2 = pair[2];
var _y2 = pair[3];
var x;
if(_x1 == _x2) {
/* _x1と_x2が等しい時は水平な線分になるので、無視する */
continue;
}
if(y < _y1 && y < _y2 || y > _y1 && y > _y2) {
/* スキャンラインが線分の範囲外の時は、無視する */
continue;
}
var x = (y - _y1) * (_x2 - _x1) / (_y2 - _y1) + _x1;
if(xl == null || xl > x) {
xl = x;
}
if(xr == null || xr < x) {
xr = x;
}
}
for(x = xl; x < xr; x++) {
pset(x, y, rgb);
}
}
};