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>
相关推荐
林恒smileZAZ1 小时前
`Array(100).map(() => 1)` 为什么全为空?
前端·javascript
小巧的砖头1 小时前
C#会重蹈覆辙吗?系列之2:反射及元数据的性能问题
开发语言·前端·c#
KaMeidebaby1 小时前
卡梅德生物技术快报|蛋白的叠氮基修饰:实操解析:核酸模板耦合蛋白的叠氮基修饰实现靶蛋白定点共价标记
前端·人工智能·物联网·算法·百度
濮水大叔2 小时前
在大型 Vue 项目中,如何有效管理复杂的状态?“散装版” vs “套装版”
前端·vue.js·typescript
玄星啊2 小时前
可修改:代码的生存底线
前端·ai编程
没落英雄2 小时前
3. DeepAgents 实战 - Memory Skills 与上下文工程
前端·人工智能·架构
Bigger2 小时前
对不起!我错怪你了,UnoCSS:一次和 AI 一起排查 DevTools 卡顿的经历
前端·css·人工智能
laowang3573 小时前
依赖故障时,日志体系需要埋哪些关键节点才能快速定位是安装失败、解析失败还是运行时缺失?
前端·javascript·vue.js·webpack·node.js
倾颜3 小时前
AI Mind 的单线程短期记忆设计:如何压缩、整理并控制当前会话上下文
前端·langchain·llm
u0143781084 小时前
行内块元素之间的空白
前端·html