OpenGL ES Or Metal 顏色查找表LUT(Color Lookup Table)的理解

1.以下是關(guān)于Metal或者OpenGL ES顏色查找表LUT的理解萨咳,具體博客見落影大神落影l(fā)oyinglin
2.常見的圖像處理是對(duì)相鄰像素點(diǎn)顏色、像素點(diǎn)本身顏色做處理疫稿。

1.在對(duì)像素點(diǎn)本身顏色做處理的情況下培他,需要把某個(gè)顏色映射成另外一個(gè)顏色,LUT實(shí)際上就是一種顏色映射成另外一種我們想要的顏色的一種手段遗座。

2.LUT核心思路就是通過RGB三個(gè)顏色分量舀凛,映射為L(zhǎng)UT紋理的紋理坐標(biāo),再通過這個(gè)坐標(biāo)獲取到LUT紋理上對(duì)應(yīng)的顏色重新作為該RGB的顏色輸出途蒋,這樣就達(dá)到了一種顏色映射為另外一種顏色猛遍,其余不需要改變的顏色映射出來的還是原來的顏色,而且是可控的号坡,根據(jù)需求定制的懊烤。

3.那么人臉美膚(變白),紅唇等都是可以通過LUT實(shí)現(xiàn)的宽堆,比如人臉顏色腌紧,可能有幾種常見的顏色值,那么如果要美白的話畜隶,顏色查找表應(yīng)該有幾張與之對(duì)應(yīng)的LUT壁肋,根據(jù)檢測(cè)出來的膚色傳入不同的LUT紋理到Fragment Shader里面,達(dá)到不同膚色都可以達(dá)到相同的美白程度和效果籽慢。

3.對(duì)轉(zhuǎn)換流程的理解如下RGB->xyz(紋理坐標(biāo))->LUT(xy)->RGB

1.假如某個(gè)點(diǎn)的值是102浸遗,那么有102/4=25.5,映射結(jié)果為a[25] * 0.5+a[26] * 0.5箱亿,即兩邊各取一半跛锌;假如某個(gè)點(diǎn)的值是101,那么有101/4=25.25届惋,映射結(jié)果為a[25] * 0.25 + a[26] * 0.75察净,按照小數(shù)點(diǎn)進(jìn)行分配驾茴。

2.一個(gè)64 * 64像素點(diǎn)的紋理圖如下
我們的映射數(shù)組是colorConvert3[64][64][64],相當(dāng)于64個(gè)二維數(shù)組colorConvert2[64][64]氢卡。如果我們colorConvert2[i][j]的結(jié)果寫入一張64 * 64的圖片第(i, j)個(gè)像素點(diǎn),即用一張64 * 64的圖片來緩存這個(gè)結(jié)果晨缴,如下

每個(gè)64*64像素點(diǎn)的圖片.png

3.一個(gè)64 * 64 * 64像素點(diǎn)的紋理圖如下


顏色查找表.png

64 * 64 * 64的LUT只是一種顏色劃分顆粒的方式译秦,可以是別的數(shù)值,但是击碗,涉及到GPU的計(jì)算量和有沒有太大的必要性筑悴,64應(yīng)該是比較合適的值,也就是說把RGB每一個(gè)分量分成64份稍途,這樣的顆粒度阁吝, 顏色值是0.0-1.0,與之對(duì)應(yīng)的就是0.0 - 63

4.同時(shí)為了有更好的過渡效果械拍,每次計(jì)算的時(shí)候我們可以用相鄰的結(jié)果進(jìn)行線性結(jié)合突勇。可能是相鄰的坷虑,也可能不是甲馋,但是顏色值相差不大。
映射關(guān)系計(jì)算原理迄损,通過RGB的b值定躏,再結(jié)合floor (向下取整,例如floor(25.9) = 25)和ceil (向上取整比如 (ceil(25.1) = 26)芹敌,獲取到相鄰或者不相鄰的LUT里面64個(gè)里面的其中2個(gè)顏色塊痊远,這一步獲取到了兩個(gè)顏色塊的具體位置

4. RGB中的B顏色分量在LUT64個(gè)顏色塊中確定兩個(gè)顏色塊

第1個(gè)顏色塊
比如b-> blueColor是22.5,那么第一個(gè)顏色塊為第2行氏捞,第6列的正方形

blueColor=22.5碧聪,則y=22/8=2,x=22-8*2=6幌衣,即是第2行矾削,第6個(gè)正方形;(因?yàn)閥是縱坐標(biāo))
quad1.y = floor(floor(blueColor) * 0.125);
quad1.x = floor(blueColor) - (quad1.y * 8.0);

第2個(gè)顏色塊
比如b-> blueColor是22.5豁护,那么第二個(gè)顏色塊為第2行哼凯,第7列的正方形

blueColor=22.5,則y=22/8=2楚里,x=23-8*2=6断部,即是第2行,第7個(gè)正方形班缎;(因?yàn)閥是縱坐標(biāo))
quad2.y = floor(ceil(blueColor) * 0.125);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);

以上是相鄰的情況蝴光,向上向下取整與8的整數(shù)倍有跨越的時(shí)候她渴,不相鄰的情況就出現(xiàn)了

第1個(gè)顏色塊
比如b-> blueColor是23.5,那么第一個(gè)顏色塊為第2行蔑祟,第7列的正方形

blueColor=23.5趁耗,則y=23/8=2,x=23-8*2=7疆虚,即是第2行苛败,第7個(gè)正方形;(因?yàn)閥是縱坐標(biāo))
quad1.y = floor(floor(blueColor) * 0.125);
quad1.x = floor(blueColor) - (quad1.y * 8.0);

第2個(gè)顏色塊
比如b-> blueColor是22.5径簿,那么第二個(gè)顏色塊為第3行罢屈,第0列的正方形

blueColor=23.5,則y=24/8=3篇亭,x=24-8*3=0缠捌,即是第3行,第0個(gè)正方形译蒂;(因?yàn)閥是縱坐標(biāo))
quad2.y = floor(ceil(blueColor) * 0.125);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
總結(jié):通過以上b分量的的映射關(guān)系曼月,可以確定兩個(gè)顏色塊。
5.通過RG兩個(gè)歸一化的顏色分量蹂随,計(jì)算每一顏色塊中xy=RG的值相對(duì)于整個(gè)LUT的紋理坐標(biāo)十嘿,即塊內(nèi)紋理坐標(biāo)相對(duì)于LUT的紋理坐標(biāo)計(jì)算

意思就是RG兩個(gè)值作為xy值是上面確定兩個(gè)顏色塊的紋理坐標(biāo),在結(jié)合對(duì)應(yīng)的百分比計(jì)算出岳锁,兩個(gè)顏色塊中的兩個(gè)點(diǎn)對(duì)應(yīng)到整個(gè)LUT紋理的紋理坐標(biāo)

constant float SquareSize = 63.0 / 512.0;
constant float stepSize = 0.0; //0.5 / 512.0;
texPos1.x = (quad1.x * 0.125) + stepSize + (SquareSize * textureColor.r);
texPos1.y = (quad1.y * 0.125) + stepSize + (SquareSize * textureColor.g);

float2 texPos2; // 同上
texPos2.x = (quad2.x * 0.125) + stepSize + (SquareSize * textureColor.r);
texPos2.y = (quad2.y * 0.125) + stepSize + (SquareSize * textureColor.g);
總結(jié):通過RG兩個(gè)顏色分量確定了绩衷,具體的兩個(gè)點(diǎn)在LUT中的紋理坐標(biāo)
6.根據(jù)紋理坐標(biāo)獲取兩個(gè)新新的顏色值,再混合激率,不同的 LUT表咳燕,顏色值可以自定義

兩個(gè)內(nèi)建函數(shù)解析

    // genType mix (genType x, genType y, genType a)、genType mix (genType x, genType y, float a)
    
    //返回線性混合的x和y乒躺,如:x?(1?a)+y?a
    
    // fract(x) 返回x-floor(x)招盲,即返回x的小數(shù)部分

獲取混合后的新的顏色值返回

進(jìn)行顏色混合,常用的顏色混合公式 AColor * (1.0 - alpha) + BColor * alpha

// 正方形1的顏色值
float4 newColor1 = lookupTableTexture.sample(textureSampler, texPos1); 

// 正方形2的顏色值
float4 newColor2 = lookupTableTexture.sample(textureSampler, texPos2);

// 根據(jù)小數(shù)點(diǎn)的部分進(jìn)行mix
float4 newColor = mix(newColor1, newColor2, fract(blueColor)); 

return float4(newColor.rgb, textureColor.w); //不修改alpha值
7.完整的vertex和fragment函數(shù)
#include <metal_stdlib>
#import "LYShaderTypes.h"

using namespace metal;

typedef struct
{
    float4 clipSpacePosition [[position]]; // position的修飾符表示這個(gè)是頂點(diǎn)
    
    float2 textureCoordinate; // 紋理坐標(biāo)嘉冒,會(huì)做插值處理
    
} RasterizerData;

vertex RasterizerData // 返回給片元著色器的結(jié)構(gòu)體
vertexShader(uint vertexID [[ vertex_id ]], // vertex_id是頂點(diǎn)shader每次處理的index曹货,用于定位當(dāng)前的頂點(diǎn)
             constant LYVertex *vertexArray [[ buffer(LYVertexInputIndexVertices) ]]) { // buffer表明是緩存數(shù)據(jù),0是索引
    RasterizerData out;
    out.clipSpacePosition = vertexArray[vertexID].position;
    out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
    return out;
}

constant float SquareSize = 63.0 / 512.0;
constant float stepSize = 0.0; //0.5 / 512.0;

fragment float4
samplingShader(RasterizerData input [[stage_in]], // stage_in表示這個(gè)數(shù)據(jù)來自光柵化讳推。(光柵化是頂點(diǎn)處理之后的步驟顶籽,業(yè)務(wù)層無法修改)
               texture2d<float> normalTexture [[ texture(LYFragmentTextureIndexNormal) ]], // texture表明是紋理數(shù)據(jù),LYFragmentTextureIndexNormal是索引
               texture2d<float> lookupTableTexture [[ texture(LYFragmentTextureIndexLookupTable) ]]) // texture表明
{
    constexpr sampler textureSampler (mag_filter::linear,
                                      min_filter::linear); // sampler是采樣器
    float4 textureColor = normalTexture.sample(textureSampler, input.textureCoordinate); //正常的紋理顏色
    
    float blueColor = textureColor.b * 63.0; // 藍(lán)色部分[0, 63] 共64種
    
    float2 quad1; // 第一個(gè)正方形的位置, 假如blueColor=22.5银觅,則y=22/8=2礼饱,x=22-8*2=6,即是第2行,第6個(gè)正方形镊绪;(因?yàn)閥是縱坐標(biāo))
    quad1.y = floor(floor(blueColor) * 0.125);
    quad1.x = floor(blueColor) - (quad1.y * 8.0);
    
    float2 quad2; // 第二個(gè)正方形的位置匀伏,同上。注意x蝴韭、y坐標(biāo)的計(jì)算拴孤,還有這里用int值也可以洒琢,但是為了效率使用float
    quad2.y = floor(ceil(blueColor) * 0.125);
    quad2.x = ceil(blueColor) - (quad2.y * 8.0);
    
    float2 texPos1; // 計(jì)算顏色(r,b,g)在第一個(gè)正方形中對(duì)應(yīng)位置
    /*
     quad1是正方形的坐標(biāo)歧蒋,每個(gè)正方形占紋理大小的1/8帐姻,即是0.125李皇,所以quad1.x * 0.125是算出正方形的左下角x坐標(biāo)
     stepSize這里設(shè)置為0精算,可以忽略蒙谓;
     SquareSize是63/512剥啤,一個(gè)正方形小格子在整個(gè)圖片的紋理寬度
     */
    
    texPos1.x = (quad1.x * 0.125) + stepSize + (SquareSize * textureColor.r);
    texPos1.y = (quad1.y * 0.125) + stepSize + (SquareSize * textureColor.g);
    
    float2 texPos2; // 同上
    texPos2.x = (quad2.x * 0.125) + stepSize + (SquareSize * textureColor.r);
    texPos2.y = (quad2.y * 0.125) + stepSize + (SquareSize * textureColor.g);
    
    float4 newColor1 = lookupTableTexture.sample(textureSampler, texPos1); // 正方形1的顏色值
    float4 newColor2 = lookupTableTexture.sample(textureSampler, texPos2); // 正方形2的顏色值
    
    float4 newColor = mix(newColor1, newColor2, fract(blueColor)); // 根據(jù)小數(shù)點(diǎn)的部分進(jìn)行mix
    // genType mix (genType x, genType y, genType a)芝雪、genType mix (genType x, genType y, float a)
    
    //返回線性混合的x和y减余,如:x?(1?a)+y?a
    
    // fract(x) 返回x-floor(x),即返回x的小數(shù)部分
    

    return float4(newColor.rgb, textureColor.w); //不修改alpha值
}

8.總結(jié)

上面是一個(gè)正常的顏色查找表LUT惩系,所以位岔,渲染出來的效果更清晰,如果想要不同的顏色效果只需要 原RGB->LUT(xyz)->LUT(RGB)對(duì)應(yīng)不同的LUT即可堡牡。靈活度很高的自定義像素顏色的一種手段抒抬,以上是Metal的代碼,OpenGL ES與之同理晤柄,再次感謝大神 落影l(fā)oyinglin擦剑!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芥颈,隨后出現(xiàn)的幾起案子惠勒,更是在濱河造成了極大的恐慌,老刑警劉巖爬坑,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纠屋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盾计,警方通過查閱死者的電腦和手機(jī)售担,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來署辉,“玉大人族铆,你說我怎么就攤上這事≌切剑” “怎么了骑素?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我献丑,道長(zhǎng)末捣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任创橄,我火速辦了婚禮箩做,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妥畏。我一直安慰自己邦邦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布醉蚁。 她就那樣靜靜地躺著燃辖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪网棍。 梳的紋絲不亂的頭發(fā)上黔龟,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音滥玷,去河邊找鬼氏身。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惑畴,可吹牛的內(nèi)容都是我干的蛋欣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼如贷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼陷虎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起倒得,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤泻红,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后霞掺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谊路,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年菩彬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缠劝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骗灶,死狀恐怖惨恭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耙旦,我是刑警寧澤脱羡,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響锉罐,放射性物質(zhì)發(fā)生泄漏帆竹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一脓规、第九天 我趴在偏房一處隱蔽的房頂上張望栽连。 院中可真熱鬧,春花似錦侨舆、人聲如沸秒紧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熔恢。三九已至,卻和暖如春臭笆,著一層夾襖步出監(jiān)牢的瞬間绩聘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工耗啦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人机杜。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓帜讲,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親椒拗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子似将,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容