「本記事は、技術的な視点から情報を提供することを目的としております。内容については可能な限り正確性を期しておりますが、記事内の見解は執筆者の意見や理解に基づいており、すべての方にとって普遍的な結論を示すものではありません。技術の分野は常に進化し、新たな知見が追加されることもあります。ご意見がある場合には、建設的な対話を歓迎いたしますが、批判的な意見を展開する際も、お互いの尊重を大切にしたコミュニケーションを心がけていただけると幸いです。」
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
シンプルなグラディエーション法に基づいたレイトレーシング風の球体の描画を行います。光源の位置やカラーはスライダーで調整でき、「描画する」ボタンを押すとその設定に基づいて描画が更新されます。
レイトレーシングは、物理的に光の経路や反射を正確にシミュレートする手法で、特に光源や反射、屈折を考慮するため計算量が膨大になります。これに対し、シェーダーを使った擬似的なレイトレーシング(「スクリーンスペース反射」などと呼ばれることもあります)は、より軽量な計算で「レイトレーシング風」の効果を出す方法です。
具体的には、簡易的なレイトレーシングシェーダーでは、球体と光源の位置情報をもとに、陰影やハイライトを計算して画像に反映させます。この方法は通常、行列変換やスクリーンスペースの法線データなどを利用して、物理的な精度よりも視覚的なリアリズムを重視しています。そのため、深い反射や屈折などを含む複雑な光の相互作用には対応しきれませんが、見た目をレイトレーシング風にするための計算コストを抑えた効果的な手法と言えます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>シンプルレイトレーシング</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; }
canvas { border: 1px solid black; }
.controls { margin: 10px; }
</style>
</head>
<body>
<h1>シンプルなレイトレーシング風球体描画</h1>
<canvas id="canvas" width="400" height="400"></canvas>
<div class="controls">
<label>赤: <input type="range" id="red" min="0" max="255" value="255"></label>
<label>緑: <input type="range" id="green" min="0" max="255" value="255"></label>
<label>青: <input type="range" id="blue" min="0" max="255" value="255"></label><br>
<label>光源 X軸: <input type="range" id="lightX" min="-200" max="200" value="100"></label>
<label>光源 Y軸: <input type="range" id="lightY" min="-200" max="200" value="100"></label>
<label>光源 Z軸: <input type="range" id="lightZ" min="0" max="400" value="300"></label><br>
<button onclick="draw()">描画する</button>
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
function draw() {
const red = document.getElementById('red').value;
const green = document.getElementById('green').value;
const blue = document.getElementById('blue').value;
const lightX = parseFloat(document.getElementById('lightX').value);
const lightY = parseFloat(document.getElementById('lightY').value);
const lightZ = parseFloat(document.getElementById('lightZ').value);
const sphereCenterX = width / 2;
const sphereCenterY = height / 2;
const sphereRadius = 150;
ctx.clearRect(0, 0, width, height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const dx = x - sphereCenterX;
const dy = y - sphereCenterY;
const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
if (distanceToCenter < sphereRadius) {
const dz = Math.sqrt(sphereRadius * sphereRadius - dx * dx - dy * dy);
const surfaceNormalX = dx / sphereRadius;
const surfaceNormalY = dy / sphereRadius;
const surfaceNormalZ = dz / sphereRadius;
const lightVectorX = lightX - dx;
const lightVectorY = lightY - dy;
const lightVectorZ = lightZ - dz;
const lightDistance = Math.sqrt(lightVectorX * lightVectorX + lightVectorY * lightVectorY + lightVectorZ * lightVectorZ);
const dotProduct = (surfaceNormalX * lightVectorX + surfaceNormalY * lightVectorY + surfaceNormalZ * lightVectorZ) / lightDistance;
const brightness = Math.max(dotProduct, 0);
const r = Math.min(255, red * brightness);
const g = Math.min(255, green * brightness);
const b = Math.min(255, blue * brightness);
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fillRect(x, y, 1, 1);
}
}
}
}
</script>
</body>
</html>
説明
HTML要素: カラー調整のためにRGBスライダー、光源位置の調整のためにX、Y、Z軸のスライダーがあり、すべての調整後に「描画する」ボタンを押すと、描画が更新されます。
JavaScriptロジック:
球体の中心を計算し、各ピクセルごとに距離を判定して球面内にあるピクセルのみ処理します。
法線ベクトルと光源ベクトルの内積を使って、光の当たり具合(輝度)を計算し、RGBの値に反映します。
このコードにより、光源の位置やカラーを変化させた際のシンプルなレイトレーシング効果がシミュレートできます。