新卒1年目の時に書いた社内勉強用のテキストです。
コンピュータグラフィックス(英語:computer graphics、略称:CG) はコンピュータを用いて作成される画像である。日本では、和製英語の「コンピュータグラフィック」または「グラフィック」も使われる。(wikiより)
CGの基礎を理解するのに必要な数学の公式を示す













BasicCG.Material.Triangle = Material.extend({
initialize: function(p1, p2, p3) {
this.vertexes = [p1, p2, p3];
},
BasicCG.util.Vector3D = function(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
},
BasicCG.Material.Box = Material.extend({
initialize: function(x, y, z, w, h, d) {
this.w = w;
this.h = h;
this.d = d;
this.angle = new BasicCG.util.Vector3D(0, 0, 0);
this.pos = new BasicCG.util.Vector3D(x, y, z);
this.vertexes = {
p11: new BasicCG.util.Vector3D(x - w/2, y - h/2, z - d/2),
p12: new BasicCG.util.Vector3D(x - w/2, y + h/2, z - d/2),
p13: new BasicCG.util.Vector3D(x + w/2, y + h/2, z - d/2),
p14: new BasicCG.util.Vector3D(x + w/2, y - h/2, z - d/2),
p21: new BasicCG.util.Vector3D(x - w/2, y - h/2, z + d/2),
p22: new BasicCG.util.Vector3D(x - w/2, y + h/2, z + d/2),
p23: new BasicCG.util.Vector3D(x + w/2, y + h/2, z + d/2),
p24: new BasicCG.util.Vector3D(x + w/2, y - h/2, z + d/2),
};
var vertex = this.vertexes;
this.triangles = [
//正面
new BasicCG.Material.Triangle(vertex.p11.getInstance(), vertex.p12.getInstance(), vertex.p13.getInstance()),
new BasicCG.Material.Triangle(vertex.p13.getInstance(), vertex.p14.getInstance(), vertex.p11.getInstance()),
//右面
new BasicCG.Material.Triangle(vertex.p14.getInstance(), vertex.p13.getInstance(), vertex.p23.getInstance()),
new BasicCG.Material.Triangle(vertex.p23.getInstance(), vertex.p24.getInstance(), vertex.p14.getInstance()),
//裏面
new BasicCG.Material.Triangle(vertex.p24.getInstance(), vertex.p23.getInstance(), vertex.p22.getInstance()),
new BasicCG.Material.Triangle(vertex.p21.getInstance(), vertex.p24.getInstance(), vertex.p22.getInstance()),
//左面
new BasicCG.Material.Triangle(vertex.p21.getInstance(), vertex.p22.getInstance(), vertex.p11.getInstance()),
new BasicCG.Material.Triangle(vertex.p12.getInstance(), vertex.p11.getInstance(), vertex.p22.getInstance()),
//上面
new BasicCG.Material.Triangle(vertex.p21.getInstance(), vertex.p11.getInstance(), vertex.p24.getInstance()),
new BasicCG.Material.Triangle(vertex.p14.getInstance(), vertex.p24.getInstance(), vertex.p11.getInstance()),
//下面
new BasicCG.Material.Triangle(vertex.p13.getInstance(), vertex.p12.getInstance(), vertex.p23.getInstance()),
new BasicCG.Material.Triangle(vertex.p22.getInstance(), vertex.p23.getInstance(), vertex.p12.getInstance()),
];
},
draw: function() {
if(!this.drawable) return;
var that = this;
var ctx = BasicCG.context;
ctx.beginPath();
ctx.moveTo(this.vertexes[0].x, this.vertexes[0].y);
for (var i = 1; i < 3; i++){
ctx.lineTo(this.vertexes[i].x, this.vertexes[i].y);
}
ctx.closePath();
ctx.strokeStyle = this.getStrokeColorTxt();
ctx.stroke();
ctx.fillStyle = this.getColorTxt();
ctx.fill();
},
ポリゴンを用いて曲面を表現するためには多数の手法がある
2次元のディスプレイに3次元の図形を描画するには、3次元図形を2次元図形に変換する必要性がある。 これを投影という。







BasicCG.util.Vector3D = function(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
},
BasicCG.util.Vector3D.prototype = {
transCoordinatesPoint: function (trans_matrix) {
var res = BasicCG.util.Matrix.mul(trans_matrix, [[this.x], [this.y], [this.z], [1]]);
this.x = res[0][0];
this.y = res[1][0];
this.z = res[2][0];
},
//点の移動
moveDistancePoint: function(x,y,z) {
this.transCoordinatesPoint(
[
[1,0,0,x],
[0,1,0,y],
[0,0,1,z],
[0,0,0,1]
]
);
},
//点の拡大・縮小
scalePoint: function (x, y, z) {
this.transCoordinatesPoint(
[
[x,0,0,0],
[0,y,0,0],
[0,0,z,0],
[0,0,0,1]
]
);
},
//点のx-y平面内における回転(z軸中心の回転)
xyRotatePoint: function (angle) {
var theta = Math.PI * angle / 180;
var sin = Math.sin(theta);
var cos = Math.cos(theta);
this.transCoordinatesPoint(
[
[cos,-sin,0,0],
[sin,cos,0,0],
[0,0,1,0],
[0,0,0,1]
]
);
},
//点のy-z平面内における回転(x軸中心の回転)
yzRotatePoint: function (angle) {
var theta = Math.PI * angle / 180;
var sin = Math.sin(theta);
var cos = Math.cos(theta);
this.transCoordinatesPoint(
[
[1,0,0,0],
[0,cos,-sin,0],
[0,sin,cos,0],
[0,0,0,1]
]
);
},
//x-z平面内における回転(y軸中心の回転)
xzRotatePoint: function (angle) {
var theta = Math.PI * angle / 180;
var sin = Math.sin(theta);
var cos = Math.cos(theta);
this.transCoordinatesPoint(
[
[cos,-sin,0,0],
[sin,cos,0,0],
[0,0,1,0],
[0,0,0,1]
]
);
},
//点のy-z平面内における回転(x軸中心の回転)
yzRotatePoint: function (angle) {
var theta = Math.PI * angle / 180;
var sin = Math.sin(theta);
var cos = Math.cos(theta);
this.transCoordinatesPoint(
[
[1,0,0,0],
[0,cos,-sin,0],
[0,sin,cos,0],
[0,0,0,1]
]
);
},
//x-z平面内における回転(y軸中心の回転)
xzRotatePoint: function (angle) {
var theta = Math.PI * angle / 180;
var sin = Math.sin(theta);
var cos = Math.cos(theta);
this.transCoordinatesPoint(
[
[cos,0,sin,0],
[0,1,0,0],
[-sin,0,cos,0],
[0,0,0,1]
]
);
},
};
BasicCG.util.Matrix = {
//行列の掛け算
mul: function(m1, m2){
var res = new Array(m2.length);
for (var i = 0; i < res.length; i++){
res[i] = [];
}
var ans = 0;
if (m1[0].length === m2.length) {
for (var i = 0; i < m1.length; i++){
for (var j = 0; j < m2[0].length; j++){
ans = 0;
for (var k = 0; k < m2.length; k++){
ans += m1[i][k] * m2[k][j];
}
res[i][j] = ans;
}
}
return res;
} else {
console.log("error: cannnot mul matrix -> m1-col-length !== m2-row-length");
}
}
};
rotate: function(angleX, angleY, angleZ) {
var that = this;
this._setAngle(angleX, angleY, angleZ);
var currentPos = this.pos.getInstance();
this.moveDistance(-currentPos.x, -currentPos.y, -currentPos.z);
_.each(this.triangles, function(triangle) {
if (angleZ !== 0) triangle.xyRotate(angleZ);
if (angleY !== 0) triangle.xzRotate(angleY);
if (angleX !== 0) triangle.yzRotate(angleX);
});
this.moveDistance(currentPos.x, currentPos.y, currentPos.z);
},

getPerspectiveProjection: function(vector3D, camera) {
var screenZ = camera.screenZ;
var projectedX =((screenZ - camera.pos.z) / (vector3D.z - camera.pos.z)) * vector3D.x;
var projectedY =((screenZ - camera.pos.z) / (vector3D.z - camera.pos.z)) * vector3D.y;
return new BasicCG.util.Vector3D(projectedX, projectedY, vector3D.z);
},

_calcInViewVolume: function(camera) {
if (this.getMinZ() > camera.minZ && this.getMaxZ() < camera.maxZ) return;
this.drawable = false;
},
getModelingTransformation: function(vector3D, camera) {
return vector3D.getInstance();
//TODO
},
getViewingTransformation: function(vector3D, camera) {
//TODO カメラの回転
var _vector3D = vector3D.getInstance();
_vector3D.moveDistancePoint(-camera.pos.x, -camera.pos.y, -camera.pos.z);
return _vector3D;
},
getPerspectiveProjection: function(vector3D, camera) {
var screenZ = camera.screenZ;
var projectedX =((screenZ - camera.pos.z) / (vector3D.z - camera.pos.z)) * vector3D.x;
var projectedY =((screenZ - camera.pos.z) / (vector3D.z - camera.pos.z)) * vector3D.y;
return new BasicCG.util.Vector3D(projectedX, projectedY, vector3D.z);
},
getViewportTransformation: function(vector3D) {
var Conf = BasicCG.Conf;
var _vector3D = vector3D.getInstance();
_vector3D.moveDistancePoint(Conf.screen.width/2, Conf.screen.height/2, 0);
return _vector3D;
},

_isTowardCamera: function(camera) {
var normalLine = this.getNormalLine();
var cameraVec = new BasicCG.util.Vector3D(
camera.pos.x -this.vertexes[0].x,
camera.pos.y -this.vertexes[0].y,
camera.pos.z -this.vertexes[0].z
);
var innerProduct = normalLine.x * cameraVec.x + normalLine.y * cameraVec.y + normalLine.z * cameraVec.z;
if (innerProduct > 0) {
return true;
}
else {
return false;
}
},
//面の法線ベクトル
//現在持っている点からベクトルを2つ導出し、
//そのベクトル2つの外積を求める
getNormalLine: function () {
var vec1 = new BasicCG.util.Vector3D(0, 0, 0);
var vec2 = new BasicCG.util.Vector3D(0, 0, 0);
var normalLine = new BasicCG.util.Vector3D(0, 0, 0); //法線ベクトル
vec1.x = this.vertexes[1].x - this.vertexes[0].x;
vec1.y = this.vertexes[1].y - this.vertexes[0].y;
vec1.z = this.vertexes[1].z - this.vertexes[0].z;
vec2.x = this.vertexes[2].x - this.vertexes[0].x;
vec2.y = this.vertexes[2].y - this.vertexes[0].y;
vec2.z = this.vertexes[2].z - this.vertexes[0].z;
// 外積
// | i j k |
// |v1x v1y v1z|
// |v2x v2y v2z|
normalLine.x = vec1.y * vec2.z - vec1.z * vec2.y;
normalLine.y = vec1.z * vec2.x - vec1.x * vec2.z;
normalLine.z = vec1.x * vec2.y - vec1.y * vec2.x;
return normalLine;
},

隠面消去アルゴリズムは、処理を行う空間により、下記の3つに分類される
drawAllObjects: function() {
var that = this;
var ctx = BasicCG.context;
this._drawBackGround(ctx);
var transformedTriangles = [];
_.each(this.objects.get(), function(object) {
$.merge(transformedTriangles, object.getTransformedTriangles(that.camera));
});
transformedTriangles = this._zSort(transformedTriangles);
_.each(transformedTriangles, function(transformedTriangle) {
transformedTriangle.draw();
});
this.topView.draw(this.camera, this.objects.get());
},
_zSort: function(transformedTriangles) {
return _.sortBy(transformedTriangles, function(triangle) {
return triangle.getMaxZ();
}).reverse();

全ての画素を背景色で初期化する;
スクリーンのすべての画素のz値を無限大で初期化する;
すべてのポリゴンを任意の順番で取り出し、
それぞれのポリゴンについて{
ポリゴンを透視投影する;
ポリゴン内部の各画素の座標値を決定する;
ポリゴン内部の各画素(x,y)について{
スクリーンの画素(x,y)に対応する点でのポリゴンの奥行き(z値)を求める
ポリゴンのz値とスクリーンの画素を比較して、
ポリゴンのz値 < スクリーンの画素のz値ならば {
スクリーンの画素(x,y)の色をポリゴンの画素(x,y)に塗る;
スクリーンの画素(x,y)のz値をポリゴンの画素(x,y)のz値に更新する;
}
}
}
スクリーンのすべての画素を順番にとり出し
それぞれの画素について{
視点から画素に向かうレイを決定する;
レイとすべての物体との交差判定を行う;
レイと交差する物体が1つ以上存在するならば{
すべての交点のなかで最も視点に近いもの(可視点)を求める;
その画素に可視点での物体の色を塗る;
}
そのほか(レイと交差する物体が存在しない)場合{
その画素に背景色を塗る;
}
}
コンピュータ・グラフィックスの基礎として、
について発表しました。 CGについて、どのような基礎を元に表示されているのかを分かって頂けたら幸いです。
今回説明できなかったことや、更に深く知りたい場合は参考書籍を是非ご一読ください。