WebGL プログラミング ガイド実践チュートリアル

学習ルート:

  1. 大学生であれば、フロントエンド > 数学 (幾何学 + 線形代数) > グラフィックス > WebGL > シェーダー > threejs > three.js ソース コードを学ぶのに十分な時間があります。
  2. 仕事で使用していて、完成品を迅速に作成する必要がある場合: フロントエンド > 3J。

チュートリアルは主に 4 つの部分に分かれています。

  1. webglコンテナ(座標系)
  2. WebGL レンダリング パイプライン
  3. WebGL の重要な用語
  4. 事件戦闘

1.webglコンテナ(座標系)

1. キャンバス座標系

2D 描画環境の座標系は、デフォルトではウィンドウ座標系と同じで、キャンバスの左上隅を座標の原点とし、右側に x 軸に沿った正の値を取り、正の値をとります。 Y 軸に沿った値。キャンバス座標の単位は「px」です。
ここに画像の説明を挿入

2. webgl 座標系

webgl は直交右手座標系を使用し、各方向に利用可能な値の範囲があります。この長方形の範囲を超える画像は描画されません。

  • X 軸の左端は -1、右端は 1 です。
  • Y 軸の下端は -1、上端は 1 です。
  • Z 軸の手前方向の最大値は 1、奥方向の最大値は -1 です。

注: これらの値はキャンバスのサイズとは関係がありません。キャンバスのアスペクト比に関係なく、webgl の間隔値は一貫しています。
ここに画像の説明を挿入

3. webglと画面座標の関係

2D 描画環境の座標系は、デフォルトではウィンドウ座標系と同じで、キャンバスの左上隅を座標の原点とし、右側に x 軸に沿った正の値を取り、正の値をとります。 Y 軸に沿った値。Canvasd 座標の単位は「px」です。
ここに画像の説明を挿入

2 つ目は、WebGL レンダリング パイプライン

レンダリングパイプラインは、特定の機能を備えた一連のデジタル回路ユニットで構成されるパイプラインのようなもので、前の機能ユニットで生成されたデータを次の機能ユニットが処理し、段階的に処理します。

固定小数点シェーダとフラグメント シェーダは、ラスタライザや深度テストなどの機能ユニットと同様に、より高い自律性を備えたプログラム可能な機能ユニットです。CPU は webgl API を通じて GPU と通信し、シェーダー プログラムとデータを渡し、シェーダー プログラムは GPU によって実行されます。
ここに画像の説明を挿入

WebGL レンダリング パイプラインは実際にはパイプラインであり、インスタント ラーメンの例です。

  1. インスタント ラーメンの頂点で構成される座標セットを生成します。このセットはインスタント ラーメンのプロトタイプの輪郭を描き、後で使用するために頂点バッファーに保存できます。
  2. 頂点バッファー内の頂点データを取得し、uniform を使用して頂点シェーダーに渡します。
  3. データを収集したら、プリミティブ アセンブリを使用してインスタント ラーメンのプロトタイプを構築します。
  4. 次に、ラスタライザーを使用して、ピクセル化と同様に、インスタント ヌードルのプロトタイプを 3 次元の小さな正方形にカットします。
  5. 次に、フラグメント シェーダーを使用して、ピクセル化されたインスタント ヌードルのプロトタイプに色を付け、テクスチャを作成します。
  6. 次に、帰属テスト、テンプレート テスト、深さテストを使用してグラフィック テストを実行します。
  7. 融合とディザリングは、ユーザーが表示および呼び出しできるようにカラー バッファー領域に保存されます。

3 つ、WebGL の重要な用語

1. 頂点シェーダー

頂点シェーダーは、シェーダー言語を実行できる GPU レンダリング パイプライン上の機能単位です。特定の実行は頂点シェーダー プログラムです。WebGL 固定小数点シェーダー プログラムは、JavaScript の文字列の形式で存在し、コンパイル後の頂点シェーダー、デバイスの実行。固定小数点シェーダの主な機能は、ポイント-ポイント シェーダ プログラムを実行して、固定点上で変換計算を実行することです。たとえば、点の位置座標上で回転や平行移動などの行列変換を実行し、変換された新しい頂点を割り当てます。頂点シェーダーの出力として組み込み変数 gl_Position への座標、プリミティブ アセンブリとラスタライゼーション リンクの入力。
ここに画像の説明を挿入
ここに画像の説明を挿入
ここに画像の説明を挿入

2. グラフィック要素の組み立て

頂点変換後の操作はプリミティブなアセンブリです。ハードウェアで何が起こっているかを考慮する必要はありません。プログラムの観点から見ると、これは描画関数または最初のパラメータです。描画モードは、固定点がどのように組み立てられるかをdrawArray()制御drawElement()modeます定義は、gl.LINES2 つの頂点をまとめてライン プリミティブにし、gl.TRIANGLES3 つの頂点をまとめて三角形のサーフェス プリミティブを定義し、gl.POINTSポイント ドメイン プリミティブを定義します。
ここに画像の説明を挿入

3. ラスタライズ

プリミティブをフラグメントに分解することです
ここに画像の説明を挿入

4. フラグメントシェーダ

フラグメント シェーダは、頂点シェーダと同様に、シェーダ プログラムを実行できる GPU レンダリング パイプライン上の機能単位であり、頂点データを頂点単位で処理するシェーダと、ソース データをフラグメント単位で処理するフラグメント シェーダがあります。gl.FragColor組み込み変数に値を割り当てることで、各フラグメントに色を付けることができます。その値は、特定の RGBA 値、フラグメントの位置に関連する値、またはフライ後の頂点の色にすることができますフラグメントに色を付けるだけでなく、キーワードをdiscard使用してそれらのリモートを破棄できることを認識することもできます。破棄されたフラグメントはフレーム バッファーには表示されず、当然キャンバスにも表示されません。
ここに画像の説明を挿入

フラグメント シェーダの機能は、単純に頂点シェーダの色付けとして理解できます。

4 番目に、マウスは点を動的に描画します。

実装アイデア:

  1. 単一点を描く
  2. マウスイベントリスナーのクリックイベント
  3. ポイントを配列にプッシュします
  4. 配列内のすべての点をプロットします

1. 空白のキャンバスを作成する

<template>
    <canvas id="webglCanvas" ref="webglCanvas" width="500" height="500"></canvas>
</template>
<script setup lang="ts">
import {
    
     ref, onMounted } from "vue";
const webglCanvas = ref(null);
// 入口函数
const init = () => {
    
    
    const gl = webglCanvas.value.getContext("webgl");   //拿到webgl实例
    if (!gl) {
    
    
        console.log("fail to get the rendering context of webgl");
        return;
    }
    gl.clearColor(0.0, 0.0, 0.0, 1.0);  //设置背景色
    gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景
}

onMounted(() => {
    
    
    init();
})
</script>
  1. gl.clearColor(赤、緑、青、アルファ)

    描画領域の背景色を実装します。

    • 赤、緑、青の設定は 0.0 ~ 1.0 です。
    • alpha は透明度を指定します。値は 0.0 ~ 1.0 です。

    CSS カラー システムの設定は 0 ~ 255 で、webgl のカラー値は 0 ~ 1 です。これは openGL から継承されているため、色が大きいほど明るくなります。背景色を指定すると、その色はwebgl システム内に常駐し、次の呼び出しまで変更されませんgl.clearColor()

  2. gl.clear(バッファ)

    指定されたバッファを所定の値に設定します。カラー バッファが空の場合は、gl.clearColor()指定された値が (事前に決定されたとおりに) 使用されます。

    • 盗賊
      • gl.COLOR_BUFFER_BIT: カラーバッファを指定します -clearColor(red,green,blue,alpha)
      • gl.DEPTH_BUFFER_BIT: 深度バッファを指定します -clearDepth( Depth)
      • gl.STENCLL_BUFFER_BIT: ステンシル バッファーを指定します - clearStencil(s)

2. 点を描く

<script setup lang="ts">
import {
    
     ref, onMounted } from "vue";
const webglCanvas = ref(null);
// 顶点着色器
var VSHADER_SOURCE =
    'void main() {\n' +
    '  gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // Set the vertex coordinates of the point
    '  gl_PointSize = 10.0;\n' +                    // Set the point size
    '}\n';

// 片元着色器
var FSHADER_SOURCE =
    'void main() {\n' +
    '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color
    '}\n';
// 入口函数
const init = () => {
    
    
    const gl = getWebGLContext(webglCanvas.value);   //拿到webgl实例
    if (!gl) {
    
    
        console.log("fail to get the rendering context of webgl");
        return;
    }
    // 初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    
    
        console.log('Failed to intialize shaders.');
        return;
    }

    gl.clearColor(0.0, 0.0, 0.0, 1.0);  //设置背景色
    gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景

    // 画一个点
    gl.drawArrays(gl.POINTS, 0, 1);
}

onMounted(() => {
    
    
    init();
})
</script>
  • gl.drawArrays(mode,first,count)

    さまざまなグラフィックの描画に使用できます。実際、シェーダが実行され、モード パラメータで指定された方法でグラフィックスが描画されます。

    • mode: 描画モードを指定し、次の定数シンボルを受け取ることができます: gl.POINTS、gl.LINEs、gl.LINE_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN。
    • 最初: 描画を開始する頂点を指定します (整数)
    • count: 描画に必要な頂点の数を指定します (整数)
  • getWebGLContext

    webgl インスタンスを取得します (詳細についてはソース コードを参照)

  • initShaders

    シェーダを初期化します(詳細はソースコードを参照)

  • VSHADER_SOURCE

    gl_Position: 位置を設定します

    gl_PointSize: サイズを設定します

  • FSHADER_SOURCE

    gl_FragColor: 色を設定します

ここに画像の説明を挿入

webgl は右手座標系に従っているため、gl_Position = vec4(0.0, 0.0, 0.0, 1.0);次のように、さまざまな効果に対応する座標値が変更されていることがわかります。

  1. vec4(1.0, 0.0, 0.0, 1.0): 右端に移動
  2. vec4(0.0, 1.0, 0.0, 1.0): 先頭に移動
  3. vec4(0.0, 0.0, 1.0, 1.0): 前方に移動 (画面の前で自分に近づく)

3. 属性を使用して値 gl_Position を渡します

<script setup lang="ts">
...
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    '  gl_PointSize = 10.0;\n' +            
    '}\n';
...
// 入口函数
const init = () => {
    
    
	...
    // 获取着色器中a_Position变量的存储位置
    const a_Position = gl.getAttribLocation(gl.program, "a_Position");

    // 将顶点位置传入a_Position位置
    gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
	...
}

onMounted(() => {
    
    
    init();
})
</script>

attribute上記のように、 js の変数を GLSL シェーダー構文に渡すための媒体として使用します。

  • getAttribLocation(プログラム,名前)

    nameパラメータで指定された変数の格納アドレスを取得しますattribute戻り値が等しい場合-1、変数が存在しないことを意味します。通常>=0;

  • vertexAttrib3f(位置,v0,v1,v2)

    ; で指定された変数(v0,v1,v2)データを渡しますlocationattribute

属性変数に値を代入する方法には他に、 、vertexAttrib3fがあり、使い方は同じです。上記はすべて浮動小数点型の入力パラメータです。上記の 4 つに似た名前を持つ 4 つの整数入力パラメータもあります: vertexAttrib1fvertexAttrib2fvertexAttrib4fvertexAttrib1ivertexAttrib2ivertexAttrib3ivertexAttrib4i

4. 属性を使用して値 gl_PointSize を渡します

<script setup lang="ts">
...
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    'attribute float a_PointSize;\n' +//定义a_PointSize
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    '  gl_PointSize = a_PointSize;\n' +            
    '}\n';
...
// 入口函数
const init = () => {
    
    
	...
    // 获取着色器中a_Position变量的存储位置
    const a_Position = gl.getAttribLocation(gl.program, "a_Position");
    const a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");

    // 将顶点位置传入a_Position位置
    gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
    gl.vertexAttrib1f(a_PointSize, 10.0);
	...
}

onMounted(() => {
    
    
    init();
})
</script>

コード ロジックはgl_Position値を渡すことに似ており、次のように要約できます。

シェーダーで変数を定義し、メインで割り当てを渡し、getAttribLocationjs コード ロジックで変数のアドレスを取得し、それを使用してvertexAttrib1f変数アドレスに値を挿入します。

5. 点を動的に描画する

現在の位置まではjsコード内に描画点をハードコーディングしていましたが、ここではマウスに合わせてキャンバス上をクリックするように変更し、クリックした位置を描画します。コードロジックは次のとおりです。

  • キャンバス上のマウス クリック イベントをリッスンする
  • クリックされたときにマウス座標を取得し、webgl 座標系に変換します
  • キャンバスをクリアし、ポイントのコレクションを横断し、1 つずつ描画します
<script setup lang="ts">
import {
    
     Canvas } from "fabric/fabric-impl";
import {
    
     ref, onMounted } from "vue";
const webglCanvas = ref(null);
// 顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    'attribute float a_PointSize;\n' +//定义a_PointSize
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    '  gl_PointSize = a_PointSize;\n' +
    '}\n';

// 片元着色器
var FSHADER_SOURCE =
    'void main() {\n' +
    '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // Set the point color
    '}\n';
// 入口函数
const init = () => {
    
    
    const gl = getWebGLContext(webglCanvas.value);   //拿到webgl实例
    if (!gl) {
    
    
        console.log("fail to get the rendering context of webgl");
        return;
    }
    // 初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    
    
        console.log('Failed to intialize shaders.');
        return;
    }

    // 获取a_Position的存储位置
    const a_Position = gl.getAttribLocation(gl.program, "a_Position");
    const a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");

    // 将点的位置传到attribute变量中
    gl.vertexAttrib1f(a_PointSize, 10.0);

    gl.clearColor(0.0, 0.0, 0.0, 1.0);  //设置背景色
    gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景

    let g_points = [];
    const canvas = webglCanvas.value;
    canvas.onmousedown = function (ev: any) {
    
    
        let {
    
     x, y } = ev;//x、y光标在整个可视区域的坐标
        let rect = canvas.getBoundingClientRect();//用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
        let coordsX = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
        let coordsY = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
        // 将坐标保存g_points
        g_points.push([coordsX, coordsY]);
        gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景
        // debugger
        g_points.forEach(point => {
    
    
            // 将点的位置传到attribute变量中
            gl.vertexAttrib3f(a_Position, ...point, 0.0);

            // 绘制点
            gl.drawArrays(gl.POINTS, 0, 1);
        });
    }
}

onMounted(() => {
    
    
    init();
})
</script>

これは、キャンバスの座標を webgl に変換するための式です。コードはより抽象的になる可能性があります。下の前の図:

ここに画像の説明を挿入

最終的な描画効果は次のとおりです。

画像の説明を追加してください

6. ポイントの色を変更する

attribute値を渡すために使用するのと同様にgl_position、カラーパス値を使用してuniform値を渡します。特定の値を渡すロジックは次のとおりです。

  • フラグメント シェーダーでuniformカラー変数を定義するu_FragColor
  • jsロジックで取得したu_FragColorアドレス
  • 各点を描画するときは、gl.uniform4f(u_FragColor, ...point, 0.0, 1.0);値渡しを使用して点の色を設定します

全体的なコードは次のとおりです。

<script setup lang="ts">
import {
    
     Canvas } from "fabric/fabric-impl";
import {
    
     ref, onMounted } from "vue";
const webglCanvas = ref(null);
// 顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    'attribute float a_PointSize;\n' +//定义a_PointSize
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    '  gl_PointSize = a_PointSize;\n' +
    '}\n';

// 片元着色器
var FSHADER_SOURCE =
    'precision mediump float;\n' +
    'uniform vec4 u_FragColor;\n' +
    'void main() {\n' +
    '  gl_FragColor = u_FragColor;\n' + // Set the point color
    '}\n';
// 入口函数
const init = () => {
    
    
    const gl = getWebGLContext(webglCanvas.value);   //拿到webgl实例
    if (!gl) {
    
    
        console.log("fail to get the rendering context of webgl");
        return;
    }
    // 初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    
    
        console.log('Failed to intialize shaders.');
        return;
    }

    // 获取a_Position的存储位置
    const a_Position = gl.getAttribLocation(gl.program, "a_Position");
    const a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");
    const u_FragColor = gl.getUniformLocation(gl.program, "u_FragColor");

    // 将点的位置传到attribute变量中
    gl.vertexAttrib1f(a_PointSize, 10.0);

    gl.clearColor(0.0, 0.0, 0.0, 1.0);  //设置背景色
    gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景

    let g_points = [];
    const canvas = webglCanvas.value;
    canvas.onmousedown = function (ev: any) {
    
    
        let {
    
     x, y } = ev;//x、y光标在整个可视区域的坐标
        let rect = canvas.getBoundingClientRect();//用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
        let coordsX = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
        let coordsY = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
        // 将坐标保存g_points
        g_points.push([coordsX, coordsY]);
        gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景
        // debugger
        g_points.forEach(point => {
    
    
            // 将点的位置传到attribute变量中
            gl.vertexAttrib3f(a_Position, ...point, 0.0);

            gl.uniform4f(u_FragColor, ...point, 0.0, 1.0);

            // 绘制点
            gl.drawArrays(gl.POINTS, 0, 1);
        });
    }
}

