偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

SpriteJS:圖形庫(kù)造輪子的那些事兒

開發(fā) 前端
在這個(gè)“造輪子”過程中,我一步步將一個(gè)很簡(jiǎn)陋的渲染庫(kù),變成一個(gè)能夠支撐可視化應(yīng)用和游戲開發(fā)的,還算不錯(cuò)的一個(gè)圖形庫(kù),其中有許多積累,也有許多思考。因?yàn)楫吘故莾赡甓嗲暗难芯?,有些?xì)節(jié)可能記得不是特別清晰,其中有些特性也許已經(jīng)有點(diǎn)過時(shí),但我想,還是有不少內(nèi)容能給大家?guī)韰⒖己蛦l(fā)。

從 2017 年到 2020 年,我花了大約 4 年的時(shí)間,從零到一,實(shí)現(xiàn)了一個(gè)可切換 WebGL 和 Canvas2D 渲染的,跨平臺(tái)支持瀏覽器、SSR、小程序,基于 DOM 結(jié)構(gòu)和支持響應(yīng)式的,高性能支持批量渲染、針對(duì)可視化場(chǎng)景優(yōu)化、支持 WebWorker 的圖形系統(tǒng)——SpriteJS。

?在這個(gè)“造輪子”過程中,我一步步將一個(gè)很簡(jiǎn)陋的渲染庫(kù),變成一個(gè)能夠支撐可視化應(yīng)用和游戲開發(fā)的,還算不錯(cuò)的一個(gè)圖形庫(kù),其中有許多積累,也有許多思考。因?yàn)楫吘故莾赡甓嗲暗难芯浚行┘?xì)節(jié)可能記得不是特別清晰,其中有些特性也許已經(jīng)有點(diǎn)過時(shí),但我想,還是有不少內(nèi)容能給大家?guī)韰⒖己蛦l(fā)。



1. 原始需求:和渲染無關(guān)

2017 年底的時(shí)候,我還在奇虎 360 負(fù)責(zé)奇舞團(tuán)。奇舞團(tuán)是一個(gè)中臺(tái)前端團(tuán)隊(duì),支持很多 360 的業(yè)務(wù)需求,其中包括一些 toB 的需求,這些需求中有不少可視化圖表和態(tài)勢(shì)感知大屏。大概在 2015-2016 年,我們的同學(xué)就開始用 D3 來完成可視化項(xiàng)目,因?yàn)?D3 具有很高的靈活性。有些同學(xué)將 D3 簡(jiǎn)單歸類為一種可視化渲染框架,實(shí)際上這種想法是錯(cuò)誤的。D3 并不是可視化框架,而是一個(gè)數(shù)據(jù)驅(qū)動(dòng)引擎。

嚴(yán)格來說,D3 關(guān)心的是數(shù)據(jù)的組織,它并不關(guān)心數(shù)據(jù)最終渲染的結(jié)果,但是,D3 的數(shù)據(jù)組織形式是基于樹狀結(jié)構(gòu)的,因?yàn)樗烊黄鹾蠘錉罱Y(jié)構(gòu)的渲染形式。正因?yàn)槿绱?,所以一般來說,D3 的官方例子都是用 DOM 或 SVG 渲染,這是因?yàn)榛?DOM 樹的渲染和 D3 的樹狀數(shù)據(jù)組織形式是絕配。

  • 使用 DOM 渲染的 D3 柱狀圖:

圖片

查看代碼:https://code.juejin.cn/pen/7160491257892962339

  • 使用 SpriteJS 渲染:

圖片

查看代碼:https://code.juejin.cn/pen/7160553901123436557

1.1  與 DOM 的一致性

為了達(dá)到上面的效果,SpriteJS 參考瀏覽器 DOM API,進(jìn)行了適配:

1.2  SpriteJS & DOM & D3

理論上,操作 SpriteJS 元素和操作 DOM 元素完全一樣,二者差異極小。

圖片

查看代碼:https://code.juejin.cn/pen/7160568056672944159

這種一致性使得 SpriteJS 完全可以和 D3 配合使用,靈活解決非常復(fù)雜的可視化問題:??http://spritejs.com/#/zh-cn/guide/d3??

2.  設(shè)計(jì)一個(gè)圖形系統(tǒng)的“骨架”

2.1  坐標(biāo)系的選擇

在圖形系統(tǒng)的設(shè)計(jì)中,首先要確定默認(rèn)坐標(biāo)系。理論上講,任何一種直角坐標(biāo)系,甚至非直角坐標(biāo)系(比如極坐標(biāo))都可以作為默認(rèn)坐標(biāo)系,在歐式幾何中,這些坐標(biāo)系都可以自由轉(zhuǎn)換。不過,考慮與 DOM 的一致性,采用瀏覽器默認(rèn)的坐標(biāo)系是一個(gè)極好的選擇。

對(duì)于 WebGL 渲染來說,我們需要將頂點(diǎn)坐標(biāo)轉(zhuǎn)換成 WebGL 坐標(biāo),在這里,我們采用根據(jù) canvas 的坐標(biāo)動(dòng)態(tài)設(shè)置 projectionMatrix 即可:??https://github.com/mesh-js/mesh.js/blob/master/src/renderer.js#L181???

updateResolution() {
const {width, height} = this.canvas;
const m1 = [ // translation
1, 0, 0,
0, 1, 0,
-width / 2, -height / 2, 1,
];
const m2 = [ // scale
2 / width, 0, 0,
0, -2 / height, 0,
0, 0, 1,
];
const m3 = mat3(m2) * mat3(m1);
this.projectionMatrix = m3;
if(this[_glRenderer]) {
this[_glRenderer].gl.viewport(0, 0, width, height);
}
}
attribute vec3 a_vertexPosition;
attribute vec3 a_vertexTextureCoord;
varying vec3 vTextureCoord;
uniform mat3 viewMatrix;
uniform mat3 projectionMatrix;
void main() {
gl_PointSize = 1.0;
vec3 pos = projectionMatrix * viewMatrix * vec3(a_vertexPosition.xy, 1.0);
gl_Position = vec4(pos.xy, 1.0, 1.0);
vTextureCoord = a_vertexTextureCoord;
}

2.2  圖層、樹形結(jié)構(gòu)與元素類型

SpriteJS 用 Scene 表示場(chǎng)景,一個(gè) Layer 表示一個(gè)圖層,在這里,我的設(shè)計(jì)是一個(gè) Layer 對(duì)應(yīng)一個(gè)畫布,即默認(rèn)每個(gè) Layer 都是獨(dú)立的 Canvas 元素。這么做有優(yōu)點(diǎn)也有缺點(diǎn),是一種設(shè)計(jì)上的取舍。

優(yōu)點(diǎn)是,每個(gè) Layer 彼此獨(dú)立,Layer 間不必考慮繪制次序,可以充分利用 WebWorker 這樣的多線程來并行繪制,而且邏輯上比較簡(jiǎn)單,如果需要在多層響應(yīng)事件,只需要注意事件處理的次序。缺點(diǎn)是如果分多層繪制,有可能產(chǎn)生較多 Canvas 對(duì)象實(shí)例,比較耗內(nèi)存。

  • 多線程繪制 

圖片

查看代碼:https://code.juejin.cn/pen/7089291575993303071

前面說過,SpriteJS 采用類似樹狀結(jié)構(gòu)來管理元素,Scene、Layer 和 Group 都是容器,而其他類型的圖形元素掛載在容器上。

圖片

SpriteJS 的元素類型比較多,一共有超過十五種圖形元素,如下圖所示。

圖片

這些元素可以分為兩類,一類是 Block 元素,包括 Sprite、Label 和 Group,一類是 Path 元素,包括各種圖形。這兩類元素中,Block 比較類似于 DOM 元素,占據(jù)矩形區(qū)域,有盒模型,有 border、padding、margin,可以計(jì)算大小;Path 比較類似于 SVG 元素,通過 Path2D 構(gòu)成矢量形狀,有 stroke 和 fill 兩類渲染,但不計(jì)算大小(不管 Path 還是 Block 都能計(jì)算 boundingClientRect)。

Group 比較特殊,SpriteJS v3 里,它默認(rèn)不計(jì)算大小,但繼承它的 Layer 和 Scene 會(huì)計(jì)算大小。在 v2 中,Group 計(jì)算大小,而且能夠做區(qū)域剪裁和設(shè)置 clipPath。v3 里,Group 主要的作用是給分組元素設(shè)置統(tǒng)一的 transform。之所以這樣設(shè)計(jì),牽扯到 WebGL 的渲染模型。在后續(xù)會(huì)詳細(xì)解釋。

考慮到擴(kuò)展性,用戶可以通過 spritejs.registerNode 注冊(cè)自定義節(jié)點(diǎn)元素。??https://github.com/spritejs/spritejs/blob/master/src/document/index.js#L15??

registerNode 的作用是注冊(cè)一個(gè)唯一的 nodeName 到 spritejs 的文檔樹上,這樣節(jié)點(diǎn)掛載之后,通過 getElementById、querySelector 等等就可以找到這個(gè)節(jié)點(diǎn)。

2.3  屬性更新和重繪機(jī)制

SpriteJS 與一般的圖形庫(kù)不同,通常情況下,一般的圖形庫(kù)會(huì)使用一個(gè)動(dòng)畫定時(shí)器來以固定幀率刷新畫布。但 SpriteJS 采用的是屬性變化時(shí)的異步更新機(jī)制。

具體原理如下圖所示:

圖片

這里有些需要注意的細(xì)節(jié):

  1. 不是所有的屬性改變都會(huì)觸發(fā) render,比如 className、ID 等改變不會(huì)觸發(fā)。
  2. 有些屬性改變不僅觸發(fā) render,還需要觸發(fā)其他操作,比如 anchor、border 等屬性的變化,需要重新計(jì)算圖形元素的輪廓(后面會(huì)講);zIndex 的變化,導(dǎo)致對(duì) group 的 children 的 renderOrder 進(jìn)行重排。

這樣設(shè)計(jì)的好處顯而易見,可以盡量減少不必要的重繪和其他計(jì)算,從而提高整體性能。

2.4  外部 Ticker

雖然 SpriteJS 有自己的更新機(jī)制,但是一些外部庫(kù),比如 ThreeJS 或者 ClayGL,有自己的更新邏輯,所以 SpriteJS 增加了手動(dòng)控制的設(shè)計(jì),以方便與外部庫(kù)配合。??http://spritejs.com/#/zh-cn/guide/ticker??

2.5  跨平臺(tái)

SpriteJS 在實(shí)現(xiàn)的時(shí)候,盡量不使用瀏覽器原生提供的能力,除非是標(biāo)準(zhǔn)的 Canvas 和 WebGL API。針對(duì)瀏覽器、NodeJS、微信小程序、微信小游戲等不同的環(huán)境,通過 polyfill 進(jìn)行適配。??https://github.com/spritejs/spritejs/tree/master/src/platform??

為了在 NodeJS 中集成 WebGL 和 Canvas 環(huán)境,做了下面這個(gè)庫(kù):??https://github.com/akira-cn/node-canvas-webgl??

3.  盒模型、事件、動(dòng)畫等

3.1  盒模型設(shè)計(jì)

對(duì) Block 類型的元素,SprteJS 采用標(biāo)準(zhǔn)的 DOM 盒模型,可以設(shè)置 border、padding 各屬性,并可以通過 boxSizing 屬性切換盒模型方式。


查看代碼:https://code.juejin.cn/pen/7160923382119137317

