题
在不使用 glLineWidth 的情况下绘制可变宽度线的最佳方法是什么?只画一个矩形?各种平行线?以上都不是?
解决方案
假设你的原始点是 (x1,y1) -> (x2,y2)。使用以下点 (x1-width/2, y1)、(x1+width/2,y1)、(x2-width/2, y2)、(x2+width/2,y2) 构造一个矩形,然后使用四边形/三边形来绘制它。这是简单天真的方法。请注意,对于大线宽,您会得到奇怪的端点行为。那么你真正想做的是使用向量数学进行一些智能平行线计算(这应该不会那么糟糕)。由于某种原因,我想到了点/叉积和矢量投影。
其他提示
您可以绘制两个三角形:
// Draws a line between (x1,y1) - (x2,y2) with a start thickness of t1 and
// end thickness t2.
void DrawLine(float x1, float y1, float x2, float y2, float t1, float t2)
{
float angle = atan2(y2 - y1, x2 - x1);
float t2sina1 = t1 / 2 * sin(angle);
float t2cosa1 = t1 / 2 * cos(angle);
float t2sina2 = t2 / 2 * sin(angle);
float t2cosa2 = t2 / 2 * cos(angle);
glBegin(GL_TRIANGLES);
glVertex2f(x1 + t2sina1, y1 - t2cosa1);
glVertex2f(x2 + t2sina2, y2 - t2cosa2);
glVertex2f(x2 - t2sina2, y2 + t2cosa2);
glVertex2f(x2 - t2sina2, y2 + t2cosa2);
glVertex2f(x1 - t2sina1, y1 + t2cosa1);
glVertex2f(x1 + t2sina1, y1 - t2cosa1);
glEnd();
}
好的,这个怎么样:(奥兹加)
A / \ / \ . p1 \ / \ / D B - .p2 - - - C
所以AB是 width1
CD 是 width2
.
然后,
// find line between p1 and p2
Vector p1p2 = p2 - p1 ;
// find a perpendicular
Vector perp = p1p2.perpendicular().normalize()
// Walk from p1 to A
Vector A = p1 + perp*(width1/2)
Vector B = p1 - perp*(width1/2)
Vector C = p2 - perp*(width2/2)
Vector D = p2 - perp*(width2/2)
// wind triangles
Triangle( A, B, D )
Triangle( B, D, C )
请注意,此算法可能存在 CW/CCW 绕线问题 - 如果上图中 perp 计算为 (-y, x),则它将是 CCW 绕线,如果 (y, -x) 则它将是 CW 绕线。
对于那些正在寻找良好解决方案的人来说,此代码是使用 LWJGL 编写的,但可以轻松适应 OpenGL 的任何实现。
import java.awt.Color;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector2f;
public static void DrawThickLine(int startScreenX, int startScreenY, int endScreenX, int endScreenY, Color color, float alpha, float width) {
Vector2f start = new Vector2f(startScreenX, startScreenY);
Vector2f end = new Vector2f(endScreenX, endScreenY);
float dx = startScreenX - endScreenX;
float dy = startScreenY - endScreenY;
Vector2f rightSide = new Vector2f(dy, -dx);
if (rightSide.length() > 0) {
rightSide.normalise();
rightSide.scale(width / 2);
}
Vector2f leftSide = new Vector2f(-dy, dx);
if (leftSide.length() > 0) {
leftSide.normalise();
leftSide.scale(width / 2);
}
Vector2f one = new Vector2f();
Vector2f.add(leftSide, start, one);
Vector2f two = new Vector2f();
Vector2f.add(rightSide, start, two);
Vector2f three = new Vector2f();
Vector2f.add(rightSide, end, three);
Vector2f four = new Vector2f();
Vector2f.add(leftSide, end, four);
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor4f(color.getRed(), color.getGreen(), color.getBlue(), alpha);
GL11.glVertex3f(one.x, one.y, 0);
GL11.glVertex3f(two.x, two.y, 0);
GL11.glVertex3f(three.x, three.y, 0);
GL11.glVertex3f(four.x, four.y, 0);
GL11.glColor4f(1, 1, 1, 1);
GL11.glEnd();
}
如果您偶然编写软件光栅器,则另一种方法是在像素着色阶段和颜色像素中使用重心坐标 当重心坐标之一接近 0 时. 。你给予的余量越多,线条就会越粗。
今天早些时候我也不得不做同样的事情。
用于创建一条跨越的线 (x1,y1) -> (x2,y2)
给定的 width
, ,一个非常简单的方法是变换一个简单的单位大小的正方形跨度 (0., -0.5) -> (1., 0.5)
使用:
glTranslatef(...)
将其移动到您想要的位置(x1,y1)
地点;glScalef(...)
将其向右缩放length
和期望的width
:使用length = sqrt( (x2-x1)^2 + (y2-y1)^2 )
或任何其他低复杂度近似;glRotatef(...)
到angle
它到正确的方向:使用angle = atan2(y2-y1, x2-x1)
.
单位正方形非常简单地由两个三角形带创建 GL_TRIANGLE_STRIP
, ,经过上述转换后就变成了实线。
这里的负担主要落在 OpenGL(和您的图形硬件)上,而不是您的应用程序代码上。上面的过程很容易通过包围变成一个通用函数 glPushMatrix()
和 glPopMatrix()
来电。
一个矩形(即GL_QUAD 或两个 GL_TRIANGLES)听起来像是你最好的选择,不确定我能想到任何其他方式。