WebGL 2.0学习 绘制系列之PRIMITIVE_RESTART_FIXED_INDEX

一、图元重启(PRIMITIVE_RESTART_FIXED_INDEX)

1.1 PRIMITIVE_RESTART_FIXED_INDEX是webgl2新增的特性,

考虑通常的情况,当用户绘制 GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_LINE_STRIP, GL_LINE_LOOP 这些图元时,所有的绘制点按照特定的顺序被连起来,以形成一个最终的复杂图形,也就是说最终的复杂图形由多个相连的三角形或线段组成。像上面提到的情况,想要绘制分散的图形,应该怎么办?

图元重启(Primitive restart) 允许用户绘制不连续的、分散的图形。考虑使用 glDrawElements 函数,绘制时按照indices所指定的顶点的顺序来绘制的。此时可以指定某一个值,该值表示一个重启的标志。遇到这个值的时候,WebGL不会绘制图元,而是结束上一段绘制,然后重新启动新的绘制,也就是說用后面的索引所指定的顶点来从头绘制一个图形。

举个例子:比如指定8为重启的标志,遇到8就重启。上面的是不启用图元重启的情况,即通常的情况。下面的是启用图元重启的情况,我们可以看到,从9开始,又重新从头开始绘制Triangle strip

1.2 在WebGL2.0的指南中,是这样描述的:

The PRIMITIVE_RESTART_FIXED_INDEX context state, controlled with Enable/Disable in OpenGL ES 3.0, is not supported in WebGL 2.0. Instead, WebGL 2.0 behaves as though this state were always enabled. This is a compatibility difference compared to WebGL 1.0.
When drawElements, drawElementsInstanced, or drawRangeElements processes an index, if the index's value is the maximum for the data type (255 for UNSIGNED_BYTE indices, 65535 for UNSIGNED_SHORT, or 4294967295 for UNSIGNED_INT), then the vertex is not processed normally. Instead, it is as if the drawing command ended with the immediately preceding vertex, and another drawing command is immediately started with the same parameters, but only transferring the immediately following index through the end of the originally specified indices.``

大概意思就是WebGL2.0中图元重启是默认开启的,当索引值类型为UNSIGNED_BYTE时设置索引为255,UNSIGNED_SHORT时设置索引为65535就会自动使用图元重启

二、实例

2.1 使用gl.TRIANGLE_STRIP绘制三角形,关键代码如下:

js 复制代码
  // -- Init Buffer
        var vertices = new Float32Array([
            -1.0, -1.0,
            -1.0,  1.0,
             1.0, -1.0,
             1.0,  1.0
        ]);

        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        
        var num_vertices = 7;
        var indices = new Uint8Array([
            0, 1, 2, 3, 1, 0,3
        ]);
        
        ......
        gl.drawElements(gl.TRIANGLE_STRIP, 7, gl.UNSIGNED_BYTE, 0);

结果应该是这样子的:

2.1.1 gl.TRIANGLE_STRIP绘制方法

其规律是:

构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):

如果当前顶点是奇数:

组成三角形的顶点排列顺序:T = n-1 n-2 n.

如果当前顶点是偶数:

组成三角形的顶点排列顺序:T = n-2 n-1 n.

以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。 这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。对于某些操作,维持方向是很重要的,比如剔除。

注意:顶点个数n至少要大于3,否则不能绘制任何三角形。 ------------------------------------------------

版权声明:本文为CSDN博主「没有昵称阿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:blog.csdn.net/xiajun07061...

由此 可以看出,有了前四个索引,其实就已经绘制出来了,结果一模一样

js 复制代码
//将7改为4
gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0);

2.2 使用图元重启

修改索引值从3改为255

js 复制代码
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18
// WebGL 2.0 behaves as though PRIMITIVE_RESTART_FIXED_INDEX were always enabled. 
var MAX_UNSIGNED_SHORT = 255;
var indices = new Uint8Array([ 0, 1, 2,MAX_UNSIGNED_SHORT, 1, 0,3 ]);

这样的话就将索引进行了分割并一次性绘制出来 [0,1,2][1,0,3] 结果如下:

2.3 完整代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <title>WebGL 2 Samples - draw_primitive_restart</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div id="info">WebGL 2 Samples - draw_primitive_restart</div>

    <script id="vs" type="x-shader/x-vertex">
        #version 300 es
        precision highp float;
        precision highp int;

        layout(location = 0) in vec2 pos;

        void main()
        {
            gl_Position = vec4(pos, 0.0, 1.0);
        }
    </script>

    <script id="fs" type="x-shader/x-fragment">
        #version 300 es
        precision highp float;
        precision highp int;

        out vec4 color;

        void main()
        {
            color = vec4(1.0, 0.5, 0.0, 1.0);
        }
    </script>

    <script src="utility.js"></script>
    <script>
    (function () {
        'use strict';

        // -- Init Canvas
        var canvas = document.createElement('canvas');
        canvas.width = Math.min(window.innerWidth, window.innerHeight);
        canvas.height = canvas.width;
        document.body.appendChild(canvas);

        // -- Init WebGL Context
        var gl = canvas.getContext('webgl2', { antialias: false });
        var isWebGL2 = !!gl;
        if(!isWebGL2) {
            document.getElementById('info').innerHTML = 'WebGL 2 is not available.  See <a href="https://www.khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">How to get a WebGL 2 implementation</a>';
            return;
        }

        // -- Init Program
        var program = createProgram(gl, getShaderSource('vs'), getShaderSource('fs'));
        gl.useProgram(program);

        // -- Init Buffer
        var vertices = new Float32Array([
            -1.0, -1.0,
            -1.0,  1.0,
             1.0, -1.0,
             1.0,  1.0
        ]);

        var vertexPosBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        // https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.18
        // WebGL 2.0 behaves as though PRIMITIVE_RESTART_FIXED_INDEX were always enabled. 
        var MAX_UNSIGNED_SHORT = 255;
        var num_vertices = 7;
        var indices = new Uint8Array([
            0, 1, 2, MAX_UNSIGNED_SHORT, 1, 0,3
        ]);

        var vertexElementBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexElementBuffer);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);

        // -- Init Vertex Array
        var vertexArray = gl.createVertexArray();
        gl.bindVertexArray(vertexArray);

        var vertexPosLocation = 0;
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexPosBuffer);
        gl.enableVertexAttribArray(vertexPosLocation);
        gl.vertexAttribPointer(vertexPosLocation, 2, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, null);
        
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexElementBuffer);

        gl.bindVertexArray(null);

        // -- Render
        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.bindVertexArray(vertexArray);

        gl.drawElements(gl.TRIANGLE_STRIP, 7, gl.UNSIGNED_BYTE, 0);

        // -- Delete WebGL resources
        gl.deleteBuffer(vertexPosBuffer);
        gl.deleteBuffer(vertexElementBuffer);
        gl.deleteProgram(program);
        gl.deleteVertexArray(vertexArray);
    })();
    </script>
</body>
</html>
相关推荐
IT_陈寒7 分钟前
用了Proxy才发现以前的JavaScript白写了
前端·人工智能·后端
爱丶不疚41 分钟前
Eval: Agent 说的 Eval 是什么?从单测、TDD 到 Sentry 聊起
前端·ai编程·vibecoding
求道於盲43 分钟前
python中的类型标注
前端
计算机魔术师1 小时前
从硅谷测试到全球铺开,ChatGPT广告的10亿美元秘密
前端
专业抄代码选手2 小时前
08|Fiber 上的 `useState`:状态终于属于具体组件
前端·javascript·react.js
默_笙2 小时前
😭 Vibe Coding 翻车实录:AI 编程为什么必须先写"剧本"
前端·javascript
LEE2 小时前
原来 Claude Code 最厉害的工具,一行代码都不写
前端·后端
parade岁月2 小时前
为了给表格加虚拟滚动而选了 vxe-table?也可以考虑下这个
前端·vue.js
打呵欠的猫2 小时前
一个 Hook 让 AI 每次写文件前自动检查编码规范,违规代码再也提交不进来
前端·ai编程·代码规范
恋猫de小郭2 小时前
Android 17 + OkHttp 5.5.0 ,全新 ECH 下你的 HTTPS 域名可以请求时被安全隐藏
android·前端·flutter