onMounted(() => {
    
    
    init();
})
</script>

フラグメント シェーダーを変更し、uniformjs によって渡された値を受け取る変数を定義します。

var FSHADER_SOURCE =
    'precision mediump float;\n' +//精度限定词来指定变量的范围(最大值和最小值)和精度,这里为中精度。
    'uniform vec4 u_FragColor;\n' +
    'void main() {\n' +
    '  gl_FragColor = u_FragColor;\n' + // Set the point color
    '}\n';

uniform変数の値を取得しますu_FragColor

const u_FragColor = gl.getUniformLocation(gl.program, "u_FragColor");

色を設定します。

gl.uniform4f(u_FragColor, ...point, 0.0, 1.0);

最終的な効果は以下の通りです。

画像の説明を追加してください

js コードでシェーダーに値を渡す場合、attribute頂点シェーダーに値を渡すために使用され、uniformフラグメント シェーダーに値を渡すために使用されます。

  • uniform4f(場所,v0,v1,v2)

    vertexAttrib と同様に、uniform4f にも同様のメソッドが合計 4 つあります。つまり、uniform1funiform2funiform3f、ですuniform4f

5. 三角形の描画と変形

1. 複数の点を描く

3D モデルの形状がどれほど複雑であっても、その基本コンポーネントは三角形ですが、複雑なモデルはさらに多くの三角形で構成されます。作成する三角形の数を減らしたり増やしたりすることで、より複雑でリアルな 3D モデルを作成することができます。以前に複数の点を描画したときは、マウスをクリックするたびに座標が に保存されg_points、最終的にg_points走査してgl.drawArrays()描画に使用されます。この方法では1点しか描画できないため、このように複雑な図形を1つずつ描画すると効率が非常に悪くなります。

webGL には、複数の頂点からのデータを一度にシェーダーに渡すことができる、非常に便利な機構であるバッファー オブジェクト (buffer object) が用意されています。バッファ オブジェクトは webGl システムのメモリ領域であり、一度に大量の頂点データをバッファ オブジェクトに格納し、頂点シェーダで使用できるようにデータをバッファ オブジェクトに保存できます。

バッファ オブジェクトを使用して複数の頂点から頂点シェーダーにデータを渡すには、5 つの手順に従う必要があります。テクスチャ オブジェクトやフレームバッファ オブジェクトなど、他のオブジェクトを処理する手順も同様です。

作成手順は以下の5つです。

  1. バッファオブジェクトの作成(gl.creaeBuffer())
  2. バインディングバッファオブジェクト (gl.bindBuffer())
  3. バッファ オブジェクトへのデータの書き込み (gl.bufferData())
  4. バッファ オブジェクトを属性変数に割り当てます (gl.vertexAttribPointer())
  5. 属性変数を有効にする (gl.enableVertexAttribArray())

