亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在iOS中使用OpenGL ES實(shí)現(xiàn)繪畫板的方法

 更新時(shí)間:2020年03月16日 11:00:01   作者:Lyman''s Blog  
這篇文章主要介紹了在iOS中使用OpenGL ES實(shí)現(xiàn)繪畫板的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

今天我們使用 OpenGL ES 來實(shí)現(xiàn)一個(gè)繪畫板,主要介紹在 OpenGL ES 中繪制平滑曲線的實(shí)現(xiàn)方案。

首先看一下最終效果:

在 iOS 中,有很多種方式可以實(shí)現(xiàn)一個(gè)繪畫板,比如我的另外一個(gè)項(xiàng)目 MFPaintView 就是基于 CoreGraphics 實(shí)現(xiàn)的。

然而,使用 OpenGL ES 來實(shí)現(xiàn)可以獲得更多的靈活性,比如我們可以自定義筆觸的形狀,這是其他實(shí)現(xiàn)方式做不到的。

我們知道,OpenGL ES 中只有 點(diǎn)、直線、三角形 這三種圖元。因此, 怎么在 OpenGL ES 中繪制曲線 ,是我們第一個(gè)要解決的問題,也是最復(fù)雜的問題。

我們會(huì)使用比較大的篇幅來講解這個(gè)問題。至于繪畫板的其他功能實(shí)現(xiàn),并不是說不重要,只是說其他的繪畫板實(shí)現(xiàn)方式,也會(huì)有類似的邏輯,所以這部分會(huì)放在最后再簡(jiǎn)單介紹一下。

一、怎么繪制曲線

在 OpenGL ES 中繪制曲線的方式,就是 將曲線拆分成點(diǎn)序列來繪制 。

因?yàn)橐L制點(diǎn),所以我們采取的是 點(diǎn)圖元 。即我們要把頂點(diǎn)數(shù)據(jù)當(dāng)成 點(diǎn) 來繪制,并且每個(gè)點(diǎn)都要繪制出筆觸的紋理。關(guān)鍵步驟如下:

指定圖元類型:

glDrawArrays(GL_POINTS, 0, self.vertexCount);

頂點(diǎn)著色器:

attribute vec4 Position;

uniform float Size;

void main (void) {
  gl_Position = Position;
  gl_PointSize = Size;
}

片段著色器:

precision highp float;

uniform float R;
uniform float G;
uniform float B;
uniform float A;

uniform sampler2D Texture;

void main (void) {
  vec4 mask = texture2D(Texture, vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y));
  gl_FragColor = A * vec4(R, G, B, 1.0) * mask;
}

這里的關(guān)鍵點(diǎn)在于 gl_PointCoord 這個(gè)內(nèi)置變量,當(dāng)我們使用點(diǎn)圖元的時(shí)候,可以通過這個(gè)變量獲取到 當(dāng)前像素在點(diǎn)圖元中的歸一化坐標(biāo) 。

但是這個(gè)坐標(biāo)的原點(diǎn)是在左上角,這和紋理坐標(biāo)在豎直方向上是相反的。所以從紋理讀取顏色的時(shí)候,要做一個(gè) y 坐標(biāo)的轉(zhuǎn)換。

接下來,我們通過 UITouch 來獲取觸摸點(diǎn)的位置,然后算出歸一化的頂點(diǎn)坐標(biāo)。

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  [super touchesMoved:touches withEvent:event];
  
  [self addPointWithTouches:touches];
}

但是由于 iOS 系統(tǒng)觸摸事件的派發(fā)頻率有限,我們最終得到的只能是稀疏的點(diǎn)。如下圖所示,每個(gè)觸摸點(diǎn)之間的間隔會(huì)比較大。

二、怎么繪制密集的點(diǎn)

很容易想到,只需要在兩個(gè)點(diǎn)之間,按照一定的密度進(jìn)行插值,就可以繪制出連續(xù)的軌跡。

但是很明顯,我們的繪制結(jié)果是折線,并不平滑。

三、怎么使曲線變平滑

解決點(diǎn)連接不平滑的問題,一般是使用貝塞爾曲線。這種方案在 MFPaintView 中也得到了很好的應(yīng)用。

具體的做法是使用 兩個(gè)頂點(diǎn)間的中點(diǎn)一個(gè)頂點(diǎn) ,來構(gòu)造一條貝塞爾曲線。如下圖,圖中的 3 個(gè) 紅點(diǎn) 被用來構(gòu)造一條貝塞爾曲線。

于是,我們的問題就變成了 怎么在 OpenGL ES 中繪制貝塞爾曲線 。相當(dāng)于已知貝塞爾曲線的 3 個(gè)關(guān)鍵點(diǎn),反向來求曲線上的點(diǎn)序列。

我們知道貝塞爾曲線的方程是 P = (1 - t)^2 * P0 + 2 * t * (1 - t) * P1 + t^2 * P2 , t 是唯一的變量,其取值范圍是 0 ~ 1 。

所以我們可以采取線性取值的方式,每一條貝塞爾曲線取 n 個(gè)點(diǎn)( n 是個(gè)確定的常量)。只要依次往方程中代入 1 / n 、 2 / n 、 ... n / n ,就可以得到一個(gè)點(diǎn)序列。

先將 n 取一個(gè)比較小的值,這樣比較容易看出存在的問題。我們發(fā)現(xiàn), 點(diǎn)序列的間隔并不均勻 。原因有兩個(gè):

  • 不同貝塞爾曲線的長(zhǎng)度不一樣,使用同一個(gè) n 值,算出來的點(diǎn)的疏密程度肯定不同。
  • 由于貝塞爾曲線隨著 t 增長(zhǎng),曲線長(zhǎng)度的增長(zhǎng)并不是線性的。按照我們上面的算法,最終會(huì)得到的結(jié)果是 兩頭比較稀疏,中間比較密集 。

四、怎么生成均勻的點(diǎn)序列

貝塞爾曲線生成均勻的點(diǎn)序列,涉及到了一個(gè)經(jīng)典的「貝塞爾曲線勻速運(yùn)動(dòng)」問題。

這個(gè)問題的推導(dǎo)和計(jì)算比較復(fù)雜。如果你有興趣,可以閱讀一下文末的兩篇文章。由于我還不能完全領(lǐng)悟,就不在這里誤導(dǎo)大家了。

簡(jiǎn)單來說,就是我們通過一系列的操作,封裝了一個(gè)方法,只需要傳入貝塞爾曲線的 3 個(gè)關(guān)鍵點(diǎn)和筆觸尺寸,就可以獲取均勻的點(diǎn)序列。

+ (NSArray <NSValue *>*)pointsWithFrom:(CGPoint)from
                  to:(CGPoint)to
                control:(CGPoint)control
               pointSize:(CGFloat)pointSize;

下面我們固定貝塞爾曲線的 起始點(diǎn)控制點(diǎn) ,只移動(dòng) 終止點(diǎn) ,來驗(yàn)證一下這個(gè)方法是否可靠。

可以看到,在移動(dòng)過程中,點(diǎn)和點(diǎn)的距離基本是保持一致的,并且是均勻的。通過這個(gè)「神奇」的方法,我們終于畫出了平滑且均勻的曲線。

五、繪畫板功能實(shí)現(xiàn)

終于講完了最麻煩的部分,接下來簡(jiǎn)單介紹一下繪畫板基本功能的實(shí)現(xiàn)。

1、顏色混合

在以往的例子中,我們?cè)陂_始一次渲染之前,都會(huì)調(diào)用 glClear(GL_COLOR_BUFFER_BIT) 來清除畫布,因?yàn)槲覀儾幌MA羯洗蔚匿秩窘Y(jié)果。

但是對(duì)于一個(gè)繪畫板來說,我們要不斷地往畫布上畫東西,所以是希望保留上次結(jié)果的。因此,在繪制之前不能執(zhí)行清除的操作。

另外,由于我們的畫筆可能是半透明的,所以新繪制的顏色需要和畫布上已經(jīng)存在的顏色進(jìn)行混合。因此在繪制開始之前,需要開啟混合選項(xiàng)。

glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

2、筆觸調(diào)整

筆觸有 3 個(gè)屬性可以調(diào)整: 顏色、尺寸、形狀 。它們本質(zhì)上都是對(duì)點(diǎn)圖元的調(diào)整,通過 uniform 變量的形式,將顏色、尺寸、紋理傳入著色器并應(yīng)用。

3、橡皮擦

GLPaintView 在初始化的時(shí)候,需要傳入一個(gè)背景色參數(shù),當(dāng)用戶切換到橡皮擦功能的時(shí)候,內(nèi)部只是單純地將畫筆的顏色切換成背景色,于是就產(chǎn)生了橡皮擦的效果。

4、撤銷重做

撤銷重做功能需要依賴兩個(gè)棧來實(shí)現(xiàn)。我們把用戶的手指從 按下屏幕到離開屏幕 這一過程中產(chǎn)生的數(shù)據(jù),定義為一個(gè)操作對(duì)象,這個(gè)操作對(duì)象保存了歸一化后的點(diǎn)序列,以及點(diǎn)的屬性。

@interface MFPaintModel : NSObject

/// 筆刷尺寸
@property (nonatomic, assign) CGFloat brushSize;
/// 筆刷顏色
@property (nonatomic, strong) UIColor *brushColor;
/// 筆刷模式
@property (nonatomic, assign) GLPaintViewBrushMode brushMode;
/// 筆觸紋理圖片文件名
@property (nonatomic, copy) NSString *brushImageName;
/// 點(diǎn)序列
@property (nonatomic, copy) NSArray<NSValue *> *points;

@end

撤銷重做的代碼實(shí)現(xiàn)大概像這樣子:

- (void)undo {
  if ([self.operationStack isEmpty]) {
    return;
  }
  MFPaintModel *model = self.operationStack.topModel;
  [self.operationStack popModel];
  [self.undoOperationStack pushModel:model];
  
  [self reDraw];
}

- (void)redo {
  if ([self.undoOperationStack isEmpty]) {
    return;
  }
  MFPaintModel *model = self.undoOperationStack.topModel;
  [self.undoOperationStack popModel];
  [self.operationStack pushModel:model];
  
  [self drawModel:model];
}

需要注意的是,由于 撤銷操作 需要先清除畫布,所以每次都需要重繪。而 重做操作 可以利用上次繪制的結(jié)果,所以每次只需要繪制一個(gè)步驟即可。

源碼

請(qǐng)到 GitHub 上查看完整代碼。

到此這篇關(guān)于在iOS中使用OpenGL ES實(shí)現(xiàn)繪畫板的方法的文章就介紹到這了,更多相關(guān)iOS 繪畫板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • iOS 隱私權(quán)限和通過openURL實(shí)現(xiàn)跳轉(zhuǎn)實(shí)例

    iOS 隱私權(quán)限和通過openURL實(shí)現(xiàn)跳轉(zhuǎn)實(shí)例

    這篇文章主要介紹了iOS 隱私權(quán)限和通過openURL實(shí)現(xiàn)跳轉(zhuǎn)實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • iOS利用手機(jī)攝像頭測(cè)心率

    iOS利用手機(jī)攝像頭測(cè)心率

    這篇文章主要為大家詳細(xì)介紹了iOS利用手機(jī)攝像頭測(cè)心率的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • iOS WKWebView無法處理URL Scheme和App Store鏈接的問題解決

    iOS WKWebView無法處理URL Scheme和App Store鏈接的問題解決

    這篇文章主要給大家介紹了關(guān)于iOS WKWebView無法處理URL Scheme和App Store鏈接的問題解決的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • iOS通過多種方式創(chuàng)建控制器

    iOS通過多種方式創(chuàng)建控制器

    這篇文章主要為大家詳細(xì)介紹了iOS通過多種方式創(chuàng)建控制器的相關(guān)方法,感興趣的小伙伴們可以參考一下
    2016-04-04
  • NSString屬性何時(shí)用strong何時(shí)用copy?

    NSString屬性何時(shí)用strong何時(shí)用copy?

    相信各位iOS開發(fā)者們都考慮過這個(gè)問題,平時(shí)寫NSString的屬性時(shí)都用copy,那strong要何時(shí)用呢?下面這篇文章就來看一下什么時(shí)候應(yīng)該用copy,什么時(shí)候應(yīng)該用strong。有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-12-12
  • 詳解iOS中多個(gè)網(wǎng)絡(luò)請(qǐng)求的同步問題總結(jié)

    詳解iOS中多個(gè)網(wǎng)絡(luò)請(qǐng)求的同步問題總結(jié)

    這篇文章主要介紹了詳解iOS中多個(gè)網(wǎng)絡(luò)請(qǐng)求的同步問題總結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • iOS自定義水平滾動(dòng)條、進(jìn)度條

    iOS自定義水平滾動(dòng)條、進(jìn)度條

    這篇文章主要為大家詳細(xì)介紹了iOS自定義水平滾動(dòng)條、進(jìn)度條,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 談?wù)勚谱鱥OS Ad-Hoc測(cè)試應(yīng)用

    談?wù)勚谱鱥OS Ad-Hoc測(cè)試應(yīng)用

    這篇文章主要介紹了談?wù)勚谱鱥OS Ad-Hoc測(cè)試應(yīng)用,AD-HOC測(cè)試是指隨機(jī)測(cè)試,這種測(cè)試的特點(diǎn)是無特定的測(cè)試用例,有興趣的可以了解一下。
    2016-12-12
  • IOS中的七種手勢(shì)小結(jié)

    IOS中的七種手勢(shì)小結(jié)

    本文給大家總結(jié)了ios中的七種手勢(shì),手勢(shì)在開發(fā)在開發(fā)中經(jīng)常會(huì)用到,今天小編通過本文給大家詳細(xì)介紹下,需要的朋友參考下
    2016-11-11
  • 查看iOS Crash logs的方法

    查看iOS Crash logs的方法

    發(fā)布了一個(gè)應(yīng)用,用戶使用 的時(shí)候crash了,現(xiàn)在想調(diào)查為何crash,所以想在這里探討一下如何查看iphone 手機(jī)的crash logs
    2015-06-06

最新評(píng)論