3.2 事件機(jī)制

視口寬高:[viewportWidth, viewportHeight]

畫布寬高:[resolutionWidth, resolutionHeight]

偏移量:[offsetLeft, offsetTop]

為什么會(huì)產(chǎn)生偏移量,詳細(xì)見屏幕適配。

采用對(duì)每個(gè)三角網(wǎng)格進(jìn)行命中檢測(cè)(此處有優(yōu)化空間,可以先排序用二分查找快速確定范圍):?

圖片

function inTriangle(p1, p2, p3, point) {
const a = p2.copy().sub(p1);
const b = p3.copy().sub(p2);
const c = p1.copy().sub(p3);

const u1 = point.copy().sub(p1);
const u2 = point.copy().sub(p2);
const u3 = point.copy().sub(p3);

const s1 = Math.sign(a.cross(u1));
let p = a.dot(u1) / a.length ** 2;
if(s1 === 0 && p >= 0 && p <= 1) return true;

const s2 = Math.sign(b.cross(u2));
p = b.dot(u2) / b.length ** 2;
if(s2 === 0 && p >= 0 && p <= 1) return true;

const s3 = Math.sign(c.cross(u3));
p = c.dot(u3) / c.length ** 2;
if(s3 === 0 && p >= 0 && p <= 1) return true;

return s1 === s2 && s2 === s3;
}

3.3  動(dòng)畫的設(shè)計(jì)

為了實(shí)現(xiàn)可以在時(shí)間軸按照任意速度播放動(dòng)畫,包括正向播放和回放,在任意時(shí)間點(diǎn)可以跳躍,實(shí)時(shí)切換播放狀態(tài)和時(shí)間軸狀態(tài),設(shè)計(jì)了 sprite-timeline 庫(kù)。

這個(gè)庫(kù)的設(shè)計(jì)是:

  1. 創(chuàng)建一個(gè) Timeline 對(duì)象,它基于當(dāng)前時(shí)間線和 playbackRate 來計(jì)算時(shí)間,playbackRate 可以是任意數(shù),所以時(shí)間可以停止,也可以回溯。playbackRate 的設(shè)置和改變會(huì)影響 Timeline 對(duì)象的 currentTime。
  2. 除了 currentTime 屬性,Timeline 對(duì)象還有一個(gè) entropy(熵)屬性,它和 currentTime 的不同是,如果 playbackRate 為負(fù)數(shù),currentTime 會(huì)回溯,但 entropy 始終增加。
  3. Timeline 對(duì)象可以 fork,fork 出的新對(duì)象以被 fork 的 Timeline 對(duì)象的 currentTime 為時(shí)間線。這意味著 Timeline 對(duì)象可以嵌套,在 SpriteJS 中,所有元素會(huì)默認(rèn) fork 它的 parent 的 timeline 對(duì)象,所以當(dāng)我們把 layer 的 timeline 的 playbackRate 設(shè)置為 0 的時(shí)候,這個(gè) layer 中所有的動(dòng)畫就都會(huì)暫停。

圖片

查看代碼:https://code.juejin.cn/pen/7160950394573553695

圖片

  • 查看代碼:https://code.juejin.cn/pen/7089261885949739016

  • Path Transition

圖片

  • 查看代碼:https://code.juejin.cn/pen/7160959750509690921

  • Play Animations

圖片

  • 查看代碼:https://code.juejin.cn/pen/7088265547250401293

  • Async frame animations

圖片

  • 查看代碼:https://code.juejin.cn/pen/7088238218914562088

4. 從 2D 到 WebGL

在 Sprite 1.0 和 2.0 的時(shí)候,主要是使用 Canvas2D 渲染,直到 3.0,我重寫了底層引擎,開始默認(rèn)采用 WebGL 渲染。

4.1  輪廓和網(wǎng)格

為了便于 WebGL 處理幾何圖形,尤其是 Path 的解析,我實(shí)現(xiàn)了一個(gè)底層渲染引擎 GitHub - mesh-js/mesh.js: A graphics system born for visualization( ??https://github.com/mesh-js/mesh.js?? ),將 2D 幾何圖形分解成輪廓和網(wǎng)格對(duì)象,這有點(diǎn)像是 ThreeJS 中的 Geometry 和 Material,只不過因?yàn)槲覀円幚淼膶?shí)際上是 2D 圖形,所以模型更加簡(jiǎn)單。

在 mesh.js 中,要繪制一個(gè)幾何圖形,我們先構(gòu)建該元素的輪廓(Figure/Contours),然后再根據(jù)輪廓?jiǎng)?chuàng)建網(wǎng)格對(duì)象。經(jīng)過這樣兩個(gè)步驟之后,我們就可以將幾何圖形繪制出來,這個(gè)過程其實(shí)比較像 Canvas2D,只是比 Canvas2D 稍復(fù)雜一點(diǎn)點(diǎn)。

圖片

查看代碼:https://code.juejin.cn/pen/7160967356489924622

4.2  三角剖分

眾所周知,WebGL 的基本圖元只有點(diǎn)、線、三角形等,要繪制多邊形,我們需要將圖形進(jìn)行三角剖分。對(duì)任意多邊形進(jìn)行三角剖分,有許多成熟算法,我選擇的是 GLU Tessellator。

我通過一系列工具庫(kù) parse-svg-path、normalize-svg-path、svg-path-contours(??https://github.com/mesh-js/mesh.js/tree/master/src/svg-path-contours??)將 SVGPath 轉(zhuǎn)換成多邊形的頂點(diǎn)列表,這里就不重復(fù)造輪子了,有些工具庫(kù)有點(diǎn)小 bug,我給順手修了一下。

獲得頂點(diǎn)之后,對(duì)頂點(diǎn)進(jìn)行三角剖分,就可以得到三角網(wǎng)格的拓?fù)浣Y(jié)構(gòu),通過這個(gè)拓?fù)浣Y(jié)構(gòu)創(chuàng)建 mesh2d 對(duì)象。

4.3 Stroke

如果不常用 WebGL 渲染,很難想象,對(duì) Canvas2D 來說非常簡(jiǎn)單的繪制帶寬度折線這類需求,會(huì)難住 WebGL 開發(fā)者。

圖片

其實(shí)這個(gè)問題已經(jīng)有比較經(jīng)典的解決方案,就是用擠壓(extrude polyline)曲線技術(shù)來實(shí)現(xiàn)。有兩種方法,一種是用 JS 算頂點(diǎn),另一種是在 shader 中進(jìn)行處理。為了靈活實(shí)現(xiàn) Canvas2D 中的“線帽(lineCap)”效果,SpriteJS 采用 JS 計(jì)算的方式來處理。

圖片

如上圖所示,黑色折線是原始的 1 個(gè)像素寬度的折線,藍(lán)色虛線組成的是我們最終要生成的帶寬度曲線,紅色虛線是頂點(diǎn)移動(dòng)的方向。因?yàn)檎劬€兩個(gè)端點(diǎn)的擠壓只和一條線段的方向有關(guān),而轉(zhuǎn)角處頂點(diǎn)的擠壓和相鄰兩條線段的方向都有關(guān),所以頂點(diǎn)移動(dòng)的方向,我們要分兩種情況討論。

首先,是折線的端點(diǎn)。假設(shè)線段的向量為(x, y),因?yàn)樗苿?dòng)方向和線段方向垂直,所以我們只要沿法線方向移動(dòng)它就可以了。根據(jù)垂直向量的點(diǎn)積為 0,我們很容易得出頂點(diǎn)的兩個(gè)移動(dòng)方向?yàn)椋?y, x)和(y, -x)。如下圖所示:

圖片

端點(diǎn)擠壓方向確定了,接下來要確定轉(zhuǎn)角的擠壓方向了,我們還是看示意圖。

圖片

如上圖,我們假設(shè)有折線 abc,b 是轉(zhuǎn)角。我們延長(zhǎng) ab,就能得到一個(gè)單位向量 v1,反向延長(zhǎng) bc,可以得到另一個(gè)單位向量 v2,那么擠壓方向就是向量 v1+v2 的方向,以及相反的 -(v1+v2) 的方向。

現(xiàn)在我們得到了擠壓方向,接下來就需要確定擠壓向量的長(zhǎng)度。

首先是折線端點(diǎn)的擠壓長(zhǎng)度,它等于 lineWidth 的一半。而轉(zhuǎn)角的擠壓長(zhǎng)度就比較復(fù)雜了,我們需要再計(jì)算一下。

圖片

綠色這條輔助線應(yīng)該等于 lineWidth 的一半,而它又恰好是 v1+v2 在綠色這條向量方向的投影,所以,我們可以先用向量點(diǎn)積求出紅色虛線和綠色虛線夾角的余弦值,然后用 lineWidth 的一半除以這個(gè)值,得到的就是擠壓向量的長(zhǎng)度了。

具體用 JavaScript 實(shí)現(xiàn)的代碼如下所示:??https://github.com/mesh-js/mesh.js/blob/master/src/extrude-contours/stroke.js?

function extrudePolyline(gl, points, {thickness = 10} = {}) {
const halfThick = 0.5 * thickness;
const innerSide = [];
const outerSide = [];

// 構(gòu)建擠壓頂點(diǎn)
for(let i = 1; i < points.length - 1; i++) {
const v1 = (new Vec2()).sub(points[i], points[i - 1]).normalize();
const v2 = (new Vec2()).sub(points[i], points[i + 1]).normalize();
const v = (new Vec2()).add(v1, v2).normalize(); // 得到擠壓方向
const norm = new Vec2(-v1.y, v1.x); // 法線方向
const cos = norm.dot(v);
const len = halfThick / cos;
if(i === 1) { // 起始點(diǎn)
const v0 = new Vec2(...norm).scale(halfThick);
outerSide.push((new Vec2()).add(points[0], v0));
innerSide.push((new Vec2()).sub(points[0], v0));
}
v.scale(len);
outerSide.push((new Vec2()).add(points[i], v));
innerSide.push((new Vec2()).sub(points[i], v));
if(i === points.length - 2) { // 結(jié)束點(diǎn)
const norm2 = new Vec2(v2.y, -v2.x);
const v0 = new Vec2(...norm2).scale(halfThick);
outerSide.push((new Vec2()).add(points[points.length - 1], v0));
innerSide.push((new Vec2()).sub(points[points.length - 1], v0));
}
}
...
}

4.4  批量繪制

因?yàn)槲覀兝L制 2D 圖形,通常這些圖形可視為同一材質(zhì),所以我們能夠?qū)⑦@些圖形網(wǎng)格數(shù)據(jù)全部壓縮到一個(gè)大的類型數(shù)組中進(jìn)行批量繪制。

??https://github.com/mesh-js/mesh.js/blob/master/src/utils/compress.js??

圖片

圖片

4.5  Shader & Pass

SpriteJS 可以使用自定義 shader 創(chuàng)建 Program,將 Program 賦給繪圖元素進(jìn)行繪制。

圖片

查看代碼:https://code.juejin.cn/pen/7088623553993506852

我們可以在渲染管線中應(yīng)用多個(gè) shader 組成管道進(jìn)行渲染,有一種特定的渲染管道叫做后期處理通道,SpriteJS 支持定義后期處理通道。

圖片

查看代碼:https://code.juejin.cn/pen/7088626022244941839

5. 關(guān)于性能優(yōu)化的那些事兒

5.1 性能的直觀感受

SpriteJS 針對(duì)可視化場(chǎng)景進(jìn)行了性能優(yōu)化??梢暬瘓?chǎng)景中有大量重復(fù)或類似形狀的幾何圖形,因此用合并頂點(diǎn)批量渲染的方式會(huì)很有效。

圖片

查看代碼:https://code.juejin.cn/pen/7088268165032968223

圖片


查看代碼:https://code.juejin.cn/pen/7088274902167322631

5.2  auto Blending 和輪廓更新

WebGL 在顏色混合的時(shí)候比較消耗性能,因此 mesh-js 對(duì)元素做了判斷,如果當(dāng)前繪制的元素都沒有 alpha 通道(透明度),那么不會(huì)開啟顏色混合,否則再開啟顏色混合。

在 SpriteJS 中,元素的大部分樣式改變,比如 transform、position、bgcolor 等等,不涉及輪廓的變化,這些情況下,我們不用重新計(jì)算輪廓,所以我們將元素輪廓計(jì)算好之后緩存起來,大部分情況下我們不需要重復(fù)計(jì)算。只有一些特殊屬性,比如 Path 的 d、lineWidth、lineCap、Block 的 border 等改變,才需要重新計(jì)算輪廓。

5.3  Seal & Cloud

??http://spritejs.com/#/zh-cn/guide/performance??

Seal 是一種特殊的方式,當(dāng)我們使用一個(gè) group 來組合一組圖形時(shí),如果只是需要使用固定的圖形拓?fù)浣Y(jié)構(gòu),我們可以使用 group 的 seal 方法將子元素的幾何圖形合并成為 group 的幾何圖形。這樣 group 的幾何圖形將被合并的幾何圖形替代,成為一個(gè)單一的元素被渲染,并且不再能夠改變幾何圖形(但是依然可以改變位置、transform、顏色等等屬性)。

seal 生效的時(shí)候,原子元素的屬性將失效,由 group 的屬性替代。

當(dāng)我們用 group 構(gòu)建組合圖形的時(shí)候,這種特殊方式能夠大大提升渲染性能。

圖片

查看代碼:https://code.juejin.cn/pen/7088273623122706466

對(duì)于繪制完全重復(fù)的幾何圖形,我們還可以利用 WebGL 的來進(jìn)行渲染。

圖片

查看代碼:https://code.juejin.cn/pen/7088273623122706466

圖片


查看代碼:https://code.juejin.cn/pen/7088274222738505732

5.4  關(guān)于 Shader 的性能開銷

有一條需要格外注意:盡量使用條件編譯代替條件分支。

6. 一些細(xì)節(jié),屏幕適配等

責(zé)任編輯:龐桂玉 來源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2015-08-06 10:14:15

造輪子facebook

2021-03-18 09:01:53

軟件開發(fā)軟件選型

2023-04-11 07:34:40

分布式系統(tǒng)算法

2022-12-07 10:34:45

AST前端編譯

2022-06-20 05:40:25

數(shù)據(jù)庫(kù)MySQL查詢

2013-12-26 14:23:03

定位系統(tǒng)GPS監(jiān)測(cè)

2021-06-09 13:28:40

密碼安全身份認(rèn)證數(shù)據(jù)安全

2018-09-26 06:50:19

2021-06-02 08:33:31

TPCTPC-H系統(tǒng)

2011-02-25 14:35:00

2022-02-08 17:39:04

MySQL服務(wù)器存儲(chǔ)

2010-10-15 10:31:00

2021-02-01 14:17:53

裝飾器外層函數(shù)里層函數(shù)

2021-03-09 23:12:51

Python集合項(xiàng)目

2016-06-07 10:47:42

2021-10-13 08:53:53

Zookeeper存儲(chǔ)系統(tǒng)

2022-04-14 11:50:39

函數(shù)組件hook

2021-09-04 16:12:33

壓縮算法數(shù)據(jù)

2018-02-02 13:58:59

數(shù)據(jù)存儲(chǔ)

2010-04-07 13:13:19

Visual Stud
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)