回路図は以下の通りです:
ここに画像の説明を挿入

  • gl.createBuffer()

    バッファオブジェクトを作成する

  • gl.deleteBuffer(バッファ)

    パラメーターbuffer で表されるバッファー オブジェクトを削除します。buffer は削除するバッファー オブジェクトです。

  • gl.bindBuffer(ターゲット,バッファ)

    バインドバッファ。作成が完了すると、バッファーは、バッファー オブジェクトの目的を表す指定されたターゲットにバインドされます (ここでは、属性変数に渡されるデータを固定小数点シェーダーに提供することです)。これにより、webgl が処理できるようになります。コンテンツ。

    • target: パラメータは次のいずれかになります。
      • gl.ARRAY_BUFFERバッファオブジェクトに頂点データが含まれていることを示します
      • gl.ELEMENTバッファオブジェクトに頂点のインデックス値が含まれていることを示します
  • gl.bufferData(ターゲット,データ,使用法)

    データをバッファに書き込みます。

    • 対象:gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER
    • data: バッファオブジェクトに書き込むデータ (型付き配列)
    • 使用法:
      • gl.STATIC_DRAW: バッファ オブジェクトにデータを書き込むのは 1 回だけですが、何度も描画する必要があります
      • gl.STREAM_DRAW: バッファ オブジェクトにデータを 1 回だけ書き込み、その後数回描画します
      • gl.DYNAMIC_DRAW: メモリ バッファ オブジェクトにデータを複数回書き込み、それを何度も描画します
  • 型付きデータ

    パフォーマンスを最適化するために、WebGL では、プリミティブ データ型ごとに特別な種類の配列 ( JavaScript 型配列) を導入しています。ブラウザーは配列内のデータ型を事前に知っているため、配列をより効率的に処理できます。

  • gl.vertexAttribPointer()

    バッファオブジェクトを属性に割り当てます。

  • gl.enableVertexAttribArray()

    属性変数をオンにします。頂点シェーダーがバッファ内のデータにアクセスするには、属性変数を有効にする必要があります。開いた後、バッファ オブジェクトと属性変数間のリンクが実際に確立されます。gl.disableVertexAttribArray()割り当てをオフにすることもできます。属性変数をオンにすると、属性変数gl.vertexAttrib[1234]f()へのデータ転送はできなくなり、表示をオフにして属性変数を変更しない限り、これら 2 つの機能を同時に使用することはできません。

次に、上記の複数点描画の場合をベースに、キャッシュオブジェクトを利用して実現し、マウスをクリックして座標収集を行い、描画せずに保存します。描画ボタンをクリックすると、バッファ内の頂点データが描画されます。一度にレンダリングされます。

コードは以下のように表示されます:

<template>
    <div class="main">
        <ol>
            <li v-for="(point, index) in g_points" :key="index">
                {
    
    {
    
     point }}</li>
        </ol>
        <div>
            <p>
                使用缓冲对象机制实现
            </p>

            <canvas id="webglCanvas" ref="webglCanvas" width="500" height="250"></canvas>
            <div>
                <el-button @click="draw">绘制</el-button>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import {
    
     Canvas } from "fabric/fabric-impl";
import {
    
     ref, onMounted } from "vue";
const webglCanvas = ref(null);
// 顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    'attribute float a_PointSize;\n' +//定义a_PointSize
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    '  gl_PointSize = a_PointSize;\n' +
    '}\n';

// 片元着色器
var FSHADER_SOURCE =
    'precision mediump float;\n' +
    'uniform vec4 u_FragColor;\n' +
    'void main() {\n' +
    '  gl_FragColor = u_FragColor;\n' + // Set the point color
    '}\n';
// 入口函数
let gl = null;
const g_points = ref([]);
const init = () => {
    
    
    gl = getWebGLContext(webglCanvas.value);   //拿到webgl实例
    if (!gl) {
    
    
        console.log("fail to get the rendering context of webgl");
        return;
    }
    // 初始化着色器
    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    
    
        console.log('Failed to intialize shaders.');
        return;
    }

    // 获取a_Position的存储位置
    const a_PointSize = gl.getAttribLocation(gl.program, "a_PointSize");
    const u_FragColor = gl.getUniformLocation(gl.program, "u_FragColor");

    // 将点的位置传到attribute变量中
    gl.vertexAttrib1f(a_PointSize, 10.0);
    gl.uniform4f(u_FragColor, 1.0, 1.0, 1.0, 1.0);

    gl.clearColor(0.0, 0.0, 0.0, 1.0);  //设置背景色
    gl.clear(gl.COLOR_BUFFER_BIT);      //清空背景

    const canvas = webglCanvas.value;
    canvas.onmousedown = function (ev: any) {
    
    
        let {
    
     x, y } = ev;//x、y光标在整个可视区域的坐标
        let rect = canvas.getBoundingClientRect();//用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
        let coordsX = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
        let coordsY = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
        // 将坐标保存g_points
        g_points.value.push([coordsX, coordsY]);
    }
}

const draw = () => {
    
    
    console.log("开始绘制");
    // 创建类型数组
    let vertices = new Float32Array(g_points.value.flat());
    // 创建缓冲区
    let vertexBuffer = gl.createBuffer();
    if (!vertexBuffer) {
    
    
        return;
    }
    // 将缓冲区绑定到目标
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    //向缓冲区写入数据
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

    const a_Position = gl.getAttribLocation(gl.program, "a_Position");
    // 将缓冲区对象分配给a_Position变量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

    gl.enableVertexAttribArray(a_Position);

    //清空背景
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 绘制点
    gl.drawArrays(gl.POINTS, 0, g_points.value.length);
}

onMounted(() => {
    
    
    init();
})
</script>

<style lang="scss">
.main{
    
    
    display: flex;
}
ol {
    
    
    float: left;
    display: block;
    width: 150px;

    li {
    
    
        text-align: left;
    }
}
</style>

効果は次のとおりです (最初にブラシをクリックすると、カーソルは表示されません)。
画像の説明を追加してください

2. 三角形を描く

上記第 4 章の三角形の描き方と比べると、変更点は 2 つだけです。

  1. a_PointSize は単一点を描画する場合のみ有効なので必要ありませんが、削除しなくてもエラーにはなりません。
// 顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义a_Position
    //'attribute float a_PointSize;\n' +//定义a_PointSize
    'void main() {\n' +
    '  gl_Position = a_Position;\n' + // a_Position传给gl_Position
    //'  gl_PointSize = a_PointSize;\n' +
    '}\n';
  1. gl.POINTS改成gl.TRIANGLES`。
gl.drawArrays(gl.TRIANGLES, 0, g_points.value.length);

type 配列内の座標は、3、6、9 のように 3 の対数倍でなければならないことに注意してください。, 三角形の頂点は 3 つなので、通常は 3 の倍数の頂点しか三角形を描画できません。

効果は次のとおりです。
画像の説明を追加してください

頂点の数が 3 の倍数であれば、対応する数の三角形を通常どおり描画できることがわかります。

3. 他のグラフィックを描画する

上記は、 drawing gl.POINTSgl.TRIANGLES、および webgl が合計 7 種類のグラフィックスをサポートしていることを示しています。次は、同じバッチの点と異なるレンダリング モードの表示結果を示しています。
ここに画像の説明を挿入

他の描画モード コードと上記のコードの唯一の違いは、モードに対応する値を変更することです。

gl.drawArrays(mode, 0, g_points.value.length);

4. モバイル

移動の基本ロジックは、頂点シェーダーにオフセットを渡し、レンダリングされるたびに各頂点座標にオフセットを追加することです。

// 顶点着色器
var VSHADER_SOURCE =
    'attribute vec4 a_Position;\n' +//定义 a_Position
    'attribute float a_PointSize;\n' +//定义 a_PointSize
    'attribute float a_Translation;\n' +//定义 偏移量
    'void main() {\n' +
    '  gl_Position = vec4(a_Position.x+a_Translation,a_Position.y+a_Translation,a_Position.z+a_Translation,1.0);\n' + 
    '  gl_PointSize = a_PointSize;\n' +
    '}\n';
    
// 移动
const move = () => {
    
    
    T = T + 0.1;
    const a_Translation = gl.getAttribLocation(gl.program, "a_Translation");	//取出偏移量地址
    gl.vertexAttrib1f(a_Translation, T);		//给偏移量赋值
    draw();
}    

画像の説明を追加してください

webGLの基本グラフィックス

1、描画配列

前述したように、drawArrayサポートされている描画モデルは 7 つあります: gl.POINTS、gl.LINEs、gl.LINE_STRIP、gl.LINE_LOOP、gl.TRIANGLES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN

drawArrays(モード: 数値、最初: 数値、カウント: 数値): void;

  • gl.POINTS 連続して描画される一連の点
  • gl.LINES は線分を 2 つのグループに分けて描画します。点の数が奇数の場合、最後の点は破棄されます。
  • gl.LINE_STRIP すべての点が順番に接続されます
  • gl.LINE_LOOP 直線を基準に最初と最後の点を結ぶ
  • gl.TRIANGLES は、三角形を 3 つずつグループにして描画します。点の数が 3 で割り切れない場合、残りの点は破棄されます。
  • gl.TRIANGLE_STRIP 一連の縞模様の三角形。各三角形にはエッジ共有があります。
  • gl.TRIANGLE_FAN 扇風機風のグラフィック

以下は各モードに対応するグラフです。

ここに画像の説明を挿入

Supongo que te gusta

Origin blog.csdn.net/bobo789456123/article/details/129343921
Recomendado
Clasificación