WebGL を一緒に学ぶ: テクスチャ オブジェクトの学習

皆さんこんにちは、フロントエンドのスイカ兄貴です今日はWebGLのテクスチャオブジェクト(Texture)について勉強していきます

テクスチャ オブジェクトは、ピクセル (テクセル) を配列で GPU に送信するオブジェクトです。一般的なシナリオは、画像データを 3D オブジェクトに適用するテクスチャです。

テクスチャ オブジェクトの作成とバインド

まずテクスチャ オブジェクトを作成します。

const texture = gl.createTexture(); // 创建纹理对象

次に、テクスチャ ユニットにバインドします。

gl.bindTexture(gl.TEXTURE_2D, texture); // 将纹理对象绑定上去

充填方法

テクスチャはキャンバスの特定の領域に貼り付けるものであり、必ずしも塗りつぶし方法を設定するだけではありません。

テクスチャが描画領域より大きい場合は拡大縮小する必要があり、テクスチャが描画領域より小さい場合は拡大する必要があり、テクスチャが描画領域を完全に埋めることができない場合は、水平方向と垂直方向に埋める必要があります。

これらのシナリオでは、それに応じてさまざまな戦略を設定する必要があります。

詳細については、私の記事を参照してください。

WebGL を一緒に学ぶ: 絵を描く

// 缩小和放大都都使用 “最近点采样”
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

テクスチャユニット

WebGL は複数のテクスチャ ユニット (Texture Unit) の設定をサポートしています。つまり、複数の画像を複数のユニットに配置して切り替えることができます。

さまざまな切手を手に持っているようなもので、押したい切手を取り出すことができます。

テクスチャ ユニットには上限があり、少なくとも 8 がサポートされる必要があり、主流のブラウザは通常 16 をサポートします。

いくつかの特定のサポートは、次のコードを通じて取得できます。

gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) // 通常是 16

デフォルトでは、テクスチャ ユニット 0 が使用され、テクスチャ ユニットは次のコード行で切り替えることができます。

gl.activeTexture(gl.TEXTURE1); // 开启 1 号纹理单元

これは、テクスチャ オブジェクトをテクスチャ ユニットにバインドする前に行われることに注意してください。

最後に、テクスチャ サンプラーが使用するテクスチャ ユニットを設定する必要があります。

gl.uniform1i(u_Sampler, 0); // 开启 0 号纹理对象

このメソッドがアクティブに呼び出されない場合、デフォルトでテクスチャ ユニット 0 が使用されます。

テクスチャ ユニットの切り替えには一定のパフォーマンス コストがかかるため、テクスチャ ユニットを短期間に連続して切り替えることはお勧めできません。単純なレンダリング シナリオは無視できます。

単色のテクスチャー

真っ赤なテクスチャを描きます。

// 红色
const data = new Uint8Array([
  255, 0, 0
]);

gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标,这里是二维纹理
  0, // 细节级别,0 表示最高级别
  gl.RGB, // 纹理内部格式,还支持其他的比如 gl.RGBA、LUMINANCE(流明)
  1, // 宽(宽高的单位为像素,且为 2 的 n 次幂)
  1, // 高
  0, // 是否描边。必须为 0(但 opengl 支持)
  gl.RGB, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素(单个像素)数据类型
  data // 数据数组,一个个像素点
);

注意すべき主な点は、gl.texImage2D()このメソッドは関数のオーバーロードをサポートしており、パラメーターを渡す方法は多数あるため、それらの区別に注意してください。詳細については、公式ドキュメントを参照してください

ここでは gl.RGB 形式を使用することを選択し、(255, 0, 0)赤色の値を設定します。

最終的にはしっかりとした赤いブロックを描くことができました。

完全なコード:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');

const vertexShaderSrc = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
void main() {
 gl_Position = a_Position;
 v_TexCoord = a_TexCoord;
}
`;

const fragmentShaderSrc = `
precision highp float;
uniform sampler2D u_Sampler;
varying vec2 v_TexCoord;
void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}
`;

// 创建程序对象
createProgram(gl);

// 顶点坐标,纹理坐标
const verticesTexCoords = new Float32Array([
  // 左上点。
  // 左边两个是顶点;右边两个是纹理
  -0.5, 0.5, 0.0, 1,
  // 左下
  -0.5, -0.5, 0.0, 0.0,
  // 右上
  0.5, 0.5, 1, 1,
  // 右下
  0.5, -0.5, 1, 0.0,
]);
const FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

// 创建缓存对象
const verticesTexBuffer = gl.createBuffer();
// 绑定缓存对象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, verticesTexBuffer);
// 向缓存区写入数据
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

// 获取 a_Position 变量地址
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
// 允许访问缓存区
gl.enableVertexAttribArray(a_Position);

// 传入纹理坐标位置信息
const a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord);

/***** 纹理对象 *****/
const texture = gl.createTexture(); // 创建纹理对象
const u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler'); // 获取 u_Sampler 地址

// 记载图片

gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 翻转纹路图像的 y 轴
gl.activeTexture(gl.TEXTURE0); // 开启 0 号纹理单元
gl.bindTexture(gl.TEXTURE_2D, texture); // 将我们的纹理对象绑定上去

// 配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

// 【----关键代码---】配置纹理图像
const data = new Uint8Array([255, 0, 0, 0, 255, 255, 0, 255, 0, 0, 255, 0]);
gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标
  0, // 细节级别
  gl.RGB, // 纹理内部格式
  1,
  1,
  0,
  gl.RGB, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素数据类型
  data // 数据
);

gl.uniform1i(u_Sampler, 0); // 开启 0 号纹理对象

/****** 绘制 ******/
// 清空画布,并指定颜色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制矩形,这里提供了 4 个点
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

/**** 封装的方法 ****/

function createProgram(gl) {
    
    
  /**** 渲染器生成处理 ****/
  // 创建顶点渲染器
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vertexShader, vertexShaderSrc);
  gl.compileShader(vertexShader);
  // 创建片元渲染器
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fragmentShader, fragmentShaderSrc);
  gl.compileShader(fragmentShader);
  // 程序对象
  const program = gl.createProgram();
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  gl.useProgram(program);
  gl.program = program;
}

オンラインデモ:

https://codesandbox.io/s/1hvp4x?file=/index.js

複数のカラーブロックテクスチャ

複数のカラーブロックを同時に設定することもできます。

const data = new Uint8Array([
  255, 0, 0, 255,   // 红色
  255, 255, 0, 255, // 黄色
  0, 0, 255, 255,  // 蓝色
  0, 255, 0, 255,  // 绿色
]);

gl.texImage2D(
  gl.TEXTURE_2D, // 纹理目标
  0, // 细节级别
  gl.RGBA, // 纹理内部格式
  2,
  2,
  0,
  gl.RGBA, // 源图像数据格式
  gl.UNSIGNED_BYTE, // 纹素数据类型
  data // 数据
);

2×2の4ピクセルのテクスチャを作成し、この4ピクセルの点の色を指定して、指定した領域に拡大して描画します。

オンラインデモデモ:

https://codesandbox.io/s/7436cs?file=/index.js

画像のテクスチャ

画像テクスチャの場合、画像をロードして再生し、画像オブジェクトをテクスチャ オブジェクトにバインドする必要があります。

// 将纹理图像分配给纹理对象
gl.texImage2D(
  gl.TEXTURE_2D,
  0, // 细节级别
  gl.RGB,
  gl.RGB,
  gl.UNSIGNED_BYTE,
  img // Image 实例
);

詳細については、私の記事を参照してください。

WebGL を一緒に学ぶ: 絵を描く

終わり

テクスチャ オブジェクトは非常に一般的に使用されるオブジェクトで、領域内を塗りつぶすピクセルを指定するために使用されます。

画像をロードして、その画像を 3 次元表面に貼り付けるのが一般的です。ピクセル値を自分で指定することもできます。

私はフロントエンドのスイカ兄弟です。私をフォローして WebGL についてもっと学んでください。

おすすめ

転載: blog.csdn.net/fe_watermelon/article/details/131255181