I'm trying to draw a cubic bezier path with a certain thickness, but the curve appears like a sequence of disconnected segments (3 in my case). This is a screenshot (the blue circles are the control points of the curve).
I noticed that the same effect occurs in the 'draw primitives' in the cocos2d-x tests. Anyway I'm pretty sure there should be a workaround but I'm not able to find it by myself.
Furthermore the line is affected by the aliasing effect and I am not sure how to apply an alpha shadow to avoid it.
This is the code I used:
glLineWidth(24.0f);
Vec2 cp1 = Vec2(200, 200);
Vec2 cp2 = Vec2(1300, 150);
Vec2 cp3 = Vec2(170, 1200);
Vec2 cp4 = Vec2(1400, 1000);
//Draw control points
DrawPrimitives::setDrawColor4B(0, 0, 255, 255);
DrawPrimitives::drawSolidCircle(cp1, 50, 360, 120, 1, 1);
DrawPrimitives::drawSolidCircle(cp2, 50, 360, 120, 1, 1);
DrawPrimitives::drawSolidCircle(cp3, 50, 360, 120, 1, 1);
DrawPrimitives::drawSolidCircle(cp4, 50, 360, 120, 1, 1);
//Draw cubic red bezier curve
DrawPrimitives::setDrawColor4B(255, 0, 0, 255);
DrawPrimitives::drawCubicBezier(cp1, cp2, cp3, cp4, 50);
That broken effect is caused by a lack of path joining between endpoints of the line strips.
OpenGL is designed for fast scanline rasterization first and foremost and isn't always so beautiful if you want to use it this way.
There could be quick and dirty workaround ways to kind of get a reasonable result, like drawing circles at the endpoints to try to fill things in.
A proper library where drawing paths beautifully is important will often offer join options between lines/curves like rounded, beveled, or a miter joint along with options for the end caps where segments aren't joining together. It might be easier and efficient enough for the kind of work you're doing to use, say, a decent SVG rasterizer for this kind of work. If you need to composite the results over elements rasterized by OGL, you could transfer the image results into a texture and render that.
You could also get pretty elaborate and roll a pretty sophisticated solution (or possibly find one elsewhere) through OpenGL. Here's an example: https://www.mapbox.com/blog/drawing-antialiased-lines/
i have another solution, but i do not know whether it make the performance slow down?
anyone please give me advise!!
void DrawNode::drawCubicBezier(const Vec2 &origin, const Vec2 &control1, const Vec2 &control2, const Vec2 &destination, unsigned int segments, const Color4F &color)
{
Vec2* vertices = new (std::nothrow) Vec2[segments + 1];
if( ! vertices )
return;
float t = 0;
for (unsigned int i = 0; i < segments; i++)
{
vertices[i].x = powf(1 - t, 3) * origin.x + 3.0f * powf(1 - t, 2) * t * control1.x + 3.0f * (1 - t) * t * t * control2.x + t * t * t * destination.x;
vertices[i].y = powf(1 - t, 3) * origin.y + 3.0f * powf(1 - t, 2) * t * control1.y + 3.0f * (1 - t) * t * t * control2.y + t * t * t * destination.y;
t += 1.0f / segments;
**///begin adddd
drawLine(Vec2(vertices[i].x, vertices[i].y - 3), Vec2(vertices[i].x, vertices[i].y + 3), color);
/// end addddd**
}
vertices[segments].x = destination.x;
vertices[segments].y = destination.y;
**drawLine(Vec2(vertices[segments].x, vertices[segments].y - 3), Vec2(vertices[segments].x, vertices[segments].y + 3), color);**
CC_SAFE_DELETE_ARRAY(vertices);
}
here is my result
Related
I've looked around here for some answers on this, I've found a few good ones, but when I implement them in my code, I get some unexpected results.
Here's my problem:
I'm creating a top down geometry shooter, and when an enemy is hit by a bullet, the enemy should explode into smaller clones, shooting out from the center of the enemy in a circular fashion, in even intervals around the enemy. I assumed I could accomplish this by getting an initial vector, coming straight out of the side of the enemy shape, then rotate that vector the appropriate amount of times. Here's my code:
void Game::spawnSmallEnemies(s_ptr<Entity> e)
{
int vertices = e->cShape->shape.getPointCount();
float angle = 360.f / vertices;
double conv = M_PI / 180.f;
double cs = cos(angle * (M_PI / 180));
double sn = sin(angle * (M_PI / 180));
// Radius of enemy shape
Vec2 velocity { e->cTransform->m_pos.m_x + m_enemyCfg.SR , e->cTransform->m_pos.m_y} ;
velocity = velocity.get_normal();
Vec2 origin {e->cTransform->m_pos};
for (int i = 0; i < vertices; i++)
{
auto small = m_entityMgr.addEntity("small");
small->cTransform = std::make_shared<CTransform>(origin, velocity * 3, 0);
small->cShape = std::make_shared<CShape>(m_enemyCfg.SR / 4, vertices,
e->cShape->shape.getFillColor(), e->cShape->shape.getOutlineColor(),
e->cShape->shape.getOutlineThickness(), small->cTransform->m_pos);
small->cCircleCollider = std::make_shared<CCircleCollider>(m_enemyCfg.SR / 4);
small->cLife = std::make_shared<CLifespan>(m_enemyCfg.L);
velocity.m_x = ((velocity.m_x - origin.m_x) * cs) - ((origin.m_y - velocity.m_y) * sn) + origin.m_x;
velocity.m_y = origin.m_y - ((origin.m_y - velocity.m_y) * cs) + ((velocity.m_x - origin.m_x) * sn);
}
}
I got the rotation code at the bottom from this post, however each of the smaller shapes only shoot toward the bottom right, all clumped together. I would assume my error is logic, however I have been unable to find a solution.
I have a 3d, stereoscopic rendering application that currently uses parallel stereoscopy by just moving (shifting) the camera to the side for each Left and Right views. It does work, but recently I felt it could be much improved if I had the off-axis option. I got a semi-working algorithm for glm::frustum() to allow for this but am having some troubles immediately when I switch to it over glm::perspective().
I followed the only GL guide I could find, Simple, Low-Cost Stereographics, that which said to replace my existing glm::perspective() with (2 calls
//OFF-AXIS STEREO
if (myAbj.stereoOffsetAxis) {
glm::vec3 targ0_stored = i->targO;
if (myAbj.stereoLR == 0)
{
float sgn = -1.f * (float)myAbj.stereoSwitchLR;
float eyeSep = myAbj.stereoSep;
float focalLength = 50.f;
float eyeOff = (sgn * (eyeSep / 2.f) * (myAbj.selCamLi->nearClip->val_f / focalLength));
float top = myAbj.selCamLi->nearClip->val_f * tan(myAbj.selCamLi->fov->val_f / 2.f);
float right = myAbj.aspect * top;
myAbj.selCamLi->PM = glm::frustum(-right - eyeOff, right - eyeOff, -top, top, myAbj.selCamLi->nearClip->val_f, myAbj.selCamLi->farClip->val_f);
i->targO += myAbj.selCamLi->rightO * myAbj.stereoSep * (float)myAbj.stereoSwitchLR;
VMup(i);
i->targO = targ0_stored;
}
if (myAbj.stereoLR == 1)
{
float sgn = 1.f * (float)myAbj.stereoSwitchLR;
float eyeSep = myAbj.stereoSep;
float focalLength = 50.f;
float eyeOff = (sgn * (eyeSep / 2.f) * (myAbj.selCamLi->nearClip->val_f / focalLength));
float top = myAbj.selCamLi->nearClip->val_f * tan(myAbj.selCamLi->fov->val_f / 2.f);
float right = myAbj.aspect * top;
myAbj.selCamLi->PM = glm::frustum(-right - eyeOff, right - eyeOff, -top, top, myAbj.selCamLi->nearClip->val_f, myAbj.selCamLi->farClip->val_f);
i->targO += myAbj.selCamLi->rightO * -myAbj.stereoSep * (float)myAbj.stereoSwitchLR;
VMup(i);
i->targO = targ0_stored;
}
}
Using this equation, my View Matrix is rotated 180 degrees on the Z axis. However, the bigger issue is a large amount of black dots and flickering on my objects. When I move the camera to a close enough point the flickering stops. Even when I minimize the scene, the issue is still there.
Why is this flickering happening and what can I do to prevent it? It is ruining my scenes.
My near clip was causing the problem. It couldnt be set to the same low value that glm::perspective() was using - it needed a little bit more.
I have my own 3D engine implementation around OpenGL (C++) and it has worked fine for everything these last years.
But today I stumbled upon a problem. I have this scene with spheres (planets around a sun, orbit rings and things like that, very very simple) and I want to ray pick them with the mouse.
As long as the camera/view matrix is identity, picking works. When the camera is rotated and then moved, the picking goes completely haywire. I have been searching for a solution for a while now so now I'm asking you guys.
This is the code (summarized for this question):
mat4f mProj = createPerspective(PI / 4.0f, float(res.x) / float(res.y), 0.1f, 100.0f);
mat4f mCamera = createTranslation(-1.5f, 3, -34.0f) * createRotationZ(20.0f * PI / 180.0f) * createRotationX(20.0f * PI / 180.0f);
... render scene, using shaders that transform vertices with gl_Position = mProj * mCamera * aPosition;
mat4f mUnproject = (mProj * mCamera).getInverse();
vec2f mouseClip(2.0f * float(coord.x) / float(res.x) - 1.0f, 1.0f - 2.0f * float(coord.y) / float(res.y));
vec3f rayOrigin = (mUnproject * vec4f(mouseClip, 0, 1)).xyz();
vec3f rayDir = (mUnproject * vec4f(mouseClip, -1, 1)).xyz();
// In a loop over all planets:
mat4f mObject = createRotationY(planet.angle) * createTranslation(planet.distance, 0, 0);
vec3f planetPos = mObject.transformCoord(vec3f(0, 0, 0));
float R = planet.distance;
float a = rayDir.dot(rayDir);
float b = 2 * rayDir.x * (rayOrigin.x - planetPos.x) + 2 * rayDir.y * (rayOrigin.y - planetPos.y) + 2 * rayDir.z * (rayOrigin.z - planetPos.z);
float c = planetPos.dot(planetPos) + rayOrigin.dot(rayOrigin) -2 * planetPos.dot(rayOrigin) - R * R;
float d = b * b - 4 * a * c;
if (d >= 0)
HIT!
So when I use identity for mCamera, everything works fine, even when I use only rotation for mCamera, it works fine. It is when I start using the translation that it goes completely wrong.
Anyone knows where I am going wrong?
BDL's answer was spot on and put me back in the right direction. Indeed, when transforming coordinates myself, I forgot to do the perspective-divide. After writing so much shader code where the gpu does this for you, you forget about these things.
It is logical that this only gave issues when the camera moved and not when it was at (0, 0, 0) as then, the translation part of the transformation matrices stayed 0 and the w-factor of the coordinates were unaffected.
I immediately wrote transformCoord and transformNormal implementations in my matrix classes to prevent this error from happening again.
Also, the ray origin and direction were incorrect, although I don't really understand why yet. I now take the origin from my camera matrix (inverted of course) and calculate the direction the same way but now subtract the camera position from it to make it a direction vector. I normalize it, although I don't think it is really necessary in this case, but normalizing it will make its numbers look more readable when debugging anyway.
This works:
vec2f mouseClip(2.0f * float(coord.x) / float(res.x) - 1.0f, 1.0f - 2.0f * float(coord.y) / float(res.y));
mat4f mUnproject = (mProj * mCamera).getInverse();
mat4f mInvCamera = mCamera.getInverse();
vec3f rayOrigin(mInvCamera.m[12], mInvCamera.m[13], mInvCamera.m[14]);
vec3f rayDir = (mUnproject.transformCoord(vec3f(mouseClip, 1)) - rayOrigin).normalized();
... per planet
vec3f planetPos = mObject.transformCoord(vec3f(0, 0, 0));
float a = rayDir.dot(rayDir);
float b = 2 * rayDir.x * (rayOrigin.x - planetPos.x) + 2 * rayDir.y * (rayOrigin.y - planetPos.y) + 2 * rayDir.z * (rayOrigin.z - planetPos.z);
float c = planetPos.dot(planetPos) + rayOrigin.dot(rayOrigin) -2 * planetPos.dot(rayOrigin) - 0.4f * 0.4f;
float d = b * b - 4 * a * c;
if (d >= 0)
... HIT!
i wrote a code that draw filled circle, but it uses CPU a lot.
The thing is i draw pixel by pixel, first outter circle with radius n the second circle with radius n-1 and so on while n is not equal to 0.
I'm drawing 4 pixel in e cycle, for each circle part. Every part, as i thought, has ~ Pi/(2*R) pixels, but it is not enough and circle fill wrong, so i used Pi/(4*R) and now circle fills normaly.
Deg0 = 0;
Deg90 = M_PI / 2;
DegStep = Deg90 / (R * 4);
CurrDeg = Deg0;
OffsetX = R;
OffsetY = 0;
TmpR = R;
while(TmpR>0 )
{
while(CurrDeg < Deg90)
{
OffsetX = cos(CurrDeg) * TmpR;
OffsetY = sin(CurrDeg) * TmpR;
SDL_RenderDrawPoint(Renderer, CX+(int)OffsetX, CY+(int)OffsetY);
SDL_RenderDrawPoint(Renderer, CX-(int)OffsetY, CY+(int)OffsetX);
SDL_RenderDrawPoint(Renderer, CX-(int)OffsetX, CY-(int)OffsetY);
SDL_RenderDrawPoint(Renderer, CX+(int)OffsetY, CY-(int)OffsetX);
CurrDeg+=DegStep;
}
CurrDeg = Deg0;
TmpR-=1;
}
So, is there any way to improve my realisation?
You could use the circle drawing capabilities of SDL, or you could optimize your own code by not actually using cos and sin. Use lookup tables instead.
Suppose I drew some simple lines in OpenGL like this:
glBegin(GL_LINES);
glVertex2f(1, 5);
glVertex2f(0, 1);
glEnd();
How do I make the line look jittery, like it was being sketched or drawn with hand?
You could try breaking your line up into segments then adding some random noise with rand().
Here's some ugly but hopefully somewhat useful code. You can refactor this as needed:
const float X1= 1.0f, Y1 = 5.0f, X2 = 0.0f, Y2 = 1.0f;
const int NUM_PTS = 10; //however many points in between
//you will need to call srand() to seed your random numbers
glBegin(GL_LINES);
glVertex2f(START_X, START_Y);
for(unsigned i = 0; i < NUM_PTS; i += 2)
{
float t = (float)i/NUM_PTS;
float rx = (rand() % 200 - 100)/100.0f; //random perturbation in x
float ry = (rand() % 200 - 100)/100.0f; //random perturbation in y
glVertex2f( t * (END_X - START_X) + r, t * (END_Y - START_Y) + r);
glVertex2f((t + 1) * (END_X - START_X), (t + 1) * (END_Y - START_Y));
}
glVertex2f(END_X, END_Y);
glEnd();
I increment the loop by 2 and draw every other point without random perturbation so that the line segments are all joined together.
You might want to be aware of the fact that the glBegin/glEnd style is called "immediate mode" and is not very efficient. It's not even supported on some mobile platforms. If you find your stuff is sluggish look at using vertex arrays.
To make the line look hand-drawn and nicer you might also want to make it fatter and use anti-aliasing.