我正在使用下面的算法来生成四边形,然后将其渲染为轮廓
http://img810.imageshack.us/img810/8530/uhohz.png
在图像上看到的问题是,有时线条太细,以至于它们应该始终保持相同的宽度。我的算法会找到4第一个2顶点,然后下一个顶点是前一个顶点2。这会创建连接的线路,但似乎并不总是有效。我该如何解决?
4
2
这是我的算法:
void OGLENGINEFUNCTIONS::GenerateLinePoly(const std::vector<std::vector<GLdouble>> &input, std::vector<GLfloat> &output, int width) { output.clear(); if(input.size() < 2) { return; } int temp; float dirlen; float perplen; POINTFLOAT start; POINTFLOAT end; POINTFLOAT dir; POINTFLOAT ndir; POINTFLOAT perp; POINTFLOAT nperp; POINTFLOAT perpoffset; POINTFLOAT diroffset; POINTFLOAT p0, p1, p2, p3; for(unsigned int i = 0; i < input.size() - 1; ++i) { start.x = static_cast<float>(input[i][0]); start.y = static_cast<float>(input[i][1]); end.x = static_cast<float>(input[i + 1][0]); end.y = static_cast<float>(input[i + 1][1]); dir.x = end.x - start.x; dir.y = end.y - start.y; dirlen = sqrt((dir.x * dir.x) + (dir.y * dir.y)); ndir.x = static_cast<float>(dir.x * 1.0 / dirlen); ndir.y = static_cast<float>(dir.y * 1.0 / dirlen); perp.x = dir.y; perp.y = -dir.x; perplen = sqrt((perp.x * perp.x) + (perp.y * perp.y)); nperp.x = static_cast<float>(perp.x * 1.0 / perplen); nperp.y = static_cast<float>(perp.y * 1.0 / perplen); perpoffset.x = static_cast<float>(nperp.x * width * 0.5); perpoffset.y = static_cast<float>(nperp.y * width * 0.5); diroffset.x = static_cast<float>(ndir.x * 0 * 0.5); diroffset.y = static_cast<float>(ndir.y * 0 * 0.5); // p0 = start + perpoffset - diroffset // p1 = start - perpoffset - diroffset // p2 = end + perpoffset + diroffset // p3 = end - perpoffset + diroffset p0.x = start.x + perpoffset.x - diroffset.x; p0.y = start.y + perpoffset.y - diroffset.y; p1.x = start.x - perpoffset.x - diroffset.x; p1.y = start.y - perpoffset.y - diroffset.y; if(i > 0) { temp = (8 * (i - 1)); p2.x = output[temp + 2]; p2.y = output[temp + 3]; p3.x = output[temp + 4]; p3.y = output[temp + 5]; } else { p2.x = end.x + perpoffset.x + diroffset.x; p2.y = end.y + perpoffset.y + diroffset.y; p3.x = end.x - perpoffset.x + diroffset.x; p3.y = end.y - perpoffset.y + diroffset.y; } output.push_back(p2.x); output.push_back(p2.y); output.push_back(p0.x); output.push_back(p0.y); output.push_back(p1.x); output.push_back(p1.y); output.push_back(p3.x); output.push_back(p3.y); } }
编辑:
POINTFLOAT multiply(const POINTFLOAT &a, float b) { POINTFLOAT result; result.x = a.x * b; result.y = a.y * b; return result; } POINTFLOAT normalize(const POINTFLOAT &a) { return multiply(a, 1.0f / sqrt(a.x * a.x + a.y * a.y)); } POINTFLOAT slerp2d( const POINTFLOAT v0, const POINTFLOAT v1, float t ) { float dot = (v0.x * v1.x + v1.y * v1.y); if( dot < -1.0f ) dot = -1.0f; if( dot > 1.0f ) dot = 1.0f; float theta_0 = acos( dot ); float theta = theta_0 * t; POINTFLOAT v2; v2.x = -v0.y; v2.y = v0.x; POINTFLOAT result; result.x = v0.x * cos(theta) + v2.x * sin(theta); result.y = v0.y * cos(theta) + v2.y * sin(theta); return result; } void OGLENGINEFUNCTIONS::GenerateLinePoly(const std::vector<std::vector<GLdouble> > &input, std::vector<GLfloat> &output, int width) { output.clear(); if(input.size() < 2) { return; } float w = width / 2.0f; //glBegin(GL_TRIANGLES); for( size_t i = 0; i < input.size()-1; ++i ) { POINTFLOAT cur; cur.x = input[i][0]; cur.y = input[i][1]; POINTFLOAT nxt; nxt.x = input[i+1][0]; nxt.y = input[i+1][1]; POINTFLOAT b; b.x = nxt.x - cur.x; b.y = nxt.y - cur.y; b = normalize(b); POINTFLOAT b_perp; b_perp.x = -b.y; b_perp.y = b.x; POINTFLOAT p0; POINTFLOAT p1; POINTFLOAT p2; POINTFLOAT p3; p0.x = cur.x + b_perp.x * w; p0.y = cur.y + b_perp.y * w; p1.x = cur.x - b_perp.x * w; p1.y = cur.y - b_perp.y * w; p2.x = nxt.x + b_perp.x * w; p2.y = nxt.y + b_perp.y * w; p3.x = nxt.x - b_perp.x * w; p3.y = nxt.y - b_perp.y * w; output.push_back(p0.x); output.push_back(p0.y); output.push_back(p1.x); output.push_back(p1.y); output.push_back(p2.x); output.push_back(p2.y); output.push_back(p2.x); output.push_back(p2.y); output.push_back(p1.x); output.push_back(p1.y); output.push_back(p3.x); output.push_back(p3.y); // only do joins when we have a prv if( i == 0 ) continue; POINTFLOAT prv; prv.x = input[i-1][0]; prv.y = input[i-1][1]; POINTFLOAT a; a.x = prv.x - cur.x; a.y = prv.y - cur.y; a = normalize(a); POINTFLOAT a_perp; a_perp.x = a.y; a_perp.y = -a.x; float det = a.x * b.y - b.x * a.y; if( det > 0 ) { a_perp.x = -a_perp.x; a_perp.y = -a_perp.y; b_perp.x = -b_perp.x; b_perp.y = -b_perp.y; } // TODO: do inner miter calculation // flip around normals and calculate round join points a_perp.x = -a_perp.x; a_perp.y = -a_perp.y; b_perp.x = -b_perp.x; b_perp.y = -b_perp.y; size_t num_pts = 4; std::vector< POINTFLOAT> round( 1 + num_pts + 1 ); POINTFLOAT nc; nc.x = cur.x + (a_perp.x * w); nc.y = cur.y + (a_perp.y * w); round.front() = nc; nc.x = cur.x + (b_perp.x * w); nc.y = cur.y + (b_perp.y * w); round.back() = nc; for( size_t j = 1; j < num_pts+1; ++j ) { float t = (float)j / (float)(num_pts + 1); if( det > 0 ) { POINTFLOAT nin; nin = slerp2d( b_perp, a_perp, 1.0f-t ); nin.x *= w; nin.y *= w; nin.x += cur.x; nin.y += cur.y; round[j] = nin; } else { POINTFLOAT nin; nin = slerp2d( a_perp, b_perp, t ); nin.x *= w; nin.y *= w; nin.x += cur.x; nin.y += cur.y; round[j] = nin; } } for( size_t j = 0; j < round.size()-1; ++j ) { output.push_back(cur.x); output.push_back(cur.y); if( det > 0 ) { output.push_back(round[j + 1].x); output.push_back(round[j + 1].y); output.push_back(round[j].x); output.push_back(round[j].y); } else { output.push_back(round[j].x); output.push_back(round[j].y); output.push_back(round[j + 1].x); output.push_back(round[j + 1].y); } } } }
需要书面形式的Eigen,但核心操作应轻松映射到您正在使用的任何矢量类。
// v0 and v1 are normalized // t can vary between 0 and 1 // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ Vector2f slerp2d( const Vector2f& v0, const Vector2f& v1, float t ) { float dot = v0.dot(v1); if( dot < -1.0f ) dot = -1.0f; if( dot > 1.0f ) dot = 1.0f; float theta_0 = acos( dot ); float theta = theta_0 * t; Vector2f v2( -v0.y(), v0.x() ); return ( v0*cos(theta) + v2*sin(theta) ); } void glPolyline( const vector<Vector2f>& polyline, float width ) { if( polyline.size() < 2 ) return; float w = width / 2.0f; glBegin(GL_TRIANGLES); for( size_t i = 0; i < polyline.size()-1; ++i ) { const Vector2f& cur = polyline[ i ]; const Vector2f& nxt = polyline[i+1]; Vector2f b = (nxt - cur).normalized(); Vector2f b_perp( -b.y(), b.x() ); Vector2f p0( cur + b_perp*w ); Vector2f p1( cur - b_perp*w ); Vector2f p2( nxt + b_perp*w ); Vector2f p3( nxt - b_perp*w ); // first triangle glVertex2fv( p0.data() ); glVertex2fv( p1.data() ); glVertex2fv( p2.data() ); // second triangle glVertex2fv( p2.data() ); glVertex2fv( p1.data() ); glVertex2fv( p3.data() ); // only do joins when we have a prv if( i == 0 ) continue; const Vector2f& prv = polyline[i-1]; Vector2f a = (prv - cur).normalized(); Vector2f a_perp( a.y(), -a.x() ); float det = a.x()*b.y() - b.x()*a.y(); if( det > 0 ) { a_perp = -a_perp; b_perp = -b_perp; } // TODO: do inner miter calculation // flip around normals and calculate round join points a_perp = -a_perp; b_perp = -b_perp; size_t num_pts = 4; vector< Vector2f > round( 1 + num_pts + 1 ); for( size_t j = 0; j <= num_pts+1; ++j ) { float t = (float)j/(float)(num_pts+1); if( det > 0 ) round[j] = cur + (slerp2d( b_perp, a_perp, 1.0f-t ) * w); else round[j] = cur + (slerp2d( a_perp, b_perp, t ) * w); } for( size_t j = 0; j < round.size()-1; ++j ) { glVertex2fv( cur.data() ); if( det > 0 ) { glVertex2fv( round[j+1].data() ); glVertex2fv( round[j+0].data() ); } else { glVertex2fv( round[j+0].data() ); glVertex2fv( round[j+1].data() ); } } } glEnd(); }
编辑: 屏幕截图: