1. 程式人生 > 其它 >取得貝塞爾曲線x座標的y值_細分曲線

取得貝塞爾曲線x座標的y值_細分曲線

技術標籤:取得貝塞爾曲線x座標的y值

本文是OpenGL 4.0 Shading Language Cookbook的學習筆記。

在本文,我們將會使用曲面細分著色器繪製三次貝塞爾曲線。貝塞爾曲線是由四個控制點定義的引數曲線。這些控制點確定了整個曲線的形狀。第一和第四個控制點定義了曲線的開端和終端,剩下兩個頂點定義了曲線的形狀,但這兩個頂點並不一定位於曲線上。曲線通過對這四個控制點使用一組調配函式進行插值。調配函式定義了每個控制點的座標對曲線的貢獻。對於貝塞爾曲線,它的調配函式也被叫做伯恩斯坦多項式

587332f340626195e6e08c8879ad7a9e.png

上面公式中的第一項是二項式係數函式(在下面給出),n是多項式的度,i是多項式的個數,t是引數項。

95353bf789b415e2d294ad3f86b17931.png

貝塞爾曲線的一般引數形式由伯恩斯坦多項式的乘積和構成。

9aaecd77a9e5ad3dec5979adf7336c60.png

在本例,我們繪製一條三次貝塞爾曲線,它使用了4個控制點(n=3)。

f2299a862870d2247f052db53902174c.png

三次伯恩斯坦多項式為:

cf5ddb7b5a2d7f0e08cc357f27bada8e.png

準備

前面提到,OpenGL的細分功能包含兩個階段。它們是曲面細分控制著色器(TCS)和曲面細分計算著色器(TES)。我在TCS中定義貝塞爾曲線的線段數量(通過定義細分級別),在TES中我們計算貝塞爾曲線在特殊頂點的位置。下圖使用了三個不同的細分級別進行繪製。左面的細分級別為3,中間的細分級別為5,右面的細分級別為30。圖中的小方塊為控制點。

b171b3bb55877fcf8ad383b80474785c.png

貝塞爾曲線的控制點被作為包含四個頂點的Patch圖元被髮送到管線的下一階段。Patch圖元是使用者自定義的圖元型別。它是一組使用者自己選擇的可以用於做任何事情的頂點集合。TCS對Patch圖元的每個頂點執行一次。TES的執行次數依賴TPG生成的頂點個數。曲面細分的最後階段會生成一系列圖元,對於本例,我們生成的是連續線段。

TCS的一部分工作就是定義細分級別。不嚴謹地說,細分級別和生成的頂點數是正相關的。對於本例,TCS將會生成線條圖元,因此這裡的細分級別就是線條中線段的個數。TCS生成的頂點都關聯著一個範圍在0到1之間的細分座標。這個座標值對應前面貝塞爾曲線式子中的引數變數t。

實際上,TCS生成了一系列獨立的線條。位於線條中的頂點都有一個u座標和一個v座標。u座標的範圍在0到1之間,v座標對每個獨立的線條來說是常量。u座標和v座標的組合數量和兩個獨立的細分級別相關。對於本例,我們只有一條線條,所以第二細分級別(v)總為1。

TES的任務主要是決定頂點的位置。在TES中,我們可以訪問頂點所關聯的u和v座標,同時也可以訪問(只讀)Patch圖元所有頂點資訊。我們可以通過上面的引數方程來決定頂點的位置,使用u作為引數,求式子的值(用u替換式子中的t)。

準備

  • 下面是本例使用到的一些重要的Uniform變數:
  • NumSegments:生成的線段數目。
  • NumStrips:生成的獨立線條數目。對於本例,我們設定為1。
  • LineColor:線條的顏色。

在本例,我們使用了四個著色器,分別是頂點著色器,片段著色器,曲面細分控制著色器,曲面細分計算著色器。

實現

我們採取以下步驟生成貝塞爾曲線:

1. 使用下面程式碼作為頂點著色器:

layout (location = 0 ) in vec2 VertexPosition;
void main()
{
	gl_Position = vec4(VertexPosition, 0.0, 1.0);
}

2. 使用下面的程式碼作為曲面細分控制著色器:

layout( vertices=4 ) out;
uniform int NumSegments;
uniform int NumStrips;
void main()
{
	// Pass along the vertex position unmodified
	gl_out[gl_InvocationID].gl_Position =
	gl_in[gl_InvocationID].gl_Position;
	// Define the tessellation levels
	gl_TessLevelOuter[0] = float(NumStrips);
	gl_TessLevelOuter[1] = float(NumSegments);
}

3. 使用下面的程式碼作為曲面細分計算著色器:

layout( isolines ) in;
uniform mat4 MVP; // projection * view * model
void main()
{
	// The tessellation u coordinate
	float u = gl_TessCoord.x;
	// The patch vertices (control points)
	vec3 p0 = gl_in[0].gl_Position.xyz;
	vec3 p1 = gl_in[1].gl_Position.xyz;
	vec3 p2 = gl_in[2].gl_Position.xyz;
	vec3 p3 = gl_in[3].gl_Position.xyz;
	float u1 = (1.0 - u);
	float u2 = u * u;
	// Bernstein polynomials evaluated at u
	float b3 = u2 * u;
	float b2 = 3.0 * u2 * u1;
	float b1 = 3.0 * u * u1 * u1;
	float b0 = u1 * u1 * u1;
	// Cubic Bezier interpolation
	vec3 p =p0 * b0 + p1 * b1 + p2 * b2 + p3 *b3;
	gl_Position = MVP * vec4(p, 1.0);
}

4. 使用下面的程式碼作為片段著色器:

uniform vec4 LineColor;
layout ( location = 0 ) out vec4 FragColor;
void main()
{
	FragColor = LineColor;
}

5. 我們需要在OpenGL程式中呼叫glPatchParameter函式定義每個Patch圖元的頂點數量。

glPatchParameteri( GL_PATCH_VERTICES, 4);

6. 使用GL_PATCHES圖元型別渲染控制點:

glDrawArrays(GL_PATCHES, 0, 4);

原理

頂點著色器只做傳遞引數的作用,它將頂點位置資訊不經修改地傳遞給渲染管線的下一階段。

在曲面細分控制著色器中,我們定義了輸出的Patch圖元的頂點個數。

layout (vertices = 4) out;

注意這個數字和細分處理產生的頂點數目不是同一個。在本例,我們的Patch圖元使用了4個控制點。

TCS的main函式將輸入的頂點位置資訊不經修改地輸出到下一渲染管線的下一階段。陣列gl_out和gl_in包含了Patch圖元的所有頂點資訊。我們可以對索引為gl_InvocationID的陣列元素進行讀寫操作,對於陣列中的其它元素,我們只能進行讀操作。

接著,我們在TCS裡通過gl_TessLevelOuter陣列設定細分級別。gl_TessLevelOuter的元素的變數型別不是整型而是浮點型別,OpenGL會自動將它四捨五入到整數。

gl_TessLevelOuter陣列的第一個元素定義了將被生成的獨立線條的個數。每個獨立線條包含一個常量值v。在本例,因為我們只有一個獨立線條,gl_TessLevelOuter[0]的值應該為1。陣列的第二個元素定義了將會生成的線段個數。線條中的每個頂點將會包含一個範圍在0到1之間的u座標。

在TES中,我們需要定義輸入圖元型別:

layout (isolines) in;

上面的程式碼指明瞭TPG進行細分的型別,它還可以是quads和triangles。

變數gl_TessCoord包含了細分的u和v座標。由於我們值進行一維細分,所以只用到u座標,它對應變數gl_TessCoord的x分量。

接下來,我們陣列gl_in訪問四個控制點的位置資訊。

然後,我們進行了三次伯恩斯坦多項式的計算,並將結果儲存在變數b0,b1,b2和b3中。接著,我們使用貝塞爾曲線方程計算曲線的插值位置,然後將位置座標轉換到剪下空間賦值給變數gl_Position。

片段著色器對片段直接使用LineColor指定的顏色著色片段。