Metal 如何高效地繪製粗直線?
Metal 的繪製直線沒有設置線寬的選項,Shading 語言也沒有相關的內置標識,那麼如何能高效的繪製粗直線?只能用三角形模擬直線的方案嗎?OpenGL ES 裡面有設置線寬的選項,那麼 OpenGL EA 底層繪製粗線條是什麼演算法實現的?
建議可渲染粗直線的四邊形(OBB),然後在 fragment shader 按 SDF 來 alpha-blend [1],就能得到抗鋸齒的粗直線。除了用膠囊體 SDF,也可用長方形 SDF,可參考[2]。
[1] 用 C 語言畫直線
[2] fractals, computer graphics, mathematics, demoscene and more
OpenGLES 那個線寬基本只有1.0像素的 形同虛設
繪製直線有以下幾個要求
1.線寬的單位應該是像素 所以線段最終在屏幕上的粗細應該只和屏幕空間有關
2.線段末端也應該只和屏幕空間的線段朝向有關
3.線段最終的深度以及所有輸出屬性應該只在「長軸」上有變化 而不應該在"寬軸"上有變化
所以做法我想應該是
在頂點著色器把線段的兩個點轉換到裁剪空間
然後在幾何著色器中
通過viewport矩陣 得到屏幕空間中線段的兩個端點
然後每個端點朝正交於線段方向的兩個方向移動線段粗細個單位
每個端點會得到2個新的頂點
這兩個頂點的深度以及所有輸出屬性應該和該端點的是一樣的
然後通過viewport的逆 把這些頂點轉換回裁剪空間
然後通過這4個頂點構造一個矩形
就能光柵化了
恭喜,使用3D引擎繪製2D矢量圖,這是一個遠古巨坑。
最一勞永逸的方式,大概是:在CPU端把曲線輪廓算出來,GPU基本只管柵格化、片元運算那些事。
目前只想實現最簡化的實現,只需要在小線寬下顯示正常就可以了,性能瓶頸發生在 http://www.zhihu.com/question/64616044 ,這個轉換無論放在CPU還是GPU,性能影響都很嚴重。
#include &
using namespace metal;
struct InputVertex {
float2 position;
float4 color;
};
struct Vertex {
float4 position [[position]];
float4 color;
};
struct Point
{
float2 position;
float4 color;
};
struct Line
{
Point begin;
Point end;
};
struct Uniforms {
float4x4 modelMatrix;
};
vertex Vertex vertex_func(constant Line *lines [[buffer(0)]],
constant Uniforms uniforms [[buffer(1)]],
uint vertexId [[vertex_id]],
uint instanceId [[instance_id]]) {
float4x4 matrix = uniforms.modelMatrix;
float thickness = 0.004;
Line line = lines[instanceId];
uint index = vertexId % 4;
float4 startPosition = matrix * float4(line.begin.position.x,line.begin.position.y ,0 ,1);
float4 endPosition = matrix * float4(line.end.position.x,line.end.position.y ,0 ,1);
startPosition = float4(startPosition.x,startPosition.y ,startPosition.z,startPosition.w);
endPosition = float4(endPosition.x,endPosition.y ,endPosition.z,endPosition.w);
float4 position;
float4 color;
float4 v = endPosition - startPosition;
float2 p0 = float2(startPosition.x,startPosition.y);
float2 v0 = float2(v.x,v.y);
float2 v1 = thickness * normalize(v0) * float2x2(float2(0,-1),float2(1,0));
if (index == 0)
{
float2 pa = p0 + v1;
position = float4(pa.x,pa.y,0,1);
color = line.begin.color;
}
else if (index == 1)
{
float2 pb = p0 - v1;
position = float4(pb.x,pb.y,0,1);
color = line.begin.color;
}
else if (index == 2)
{
float2 pc = p0 - v1 + v0;
position = float4(pc.x,pc.y,0,1);
color = line.end.color;
}
else if (index == 3)
{
float2 pd = p0 + v1 + v0;
position = float4(pd.x,pd.y,0,1);
color = line.end.color;
}
Vertex out;
out.position = position;
out.color = color;
return out;
}
fragment float4 fragment_func(Vertex vert [[stage_in]]) {
return vert.color;
}
推薦閱讀: