3Dグラフィックの基礎
6851 ワード
3Dグラフィックの基礎を JavaScript で実践
波紋のシミュレーションをワイヤフレームで表示します。
視点変換、透視変換の基礎が学べます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>3D</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
var SCREEN_X = 1280;
var SCREEN_Y = 720;
var CX = SCREEN_X/2; // 中央の位置を記録
var CY = SCREEN_Y/2; // 中央の位置を記録
var L = 0.55; // スクリーンと目までの距離[m]
var STOR = 1280/0.34; // スクリーンとリアルの比率(34cmで1280px)
var POINT_X_NUM = 64;
var POINT_Y_NUM = 64;
var eye = {};
eye.p = {x: 5, y: 10, z: -18}; // 目の位置
eye.v = {x: -eye.p.x, y: -eye.p.y, z: -eye.p.z}; // 視線方向
eye.l = {x: -1, y: -1, z: 0}; // 光の方向
var canvas;
var ctx;
var p; // 点の配列(x, y, z, vx, vy, vz, sx, sy)
var poly; // ポリゴンの配列
var timer; // インターバルのハンドル
var hamon;
$(document).ready(function(){
canvas = $('#canvas')[0];
canvas.width = SCREEN_X;
canvas.height = SCREEN_Y;
ctx = canvas.getContext('2d');
p = [];
poly = [];
for(var i = 0; i < POINT_Y_NUM; i++){
for(var j = 0; j < POINT_X_NUM; j++){
var bp = i*POINT_Y_NUM + j;
p[bp] = {
x: j*10/POINT_X_NUM - 5,
y: 0.0,
z: 5 - i*10/POINT_Y_NUM,
vx: 0.0,
vy: 0.0,
vz: 0.0,
sx: 0,
sy: 0
};
}
}
var n = 0;
for(var i = 0; i < POINT_Y_NUM - 1; i++){
for(var j = 0; j < POINT_X_NUM - 1; j++){
var bp = i*POINT_Y_NUM + j;
poly[n] = [bp, bp + POINT_Y_NUM + 1, bp + 1];
n++;
poly[n] = [bp, bp + POINT_Y_NUM , bp + POINT_Y_NUM + 1];
n++;
}
}
var NUM = 16;
hamon = [];
for(var i = 0; i < NUM; i++){
hamon[i] = {
x: 5*Math.cos(2*Math.PI/NUM*i),
z: 5*Math.sin(2*Math.PI/NUM*i),
r: 0.05,
i: 0
};
}
calcViewG();
var t = 0;
timer = setInterval(function(){
t += 0.1;
if(t >= 2*Math.PI){
t -= 2*Math.PI;
}
for(var i = 0; i < POINT_Y_NUM; i++){
for(var j = 0; j < POINT_X_NUM; j++){
bp = i*POINT_Y_NUM + j;
p[bp].y = 0;
for(var k in hamon){
var h = hamon[k];
var r = Math.sqrt((h.x - p[bp].x)*(h.x - p[bp].x) + (h.z - p[bp].z)*(h.z - p[bp].z));
p[bp].y += h.r*Math.sin(t - 5*r + h.i);
}
}
}
reDraw();
}, 1);
});
function reDraw(){
// 視点変換
var g = eye.g;
for(var i in p){
p[i].vx = p[i].x*g[0][0] + p[i].y*g[0][1] + p[i].z*g[0][2] + g[0][3];
p[i].vy = p[i].x*g[1][0] + p[i].y*g[1][1] + p[i].z*g[1][2] + g[1][3];
p[i].vz = p[i].x*g[2][0] + p[i].y*g[2][1] + p[i].z*g[2][2] + g[2][3];
}
// 透視変換
for(var i in p){
p[i].sx = parseInt(CX + STOR*L*p[i].vx/p[i].vz);
p[i].sy = parseInt(CY - STOR*L*p[i].vy/p[i].vz);
}
// キャンバス初期化
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 描画
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
// 横線
for(var i = 0; i < POINT_Y_NUM; i++){
for(var j = 0; j < POINT_X_NUM; j++){
bp = i*POINT_Y_NUM + j;
if(j == 0){
ctx.beginPath();
ctx.moveTo(p[bp].sx, p[bp].sy);
}else{
ctx.lineTo(p[bp].sx, p[bp].sy);
}
}
ctx.stroke();
}
// 縦線
for(var j = 0; j < POINT_X_NUM; j++){
for(var i = 0; i < POINT_Y_NUM; i++){
bp = i*POINT_Y_NUM + j;
if(i == 0){
ctx.beginPath();
ctx.moveTo(p[bp].sx, p[bp].sy);
}else{
ctx.lineTo(p[bp].sx, p[bp].sy);
}
}
ctx.stroke();
}
}
// 視線左回転
function turnLeft(){
var cosT = Math.cos(-0.1);
var sinT = Math.sin(-0.1);
var x = eye.p.x;
var y = eye.p.z;
eye.p.x = x*cosT - y*sinT;
eye.p.z = y*cosT + x*sinT;
eye.v.x = -eye.p.x;
eye.v.z = -eye.p.z;
calcViewG();
}
// 視線右回転
function turnRight(){
var cosT = Math.cos(0.1);
var sinT = Math.sin(0.1);
var x = eye.p.x;
var y = eye.p.z;
eye.p.x = x*cosT - y*sinT;
eye.p.z = y*cosT + x*sinT;
eye.v.x = -eye.p.x;
eye.v.z = -eye.p.z;
calcViewG();
}
/**
* 行列計算
*/
function gyoretsu(a, b){
var r = [];
for(var y = 0; y < 4; y++){
r[y] = [];
for(var x = 0; x < 4; x++){
r[y][x] = a[0][x]*b[y][0]
+ a[1][x]*b[y][1]
+ a[2][x]*b[y][2]
+ a[3][x]*b[y][3];
}
}
return r;
}
/**
* 視点行列計算
*/
function calcViewG(){
// 光源ベクトルを単位ベクトルに
var r = Math.sqrt(eye.l.x*eye.l.x + eye.l.y*eye.l.y + eye.l.z*eye.l.z);
eye.l.x = eye.l.x/r;
eye.l.y = eye.l.y/r;
eye.l.z = eye.l.z/r;
// 平行移動
var g = [
[1, 0, 0, -eye.p.x],
[0, 1, 0, -eye.p.y],
[0, 0, 1, -eye.p.z],
[0, 0, 0, 1]
];
// y軸回転で視線をz軸方向に
r = Math.sqrt(eye.v.x*eye.v.x + eye.v.z*eye.v.z);
var cosT = eye.v.z/r;
var sinT = eye.v.x/r;
g = gyoretsu(g, [
[cosT, 0, -sinT, 0],
[ 0, 1, 0, 0],
[sinT, 0, cosT, 0],
[ 0, 0, 0, 1]
]);
// x軸回転で視線をz軸と平行に
var ez = r;
r = Math.sqrt(ez*ez + eye.v.y*eye.v.y);
cosT = ez/r;
sinT = -eye.v.y/r;
eye.g = gyoretsu(g, [
[ 1, 0, 0, 0],
[ 0, cosT, sinT, 0],
[ 0, -sinT, cosT, 0],
[ 0, 0, 0, 1]
]);
}
</script>
</head>
<body>
<div style="width: 1280px; margin: auto">
<h1 style="float:left">3D</h1>
<button id="left" style="float:left" onclick="turnLeft();">←</button>
<button id="right" style="float:left" onclick="turnRight();">→</button><br />
<button id="stop" style="float:left" onclick="clearInterval(timer);">停止</button>
<div style="clear;both"></div>
<canvas id="canvas" style="width:100%; height: 720px;border:solid 1px black"></canvas>
</div>
</body>
</html>
Author And Source
この問題について(3Dグラフィックの基礎), 我々は、より多くの情報をここで見つけました https://qiita.com/pyuruco/items/ceedb3d86976eb3f4b6f著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .