記事ディレクトリ
序文
WebGL の光は現実世界の光に似ており、物理的な光のシミュレーションです。WebGL はレイ トレーシング アルゴリズムを使用して 3D シーンの光の反射、屈折、散乱をシミュレートし、リアルな光と影の効果を実現します。
WebGL では、光源は光源であり、平行光、点光源、スポットライトなど、さまざまなタイプの光源をシミュレートできます。光は光源から発せられ、シーン内のさまざまなオブジェクトを通過し、カメラで捕捉されて最終的なレンダリング イメージを生成します。
光源の位置、色、強度、その他のパラメーターを調整することで、明暗のコントラスト、強い光の反射、鏡面反射など、さまざまな光と影の効果を WebGL で実現できます。これらの光と影の効果は、3D シーンのリアリズムと表現力を高めることができます。
1. 光の利用
1. ライトの種類
1.1 点光源
WEBGL では、点光源は、点光源によって生成される照明効果をシミュレートする光源のタイプです。点光源は通常、電球やろうそくなど、比較的近い光源をシミュレートするために使用されます。点光源は他の光源と比べて、照らす対象物が光源から離れるほど暗くなるという特徴があると同時に、特定の空間位置のみを照らすことができるため、正確な光を当てることができないという特徴があります。シーン全体または特定の領域を照らすのに適しています。点光源は、位置、強度、色などのプロパティを設定することで照明効果を制御でき、他のタイプの光源と組み合わせてより複雑な照明効果を実現できます。WEBGL では、点光源を使用することで、3D シーンでさまざまな照明効果をシミュレートできるため、シーンがよりリアルで鮮やかになります。
1.2 平行光
WEBGL (Web Graphics Library) は、Web ブラウザ上でインタラクティブな 3D および 2D グラフィックスをレンダリングするための JavaScript API です。平行光は、WEBGL の光源の一種です。太陽光線に似た、仮想光線に平行な光源です。すべての光の方向が同じであるため、すべての影の投影は平行になります。
WEBGL では、gl.LIGHT0 を使用して平行光源を作成できます。指向性光源には方向があり、すべての光線がどの方向に放射されるかを決定します。平行光源を作成するには、その位置と方向を設定する必要があり、通常は gl.uniformMatrix4fv() 関数を使用して平行光源の位置と方向の行列を設定します。
指向性光源を使用すると、屋外シーンで太陽光線をシミュレートしたり、影の効果を作成したり、マテリアルのハイライト部分などの特殊効果を作成するために使用することもできます。
1.3 環境光
WebGL において、環境光 (Ambient Light) はシーン全体に広く存在する照明の一種であり、オブジェクト表面の基本的な明るさを補うものです。環境光はオブジェクトの表面を直接照らすのではなく、シーンをより自然に見せる背景光を提供します。WebGL では、環境光は通常、固定方向または固定色のライトによって生成されますが、特定のマテリアルの環境光の色を調整することで環境光の色と強度を変更することもできます。環境光を他のタイプのライトと混合して、より複雑な照明効果を作成することもできます。
2. 光の反射
1. 光の方向
「光の方向」とは、入射方向とは逆の方向、すなわち入射点から光源の方向に向かう方向をいう。
2.1 環境への反映
WebGL では、光の環境反射とは、オブジェクトの表面で反射した周囲光がオブジェクトの外観に及ぼす影響を指します。周囲光は、すべての方向から反射された周囲光の平均です。通常は非常にかすかですが、表面の細部に焦点が当てられると、オブジェクトの外観に劇的な影響を与える可能性があります。
環境反射は、オブジェクトのマテリアル プロパティを設定することで実現できます。WebGL では、周囲の反射の影響は、周囲の色のプロパティを使用して制御できます。このプロパティは通常、オブジェクトの基本色に設定され、最終的な色を計算するために周囲光の色が乗算されます。
マテリアル カラーに加えて、WebGL は環境反射をシミュレートするためのアンビエント マップの使用もサポートしています。環境マップは、周囲光の色と輝度の情報を含む画像です。オブジェクトをレンダリングするとき、環境マップの情報を使用して環境反射の影響が計算されます。
環境反射は、周囲の光の効果をシミュレートするため、オブジェクトをよりリアルでリアルに見せることができます。ゲームで高品質のシーンやリアルなエフェクトを作成するためによく使用されます。
環境反射は周囲光に対するものです。環境反射では、周囲光はあらゆる面で均一かつ等しい強度でオブジェクトを照らします。反射方向は入射光の反対方向です。オブジェクトの最終的な色は、オブジェクトの色のみに関係します。入射光の色とベースの色が関係します。
<环境反射光颜色>=<入射光颜色>*表面基底色>
2.2 拡散反射
WebGL における光の拡散反射とは、光が表面に当たると、表面の粗さやオブジェクト自体の材質などの要因により、光の一部が表面で反射されるのではなく、あらゆる方向に散乱されることを意味します。同じ角度のような鏡面反射です。この散乱プロセスが拡散反射です。拡散反射の効果はより自然で、オブジェクトの表面をよりリアルにし、シーンの立体感を高めることができます。WebGLでは、光と表面法線とのなす角度や表面材質の散乱係数を計算することで拡散反射を実現できます。
拡散反射における反射光の色は、入射光の色や表面の下地の色だけでなく、入射光と物体表面の法線ベクトルとのなす入射角にも依存します。
入射角a 可以通过 光线方向和法线方向 的点积来计算:
1、<光线方向>·<法线方向> = cosa
2、<漫反射光颜色>=<入射光颜色>*<表面基底色>(<光线方向>*<法
线方向>)
2.3 混合反射
拡散反射と周囲反射が同時に存在する場合、その 2 つを加算すると、オブジェクトの最終的に観察される色が得られます。
<表面的反射光颜色> = <漫反射光颜色>+<环境反射光颜色>
3. ケース
3.1 固定法線ベクトル
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
canvas{
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
uniform mat4 mat;
void main() {
// 定义点光源的颜色
vec3 uPointLightColor = vec3(1.0,1.0,0.0);
// 点光源的位置
vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);
// 环境光
vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 顶点的世界坐标
vec4 vertexPosition = mat * aPosition;
// 点光源的方向
vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
// 环境反射
vec3 ambient = uAmbientLightColor * vec3(aColor);
// 计算入射角 光线方向和法线方向的点积
float dotDeg = dot(lightDirection, vec3(aNormal));
// 漫反射光的颜色
vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;
gl_Position = vertexPosition;
vColor = vec4(ambient + diffuseColor, aColor.a);
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aNormal = gl.getAttribLocation(program, 'aNormal');
const mat = gl.getUniformLocation(program, 'mat');
const vertices = new Float32Array([
// 0123
1, 1, 1,
-1, 1, 1,
-1,-1, 1,
1,-1, 1,
// 0345
1, 1, 1,
1,-1, 1,
1,-1,-1,
1, 1,-1,
// 0156
1, 1, 1,
1, 1, -1,
-1, 1,-1,
-1, 1,1,
// 1267
-1, 1, 1,
-1,1, -1,
-1, -1,-1,
-1,-1,1,
// 2347
-1,-1, 1,
1,-1, 1,
1,-1,-1,
-1,-1,-1,
// 4567
1,-1,-1,
1, 1,-1,
-1, 1,-1,
-1,-1,-1,
])
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
// 法向量
const normals = new Float32Array([
0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,
0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,
-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,
1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,
0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,
0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,
])
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)
const indeces = new Uint8Array([
0,1,2,0,2,3,
4,5,6,4,6,7,
8,9,10,8,10,11,
12,13,14,12,14,15,
16,17,18,16,18,19,
20,21,22,20,22,23,
])
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);
const vm = getViewMatrix(3,3,5,0.0,0.0,0.0,0.0,0.6,0.0);
const perspective = getPerspective(30, ctx.width / ctx.height, 100, 1);
gl.enable(gl.DEPTH_TEST);
gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));
gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);
</script>
3.2 法線ベクトルの計算
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
canvas{
margin: 50px auto 0;
display: block;
background: yellow;
}
</style>
</head>
<body>
<canvas id="canvas" width="400" height="400">
此浏览器不支持canvas
</canvas>
</body>
</html>
<script>
const ctx = document.getElementById('canvas')
const gl = ctx.getContext('webgl')
// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
attribute vec4 aPosition;
attribute vec4 aNormal;
varying vec4 vColor;
uniform mat4 mat;
void main() {
// 定义点光源的颜色
vec3 uPointLightColor = vec3(1.0,1.0,0.0);
// 点光源的位置
vec3 uPointLightPosition = vec3(-5.0,6.0,10.0);
// 环境光
vec3 uAmbientLightColor = vec3(0.2,0.2,0.2);
// 物体表面的颜色
vec4 aColor = vec4(1.0,0.0,0.0,1.0);
// 顶点的世界坐标
vec4 vertexPosition = mat * aPosition;
// 点光源的方向
vec3 lightDirection = normalize(uPointLightPosition - vec3(vertexPosition));
// 环境反射
vec3 ambient = uAmbientLightColor * vec3(aColor);
// 计算入射角 光线方向和法线方向的点积
float dotDeg = dot(lightDirection, vec3(aNormal));
// 漫反射光的颜色
vec3 diffuseColor = uPointLightColor * vec3(aColor) * dotDeg;
gl_Position = vertexPosition;
vColor = vec4(ambient + diffuseColor, aColor.a);
}
`; // 顶点着色器
const FRAGMENT_SHADER_SOURCE = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`; // 片元着色器
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)
const aPosition = gl.getAttribLocation(program, 'aPosition');
const aNormal = gl.getAttribLocation(program, 'aNormal');
const mat = gl.getUniformLocation(program, 'mat');
const vertices = new Float32Array([
// 0123
1, 1, 1,
-1, 1, 1,
-1,-1, 1,
1,-1, 1,
// 0345
1, 1, 1,
1,-1, 1,
1,-1,-1,
1, 1,-1,
// 0156
1, 1, 1,
1, 1, -1,
-1, 1,-1,
-1, 1,1,
// 1267
-1, 1, 1,
-1,1, -1,
-1, -1,-1,
-1,-1,1,
// 2347
-1,-1, 1,
1,-1, 1,
1,-1,-1,
-1,-1,-1,
// 4567
1,-1,-1,
1, 1,-1,
-1, 1,-1,
-1,-1,-1,
])
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition)
// 通过顶点来计算法向量 start =============
const res = []
for (let i = 0; i < vertices.length; i+=12) {
const item = cross(
[
vertices[i],
vertices[i+1],
vertices[i+2]
],
[
vertices[i+3],
vertices[i+4],
vertices[i+5]
])
for (let j = 0; j < 4; j++) {
res.push(...item)
}
}
// 法向量
const normals = new Float32Array(res)
// 通过顶点来计算法向量 end =============
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aNormal)
const indeces = new Uint8Array([
0,1,2,0,2,3,
4,5,6,4,6,7,
8,9,10,8,10,11,
12,13,14,12,14,15,
16,17,18,16,18,19,
20,21,22,20,22,23,
])
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indeces, gl.STATIC_DRAW);
const vm = getViewMatrix(3,3,5,0.0,0.0,0.0,0.0,0.6,0.0);
const perspective = getPerspective(30, ctx.width / ctx.height, 100, 1);
gl.enable(gl.DEPTH_TEST);
gl.uniformMatrix4fv(mat, false, mixMatrix(perspective, vm));
gl.drawElements(gl.TRIANGLES, indeces.length, gl.UNSIGNED_BYTE, 0);
</script>