【性能篇】28 # Canvas、SVG与WebGL在性能上的优势与劣势

news2024/11/25 23:22:58

说明

【跟月影学可视化】学习笔记。

可视化渲染的性能问题有哪些?

  • 渲染效率问题:指的是图形系统在绘图部分所花费的时间
  • 计算问题:指绘图之外的其他处理所花费的时间,包括图形数据的计算、正常的程序逻辑处理等等。

在浏览器上渲染动画,每一秒钟最高达到 60 帧左右。1 秒钟内完成 60 次图像的绘制,那么完成一次图像绘制的时间就是 1000/60(1 秒 =1000 毫秒),约等于 16 毫秒。

60fps(即 60 帧每秒,fps 全称是 frame per second,是帧率单位)。

达到比较流畅的动画效果的最低帧率是 24fps,相当于图形系统要在大约 42 毫秒内完成一帧图像的绘制。

影响 Canvas 渲染性能的 2 大要素

影响 Canvas 渲染性能的 2 大要素:

  • 绘制图形的数量
  • 绘制图形的大小

Google Chrome浏览器怎么开启查看帧率功能?

测试例子:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>canvas性能测试</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script>
            const canvas = document.querySelector("canvas");
            const ctx = canvas.getContext("2d");

            const WIDTH = canvas.width;
            const HEIGHT = canvas.height;
            const COUNT = 500;
            const RADIUS = 10;

            function randomColor() {
                return `hsl(${Math.random() * 360}, 100%, 50%)`;
            }

            function drawCircle(context, radius) {
                const x = Math.random() * WIDTH;
                const y = Math.random() * HEIGHT;
                const fillColor = randomColor();
                context.fillStyle = fillColor;
                context.beginPath();
                context.arc(x, y, radius, 0, Math.PI * 2);
                context.fill();
            }

            function draw(context, count = 500, radius = 10) {
                for (let i = 0; i < count; i++) {
                    drawCircle(context, radius);
                }
            }

            requestAnimationFrame(function update() {
                ctx.clearRect(0, 0, WIDTH, HEIGHT);
                draw(ctx, COUNT, RADIUS);
                requestAnimationFrame(update);
            });
        </script>
    </body>
</html>

在 Canvas 上每一帧绘制 500 个半径为 10 的小圆:

在这里插入图片描述

在 Canvas 上每一帧绘制 10000 个半径为 10 的小圆:

在这里插入图片描述
在 Canvas 上每一帧绘制 10000 个半径为 100 的小圆:
在这里插入图片描述

我们可以看到随着数量的增大,半径的增大 fps 已经降到 24 以下了(还跟个人电脑的 GPU 和显卡有关)。

影响 SVG 性能的 2 大要素

影响 SVG 渲染性能的 2 大要素:

  • 绘制图形的数量
  • 绘制图形的大小

测试例子:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>svg性能测试</title>
        <style>
            svg {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500"></svg>
        <script>
            const root = document.querySelector("svg");

            const WIDTH = 500;
            const HEIGHT = 500;
            const COUNT = 500;
            const RADIUS = 10;

            function randomColor() {
                return `hsl(${Math.random() * 360}, 100%, 50%)`;
            }

            function initCircles(count = COUNT) {
                for (let i = 0; i < count; i++) {
                    const circle = document.createElementNS(
                        "http://www.w3.org/2000/svg",
                        "circle"
                    );
                    root.appendChild(circle);
                }
                return [...root.querySelectorAll("circle")];
            }
            const circles = initCircles();

            function drawCircle(circle, radius = 10) {
                const x = Math.random() * WIDTH;
                const y = Math.random() * HEIGHT;
                const fillColor = randomColor();
                circle.setAttribute("cx", x);
                circle.setAttribute("cy", y);
                circle.setAttribute("r", radius);
                circle.setAttribute("fill", fillColor);
            }

            function draw() {
                for (let i = 0; i < COUNT; i++) {
                    drawCircle(circles[i], RADIUS);
                }
                requestAnimationFrame(draw);
            }

            draw();
        </script>
    </body>
</html>

在 SVG 上每一帧绘制 500 个半径为 10 的小圆:

在这里插入图片描述

在 SVG 上每一帧绘制 10000 个半径为 10 的小圆:跟 canvas 对比的 SVG 的帧率就要略差一些。

在这里插入图片描述
在 SVG 上每一帧绘制 10000 个半径为 100 的小圆:跟 canvas 对比二者差距很大,因为 SVG 是浏览器 DOM 来渲染的,元素个数越多,消耗就越大。

在这里插入图片描述

SVG 与 Canvas 不同的是,图形数量增多的时候,SVG 的帧率下降会更明显,因此,一般来说,在图形数量小于 1000 时,我们可以考虑使用 SVG,当图形数量大于 1000 但不超过 3000 时,我们考虑使用 Canvas2D,当图形数量超过 3000 时,用 Canvas2D 也很难达到比较理想的帧率了,这时候,我们就要使用 WebGL 渲染。

影响 WebGL 性能的要素

WebGL 的性能主要有三点决定因素:

  • 渲染次数
  • 着色器执行的次数:图形增大,片元着色器要执行的次数就会增多,就会增加 GPU 运算的开销。
  • 着色器运算的复杂度

另外,元素越多,本身渲染耗费的内存也越多,占用内存太多,渲染效率也会下降。

WebGL 有支持的批量绘制的技术,叫做 InstancedDrawing(实例化渲染),在 OGL 库中,只需要给几何体数据传递带有 instanced 属性的顶点数据,就可以自动使用 instanced drawing 技术来批量绘制图形。

下面例子会用到:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>webgl性能测试</title>
        <style>
            canvas {
                border: 1px dashed #fa8072;
            }
        </style>
    </head>
    <body>
        <canvas width="500" height="500"></canvas>
        <script type="module">
            import {
                Renderer,
                Program,
                Geometry,
                Transform,
                Mesh,
            } from "./common/lib/ogl/index.mjs";

            const canvas = document.querySelector("canvas");
            const renderer = new Renderer({
                canvas,
                antialias: true,
                width: 500,
                height: 500,
            });
            const gl = renderer.gl;
            gl.clearColor(1, 1, 1, 1);

            // 用来生成指定数量的小球的定点数据
            function circleGeometry(
                gl,
                radius = 0.002,
                count = 30000,
                segments = 20
            ) {
                const tau = Math.PI * 2;
                const position = new Float32Array(segments * 2 + 2);
                const index = new Uint16Array(segments * 3);
                const id = new Uint16Array(count);

                for (let i = 0; i < segments; i++) {
                    const alpha = (i / segments) * tau;
                    position.set(
                        [radius * Math.cos(alpha), radius * Math.sin(alpha)],
                        i * 2 + 2
                    );
                }
                for (let i = 0; i < segments; i++) {
                    if (i === segments - 1) {
                        index.set([0, i + 1, 1], i * 3);
                    } else {
                        index.set([0, i + 1, i + 2], i * 3);
                    }
                }
                for (let i = 0; i < count; i++) {
                    id.set([i], i);
                }
                return new Geometry(gl, {
                    position: {
                        data: position,
                        size: 2,
                    },
                    index: {
                        data: index,
                    },
                    id: {
                        instanced: 1, // 通过 instanced:1 的方式告诉 WebGL 这是一个批量绘制的数据
                        size: 1,
                        data: id,
                    },
                });
            }

            const geometry = circleGeometry(gl);

            // 实现顶点着色器,并且在顶点着色器代码中实现随机位置和随机颜色。
            const vertex = `
                precision highp float;
                attribute vec2 position;
                attribute float id;
                uniform float uTime;
                highp float random(vec2 co) {
                    highp float a = 12.9898;
                    highp float b = 78.233;
                    highp float c = 43758.5453;
                    highp float dt= dot(co.xy ,vec2(a,b));
                    highp float sn= mod(dt,3.14);
                    return fract(sin(sn) * c);
                }
                //  Function from Iñigo Quiles
                //  https://www.shadertoy.com/view/MsS3Wc
                vec3 hsb2rgb(vec3 c){
                    vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
                    rgb = rgb * rgb * (3.0 - 2.0 * rgb);
                    return c.z * mix(vec3(1.0), rgb, c.y);
                }
                varying vec3 vColor;
            
                void main() {
                    vec2 offset = vec2(
                        1.0 - 2.0 * random(vec2(id + uTime, 100000.0)),
                        1.0 - 2.0 * random(vec2(id + uTime, 200000.0))
                    );
                    vec3 color = vec3(
                        random(vec2(id + uTime, 300000.0)),
                        1.0,
                        1.0
                    );
                    vColor = hsb2rgb(color);
                    gl_Position = vec4(position * 20.0 + offset, 0, 1);
                }
            `;

            const fragment = `
                precision highp float;
                varying vec3 vColor;
                void main() {
                    gl_FragColor = vec4(vColor, 1);
                }
            `;

            const program = new Program(gl, {
                vertex,
                fragment,
                uniforms: {
                    uTime: { value: 0 },
                },
            });

            const scene = new Transform();
            const mesh = new Mesh(gl, { geometry, program });
            mesh.setParent(scene);

            function update(t) {
                program.uniforms.uTime.value = t / 1000;
                renderer.render({ scene });
                requestAnimationFrame(update);
            }
            update(0);
        </script>
    </body>
</html>

WebGL,绘制 30000 个小球:WebGL 渲染之所以能达到这么高的性能,是因为 WebGL 利用 GPU 并行执行的特性,无论批量绘制多少个小球,都能够同时完成计算并渲染出来。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/101953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode 1760. 袋子里最少数目的球

给你一个整数数组 nums &#xff0c;其中 nums[i] 表示第 i 个袋子里球的数目。同时给你一个整数 maxOperations 。你可以进行如下操作至多 maxOperations 次&#xff1a;选择任意一个袋子&#xff0c;并将袋子里的球分到 2 个新的袋子中&#xff0c;每个袋子里都有 正整数 个球…

深圳电巢携手东华理工大学“电巢相伴 研职引航” 线上讲座圆满结束

前 言 2022年12月13日下午2时&#xff0c;电巢十日谈——“电巢相伴 研职引航”在电巢app直播间开讲&#xff0c;本次活动由电巢科技与东华理工大学共同举办&#xff0c;为机械与电子工程学院的大一到大四的学生带来了不少启发。 「电巢十日谈」这一系列活动正是受到薄伽丘《…

如何实现冷库冷链远程监控

本解决方案是通过智能物联网技术&#xff0c;实现对冷库温湿度的远程监控管理。 根据冷库内的温湿度数据可以及时掌握冷库内的环境状况&#xff0c;并及时采取相应的措施。 该系统通过实时监测与显示不同冷库的温湿度&#xff0c;将温度数据上传到服务器&#xff0c;并可根据实…

Fully Convolutional Adaptation Networks for Semantic Segmentation

参考 论文解析之《Fully Convolutional Adaptation Networks for Semantic Segmentation》 - 云社区 - 腾讯云 论文网址&#xff1a;Fully Convolutional Adaptation Networks for Semantic Segmentation 摘要 深度神经网络的最新进展令人信服地证明了在大数据集上学习视觉模…

基于MediatR管道的公共业务校验

基于MediatR的管道模式&#xff0c;我们可以在处理业务之前&#xff0c;进行统一验证&#xff0c;记录日志等。 所有命令&#xff08;Command&#xff09;再被处理(Handle)之前&#xff0c;都要经过IRequestPreProcessor处理&#xff0c;我们注入自己的拦截器&#xff0c;执行…

chrome拓展插件开发中使用chrome.storage本地存储

一、描述 在扩展程序中本地存储数据可以通过 chrome.storage API 实现&#xff0c;和 web 中的 localstorage 在某些方面是有区别的&#xff0c;chrome.storage 已经做了优化。 与 localStorage 的区别&#xff1a; 用户数据可以与 chrome 自动同步&#xff08;通过 storage…

【推荐】智能制造工业4.0与MES资料合集

智能制造&#xff0c;源于人工智能的研究&#xff0c;一般认为智能是知识和智力的总和&#xff0c;前者是智能的基础&#xff0c;后者是指获取和运用知识求解的能力。 智能制造应当包含智能制造技术和智能制造系统&#xff0c;智能制造系统不仅能够在实践中不断地充实知识库&a…

请上车MySQL面试必备点:从常见的存储引擎到混淆的锁分类

我们之前的文章InnoDB解决幻读的方案中提到了记录锁&#xff08;行锁&#xff09;、间隙锁和临键锁&#xff0c;后台有小伙伴催我更新一下其他的锁。拖延症又犯了&#xff0c;趁周末&#xff0c;今天我们来总结一下MyISAM和InnoDB引擎下锁的种类及使用方法。 MySQL的四大常见存…

面试题:Linux是如何避免内存碎片的

Linux是如何避免内存碎片的&#xff1f; 在网上看到这个面试题&#xff0c;参考答案是这样的&#xff1a; 伙伴算法&#xff0c;用于管理物理内存&#xff0c;避免内存碎片;高速缓存Slab层用于管理内核分配内存&#xff0c;避免碎片。 故继而去深入了解了一波&#xff0c;做了…

牛客之基础单片机知识_1

✅作者简介&#xff1a;大家好我是 xxx&#xff0c;是一名嵌入式工程师&#xff0c;希望一起努力&#xff0c;一起进步&#xff01; &#x1f4c3;参照主页&#xff1a;嵌入式基地 &#x1f525;系列专栏&#xff1a;硬件基础知识——单片机 习题专栏 &#x1f4ac;网上关于嵌入…

软件测试不常用但是一定要会的测试技术与用例设计

一、认识基本术语 术语一&#xff1a; ◆动态测试&#xff08;dynamic testing&#xff09; 通过运行软件的组件或系统来测试软件(实际运行被测软件/系统)【需要进行操作】 ◆静态测试&#xff08;static testing) 对组件的规格说明书进行评审&#xff0c;对静态代码进行走…

【推荐】700套高端简历模板合集

简历&#xff08;英语&#xff1a;resume&#xff09;&#xff0c;顾名思义&#xff0c;就是对个人学历、经历、特长、爱好及其它有关情况所作的简明扼要的书面介绍。简历是有针对性的自我介绍的一种规范化、逻辑化的书面表达。对应聘者来说&#xff0c;简历是求职的“敲门砖”…

JSP学生宿舍网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 模块划分&#xff1a;通知类型模块、通知信息模块、院系信息、班级信息、宿舍 楼信息、宿舍信息、宿管信息、学生信息、…

three.js之多线条组合

文章目录多线条组合例子专栏目录请点击 多线条组合 我们可以通过CurvePath把多个曲线、直线等合并成一个曲线 例子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><style>body {margin: 0;overflow: hidde…

CSS习题解答

文章目录1.1 样式定义方式1.2 选择器1.3 颜色1.4 文本1.5 字体1.6 背景1.7 边框1.8 元素展示格式1.9 内边距与外边距1.10 盒子模型1.11 位置1.12 浮动实战&#xff1a;个人名片1.13 flex布局1.14 响应式布局作业01作业02作业03作业04作业05作业06作业07作业08作业09作业10其他1…

正则表达式学习

文章目录入门开始和结束字符组区间特殊字符转义取反快捷匹配数字和字母匹配空白任意字符重复次数和区间或者条件进阶分组非捕获分组分组的引用正向先行断言反向先行断言正向后行断言反向后行断言常用元字符整理相关网站在线测试网站题目练习网站其他笔记入门 开始和结束 正则…

计算机毕设Python+Vue学生量化考核系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Python中groupby函数详解(非常容易懂)

目录&#xff1a;Python中的groupby函数一、groupby 能做什么&#xff1f;二、单类分组2.1 创建数据集三、多类分组一、groupby 能做什么&#xff1f; groupby函数主要的作用是进行数据的分组以及分组后地组内运算&#xff01; 于数据的分组和分组运算主要是指groupby函数的应…

网络安全等级保护十问十答

网络安全等级保护十问十答1.什么是等级保护&#xff1f;2.为什么需要等级保护&#xff1f;3.目标客户是&#xff1f;4.等保工作步骤包括什么&#xff1f;5.等保测评结论有几种结果&#xff1f;6.等保如何定级&#xff1f;7. 定级对象在哪备案&#xff1f;8. 等级保护都测评什么…

[附源码]计算机毕业设计Node.js宠物商店网站(程序+LW)

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…