簡體   English   中英

如何在opengl上為兩個重疊對象設置混合功能

[英]How to set blend function on opengl for two overlapping objects

嗨,我需要繪制一個圓角矩形。 圓角矩形繪制程序

我按照上圖的過程進行操作。 我首先畫了綠色矩形。 然后我畫了兩個黑色的矩形,然后在邊緣上畫了圓以使角變圓。 現在,執行此操作后得到的是下圖。

在此處輸入圖片說明

可以看出,角圓在與矩形重疊的部分上透明度較低。 但是,當不與矩形重疊時,透明度更高。 矩形的alpha設置為0.5f。 並且圓圈也具有0.5f的alpha。 因此,這就是為什么在重疊部分為白色而在非重疊部分為透明的原因。 我希望重疊的部分具有與矩形相同的透明度,以便看不到重疊的圓形部分。我的混合函數是glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 我試圖在這里更詳細地了解blend函數。 但是我什么也聽不懂。 我的代碼如下

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, (int) screenWidth, (int) screenHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0.0f, (double)screenWidth / screenHeight, 0.0f, 1.0f, -1.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

glEnable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(3, GL_FLOAT, 0, bubbleMiddleRectStartCoord);
glColorPointer(4, GL_FLOAT, 0, rectColor);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glVertexPointer(3, GL_FLOAT, 0, bubbleTopRectStartCoord);
glColorPointer(4, GL_FLOAT, 0, rectColor);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glVertexPointer(3, GL_FLOAT, 0, bubbleBottomRectStartCoord);
glColorPointer(4, GL_FLOAT, 0, rectColor);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

//smooth edge of the bubble rectangle
drawCircle(triangleAmount,bubbleEdgeRadius,bubbleMiddleRectStartCoord->upperLeft.x+bubbleEdgeRadius,bubbleMiddleRectStartCoord->upperLeft.y,255,255,255,128);
drawCircle(triangleAmount,bubbleEdgeRadius,bubbleMiddleRectStartCoord->lowerLeft.x+bubbleEdgeRadius,bubbleMiddleRectStartCoord->lowerLeft.y,255,255,255,128);
drawCircle(triangleAmount,bubbleEdgeRadius,bubbleMiddleRectStartCoord->upperRight.x-bubbleEdgeRadius,bubbleMiddleRectStartCoord->upperRight.y,255,255,255,128);
drawCircle(triangleAmount,bubbleEdgeRadius,bubbleMiddleRectStartCoord->lowerRight.x-bubbleEdgeRadius,bubbleMiddleRectStartCoord->lowerRight.y,255,255,255,128);

glDisableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

glDisableClientState(GL_COLOR_MATERIAL);
glDisable(GL_TEXTURE_2D);
swapBuffers();

rectColor有價值

GLfloat rectColor[]=
{
    1.0f,1.0f,1.0f,0.5,
    1.0f,1.0f,1.0f,0.5,
    1.0f,1.0f,1.0f,0.5,
    1.0f,1.0f,1.0f,0.5
};

drawCircle函數為圓生成點並進行繪制。 該函數的繪圖部分是

glVertexPointer(2, GL_FLOAT, 0, vertices);
 glColorPointer(4, GL_UNSIGNED_BYTE, 0, color);
 glDrawArrays(GL_TRIANGLE_FAN, 0, triangleAmount+2);

誰能幫助我解決問題? 謝謝。

編輯:這是使用這兩個混合函數后的外觀。 在此處輸入圖片說明

我看到了要進行此操作的地方,並看到了結果,您可能只需要在繪制蒙版(3個矩形和4個圓形)時禁用混合,然后使用glBlendFunc(GL_DST_ALPHA, GL_ZERO) 盡管這僅在場景尚未繪制任何內容的情況下才有效。

為了解釋您所做的工作,您正在繪制帶有.5 alpha的白色並將其混合。 首先考慮像素顏色“目標”為(0,0,0,0),而傳入的“源”在您的情況下始終為(1,1,1,.5)。 可以說源顏色是“ S”,目標顏色是“ D”,而分量是(r,g,b,a),因此源alpha是“ Sa”,您在blend函數中寫的是:

output = S*S.a + D*(1.0-S.a) = 
(1,1,1,.5)*.5 + (0,0,0,0)*(1.0-.5) = 
(.5, .5, .5, .25) + (0,0,0,0) =
(.5, .5, .5, .25)

因此,當您在已繪制的矩形上繪制圓時:

output = S*S.a + D*(1.0-S.a) = 
(1,1,1,.5)*.5 + (.5, .5, .5, .25)*(1.0-.5) = 
(.5, .5, .5, .25) + (.25, .25, .25, .125) =
(.75, .75, .75, .375)

導致alpha差異。 因此,我希望您能夠理解這兩個參數在blend函數中的含義:第一個參數說明用於乘以源(傳入)顏色的因素,第二個參數說明如何與目標顏色相乘。 最后將它們加在一起。

因此,對於您的情況,您希望在繪制這些圖元的任何地方都將alpha通道強制為某個值。 要實現這一點,您需要S*1.0 + D*.0 ,其參數為glBlendFunc(GL_ONE, GL_ZERO) ,盡管這與禁用混合功能相同。 僅編寫此基元會生成透明度為.5的白色(灰色)圓角矩形,而其余所有元素都是完全透明的。 現在,在此之后,您需要設置混合功能,以將輸入的顏色與目標alpha glBlendFunc(GL_DST_ALPHA, GL_ZERO)

編輯:

到目前為止,我還不完全了解您想要實現的目標。 正如我上面提到的,如果您已經繪制了一些場景,這將不起作用。

要在現有場景上覆蓋一些復雜的對象(在這種情況下,該對象在某些部分上會自身重疊),使用模板緩沖區是最防彈的方法。 創建它很像深度緩沖區,但是您可以將其視為另一個顏色通道,很容易繪制它並在以后使用它,因此您可能希望在某些時候對其進行研究。

就您而言,可以肯定地說這是您的主要緩沖區並用於顯示。 在這種情況下,您可以只使用Alpha通道:

要僅繪制到Alpha通道,必須設置glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ,完成后,將所有參數設置為true。

  1. 要清除Alpha通道,您必須繪制具有所需Alpha某種顏色的全屏矩形(我建議您使用(1,1,1,1) )並僅繪制到Alpha通道
  2. 要繪制該蒙版(3個矩形和4個圓),請使用glBlendFunc(GL_ONE, GL_ZERO)和顏色(1,1,1, 1-desiredAlpha)
  3. 要繪制圓形標簽,請使用glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA)

因此,該過程將是:

//your background is drawn, time to overly labels
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glColor(1.0f, 1.0f, 1.0f, 1.0f);
//draw fullscreen rect
glBlendFunc(GL_ONE, GL_ZERO);
glColor(1.0f, 1.0f, 1.0f, 1.0f-.5f);
//draw 3 rects and 4 circles
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
//draw the label as a normal rect (the rounded parts will be trimmed because of alpha channel)

您可以在for循環中為所有標簽重復該操作。

我知道事情有些復雜,但是您要嘗試做的並不像看起來那樣容易。 我之所以向您介紹此解決方案,是因為這樣一來,您只需更改最少的代碼,通常我建議使用模板緩沖區(已經提到)或FBO(幀緩沖區對象)。 FBO系統將創建另一個幀緩沖區並為其添加紋理,將整個標簽對象繪制到該緩沖區,然后使用綁定的紋理將其繪制到主屏幕。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM