畫線紋理的一種簡單實現!繩子紋理! Cocos Creator !


cc.Graphics 畫線也能加紋理了?文末附送完整代碼。

初步實現

初探精靈中的網格渲染模式 ! 中簡單分析了 Sprite 組件的渲染模式 Mesh

這次,我們應用 Sprite 渲染模式 Meshcc.Graphics ,實現畫線紋理的操作。

先看看效果如何。

先在場景創建一個 cc.Graphics 節點。並添加一個子節點 cc.Sprite ,渲染模式改為 Mesh

因為 Mesh 中計算的坐標是從左上角,而 Graphics 畫圖是從中心開始畫的。

所以 cc.Sprite 節點, Scale 調整為 (1,-1)Anchor 調整為 (0,1)

為了使紋理超出邊界后可以重復填充,這個紋理大小得是 2n 次方,並設為 Repeat

畫紋理肯定需要坐標位置信息。

來一起看看,Graphicswebgl 實現。

Graphics 中有一個 _impl 變量。

這個 _impl 里有一個 _paths 變量,記錄了所有畫線路徑,和對應的畫線的點。

lineTomoveTo 都會往 _paths 塞入畫線的點數據。

對於 circlearc 以及 rect 等接口,最終還是調用 lineTomoveTo

所以有了這個 _paths 我們畫紋理的時候,可以先把點遍歷出來。

for (let index = 0; index < _impl._paths.length; index++) {
    const path = _impl._paths[index];
    const pathPoints = path.points;
    if (pathPoints.length < 2) continue;
    for (let index2 = 1; index2 < pathPoints.length; index2++) {
        // 當前點
        const p = cc.v2(pathPoints[index2].x, pathPoints[index2].y);
        // 上一個點
        const p_pre = cc.v2(pathPoints[index2 - 1].x, pathPoints[index2 - 1].y);
    }
}

如何畫紋理呢?

先考慮相鄰的兩個點,再根據線寬 w 畫一個長方形。長方形有四個點,我們要求出這四個點的坐標。

先算出這兩個點的方向。

const dir = p.sub(p_pre); //方向

接着求出一個垂直方向的向量(根據向量內積為0求出),長度為線寬一半。

const cross_dir = (dir.y == 0 ? cc.v2(0, 1) : cc.v2(1, -dir.x / dir.y).normalize()).mulSelf(w / 2); //垂直方向

根據兩個點和垂直方向可以求出這個長方形的四個頂點。

const p_r_t = p.add(cross_dir); //右上
const p_r_b = p.sub(cross_dir); // 右下
const p_l_t = p_pre.add(cross_dir); // 左上
const p_l_b = p_pre.sub(cross_dir); // 左下

最后根據四個點填充 sprite.spriteFrame 中的數據 vertices ,如果不理解的話,可以參考初探精靈中的網格渲染模式 !

對於 uv 紋理坐標,這邊就直接使用頂點坐標的縮放一個系數。參考代碼如下。

const uv_mul = 50;
const i_offset = vertices.x.length;
vertices.x.push(p_r_t.x, p_r_b.x, p_l_t.x, p_l_b.x);
vertices.y.push(p_r_t.y, p_r_b.y, p_l_t.y, p_l_b.y);
vertices.nu.push(p_r_t.x / uv_mul, p_r_b.x / uv_mul, p_l_t.x / uv_mul, p_l_b.x / uv_mul);
vertices.nv.push(p_r_t.y / uv_mul, p_r_b.y / uv_mul, p_l_t.y / uv_mul, p_l_b.y / uv_mul);

vertices.triangles.push(i_offset + 0);
vertices.triangles.push(i_offset + 1);
vertices.triangles.push(i_offset + 2);
vertices.triangles.push(i_offset + 1);
vertices.triangles.push(i_offset + 2);
vertices.triangles.push(i_offset + 3);

這么畫長方形存在一個問題,對於畫圓弧,如果分隔太大,或者線寬比較大,會出現分割的問題。

怎么解決這個問題呢?

一是可以參考源碼,把連接處補上。

另一種方式是直接用 GraphicsAssembler 中的 buffers 數據,重新組織一下。

當然,這些等我研究出來再分享給大家(日常挖坑)!

以上為白玉無冰使用 Cocos Creator v2.3.3 關於 "畫線紋理的一種簡單實現!"的技術分享。如果對你有點幫助,歡迎分享給身邊的朋友。


原文鏈接


畫線紋理之連接優化

對轉角處加一層處理,就可以更加平滑了。。。。

先看看效果。

畫線紋理的一種簡單實現! 中介紹了可以使用 Sprite 渲染模式 Meshcc.Graphics ,實現畫線紋理。

不過在連接處存在縫隙。

那么怎么處理這個縫隙呢?

只需要在連接點畫一個圓,這樣縫隙就能去掉了。

那么怎么畫圓呢?可以把圓看成是正多邊形,根據半徑和圓心的關系,可以確認位置坐標。可參考 shader 動畫之 loading 特效!這篇文章。

半徑剛好就是畫線寬度的一半,某個圓上的坐標轉成代碼如下。

// 角度
const r = 2 * Math.PI * index3 / count;
// 先算方向向量,在加上圓心坐標就是,圓上的點。
const pos_circle = cc.v2(w / 2 * Math.cos(r), w / 2 * Math.sin(r)).addSelf(p);

怎么確定頂點索引呢?

其實可以按照圓心走,畫一個個三角形就行啦。

當然這是其中一種索引方式,轉成代碼如下。

//畫圓
const count = 12;
i_offset = vertices.x.length;
vertices.x.push(p.x);
vertices.y.push(p.y);
vertices.nu.push(p.x / uv_mul);
vertices.nv.push(p.y / uv_mul);
for (let index3 = 0; index3 < count; index3++) {
    const r = 2 * Math.PI * index3 / count;
    const pos_circle = cc.v2(w / 2 * Math.cos(r), w / 2 * Math.sin(r)).addSelf(p);
    vertices.x.push(pos_circle.x);
    vertices.y.push(pos_circle.y);
    vertices.nu.push(pos_circle.x / uv_mul);
    vertices.nv.push(pos_circle.y / uv_mul);
    if (index3 === 0) {
        // 0 - count -1
        vertices.triangles.push(i_offset, i_offset + 1 + index3, i_offset + count);
    } else {
        // 0 - index3 - (index3-1)
        vertices.triangles.push(i_offset, i_offset + 1 + index3, i_offset + index3);
    }
}

以上只是實現簡單畫線紋理的效果,如果要實現繩子這種效果,那就需要重新計算紋理坐標,和位置/方向/長度等有關系。

這個暫時還沒想好,留給大家討論吧哈哈~

以上為白玉無冰使用 Cocos Creator v2.3.3 關於 "畫線紋理之連接優化!" 的技術分享。如果對你有點幫助,歡迎分享給身邊的朋友。


畫線紋理之繩子

為繩子任意方向的拖動添加紋理~

效果預覽

前置教程

這次的紋理是使用 Sprite 組件的渲染模式 Mesh ,前文 初探精靈中的網格渲染模式 ! 介紹了這個用法。

繪制的數據要用到 _poins 來畫長方形,前文 畫線紋理之簡單實現 中有介紹。

在連接處畫個圓達到平衡效果,前文 畫線紋理之連接優化 中有講到處理方法。

回顧一下這三篇文章有助於本文的理解哦~

實現原理

前幾篇已經實現了畫線紋理,這次主要的目標是計算正確的 uv 坐標。

因為這個線有方向,有長度,都會影響紋理坐標的計算。

這里想到的一個思路是,把所有的線段拉成一條直線,並放到一個方向

為了使這個紋理能從尾部帶動頭部的效果,拉直后,最后一個點作為紋理的起始點。

所以遍歷這個點的時候,要從尾部開始,並記錄一下每節的長度。

紋理坐標 v 的兩個點是 01 。 紋理坐標 u (水平方向) 根據繩子的長度去推算。

// 從最后一點開始計算
for (let index2 = pathPoints.length - 1; index2 > 0; index2--) {
	// 省略部分代碼
	vertices.x.push(p_r_t.x, p_r_b.x, p_l_t.x, p_l_b.x);
	vertices.y.push(p_r_t.y, p_r_b.y, p_l_t.y, p_l_b.y);
    // 計算uv
	vertices.nu.push(offsetX.x * uv_mul, offsetX.x * uv_mul, (offsetX.x + dirLen) * uv_mul, (offsetX.x + dirLen) * uv_mul);
	vertices.nv.push(1, 0, 1, 0);
	// 省略部分代碼
	offsetX.addSelf(cc.v2(dirLen, 0)); // 記錄已經畫的長度長度
}

這么倒着遍歷會出現一個問題,就是尾巴的紋理會被頭覆蓋。

所以計算長方形的頂點索引后,要整體反轉,讓他從頭開始畫。主要代碼如下。

let trianglesCache: number[][] = [];
for (let index2 = pathPoints.length - 1; index2 > 0; index2--) {
    // 省略部分代碼
    triangles.push(i_offset + 0);
    triangles.push(i_offset + 1);
    triangles.push(i_offset + 2);
    triangles.push(i_offset + 1);
    triangles.push(i_offset + 2);
    triangles.push(i_offset + 3);
    trianglesCache.push(triangles);
}	
trianglesCache.reverse(); // 頂點索引反轉
trianglesCache.forEach(v => {
	// 真正的頂點索引順序
    vertices.triangles.push(...v)
})

反轉后,繩子的紋理就正確了。

對於連接處畫圓(實際是多邊形),需要注意每個點都要旋轉,這樣才能讓圓的紋理方向正確。

參考代碼如下。

//畫圓
const dir_angle = dir.signAngle(cc.v2(-1, 0));//與x軸的負方向的夾角
const count = 12;
i_offset = vertices.x.length;
// 這里是圓心
vertices.x.push(p.x);
vertices.y.push(p.y);
vertices.nu.push(offsetX.x * uv_mul);
vertices.nv.push(0.5);
for (let index3 = 0; index3 < count; index3++) {
    const r = 2 * Math.PI * index3 / count;
    // 圓心到各個邊的向量
    const pos_circle = cc.v2(w / 2 * Math.cos(r), w / 2 * Math.sin(r));
    vertices.x.push(pos_circle.add(p).x);
    vertices.y.push(pos_circle.add(p).y);
    // 對於圓的uv需要旋轉
    vertices.nu.push((pos_circle.rotate(dir_angle).x + offsetX.x) * uv_mul);
    vertices.nv.push(pos_circle.rotate(dir_angle).y / w + 0.5);
    if (index3 === 0) {
        triangles.push(i_offset, i_offset + 1 + index3, i_offset + count);
    } else {
        triangles.push(i_offset, i_offset + 1 + index3, i_offset + index3);
    }
}

最后,給大家畫個星吧~

小結

把彎的掰直!

這個繩子紋理的整個思路就是把所有彎的線,都轉化成直的后,再計算紋理坐標。

以上為白玉無冰使用 Cocos Creator v2.3.3 關於 "畫線紋理之繩子!" 的技術分享。如果對你有點幫助,歡迎分享給身邊的朋友。


畫線紋理1
畫線紋理2
畫線紋理3
完整代碼(詳細見readme)
原創文章導航


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM