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.
Related
This code draws a sine wave with function. In the following panning/zooming code, I am trying to understand how fWorldPerScreenWidthPixel is being used to draw the line segments.
WorldToScreen(fWorldLeft - fWorldPerScreenWidthPixel, -function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f, opx, opy);
It is setting opx and opy, but why is it subtracted from: fWorldLeft
It seems strange to want to start left of fWorldLeft in the for loop where it draws the line. fWorldLeft starts at -25.
I have included the necessary code to explain:
// Draw Chart
float fWorldPerScreenWidthPixel = (fWorldRight - fWorldLeft) / ScreenWidth();
float fWorldPerScreenHeightPixel = (fWorldBottom - fWorldTop) / ScreenHeight();
int px, py, opx = 0, opy = 0;
WorldToScreen(fWorldLeft - fWorldPerScreenWidthPixel, -function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f, opx, opy);
for (float x = fWorldLeft; x < fWorldRight; x += fWorldPerScreenWidthPixel)
{
float y = -function(x - 5.0f) + 5.0f;
WorldToScreen(x, y, px, py);
DrawLine(opx, opy, px, py, PIXEL_SOLID, FG_GREEN);
opx = px;
opy = py;
}
Call to set fWorldLeft:
// Clip
float fWorldLeft, fWorldTop, fWorldRight, fWorldBottom;
ScreenToWorld(0, 0, fWorldLeft, fWorldTop);
Sets fWorldleft:
// Convert coordinates from Screen Space --> World Space
void ScreenToWorld(int nScreenX, int nScreenY, float &fWorldX, float &fWorldY)
{
fWorldX = ((float)nScreenX / fScaleX) + fOffsetX;
fWorldY = ((float)nScreenY / fScaleY) + fOffsetY;
}
and while I'm at it, World to Screen:
// Convert coordinates from World Space --> Screen Space
void WorldToScreen(float fWorldX, float fWorldY, int &nScreenX, int &nScreenY)
{
nScreenX = (int)((fWorldX - fOffsetX) * fScaleX);
nScreenY = (int)((fWorldY - fOffsetY) * fScaleY);
}
Thank you!
Josh
Let's break it down
WorldToScreen(
fWorldLeft - fWorldPerScreenWidthPixel,
-function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f,
opx, opy);
A clearer way to write that would be
x = fWorldLeft - fWorldPerScreenWidthPixel;
WorldToScreen(
x,
-function((x) - 5.0f) + 5.0f,
opx, opy);
This transforms the position (x, f(x)) from world space to screen space and stores the result in (opx, opy). Let's see how these two variables are used:
for(...)
{
...
DrawLine(opx, opy, px, py, PIXEL_SOLID, FG_GREEN);
...
}
This draws a line from (opx, opy) to (px, py) (which is the current point on the function. (opx, opy) is the old point on the function. And this is exactly what you are doing with the initialization from above. You set (opx, opy) to a point that is one pixel outside of the screen to ensure that there are no gaps at the border.
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
I'm trying to make an application where balls bounce off the walls and also off each other. The bouncing off the walls works fine, but I'm having some trouble getting them to bounce off each other. Here's the code I'm using to make them bounce off another ball (for testing I only have 2 balls)
// Calculate the distance using Pyth. Thrm.
GLfloat x1, y1, x2, y2, xd, yd, distance;
x1 = balls[0].xPos;
y1 = balls[0].yPos;
x2 = balls[1].xPos;
y2 = balls[1].yPos;
xd = x2 - x1;
yd = y2 - y1;
distance = sqrt((xd * xd) + (yd * yd));
if(distance < (balls[0].ballRadius + balls[1].ballRadius))
{
std::cout << "Collision\n";
balls[0].xSpeed = -balls[0].xSpeed;
balls[0].ySpeed = -balls[0].ySpeed;
balls[1].xSpeed = -balls[1].xSpeed;
balls[1].ySpeed = -balls[1].ySpeed;
}
What happens is that they randomly bounce, or pass through each other. Is there some physics that I'm missing?
EDIT: Here's the full function
// Callback handler for window re-paint event
void display()
{
glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
// FOR LOOP
for (int i = 0; i < numOfBalls; i++)
{
glLoadIdentity(); // Reset model-view matrix
int numSegments = 100;
GLfloat angle = 0;
glTranslatef(balls[i].xPos, balls[i].yPos, 0.0f); // Translate to (xPos, yPos)
// Use triangular segments to form a circle
glBegin(GL_TRIANGLE_FAN);
glColor4f(balls[i].colorR, balls[i].colorG, balls[i].colorB, balls[i].colorA);
glVertex2f(0.0f, 0.0f); // Center of circle
for (int j = 0; j <= numSegments; j++)
{
// Last vertex same as first vertex
angle = j * 2.0f * PI / numSegments; // 360 deg for all segments
glVertex2f(cos(angle) * balls[i].ballRadius, sin(angle) * balls[i].ballRadius);
}
glEnd();
// Animation Control - compute the location for the next refresh
balls[i].xPos += balls[i].xSpeed;
balls[i].yPos += balls[i].ySpeed;
// Calculate the distance using Pyth. Thrm.
GLfloat x1, y1, x2, y2, xd, yd, distance;
x1 = balls[0].xPos;
y1 = balls[0].yPos;
x2 = balls[1].xPos;
y2 = balls[1].yPos;
xd = x2 - x1;
yd = y2 - y1;
distance = sqrt((xd * xd) + (yd * yd));
if(distance < (balls[0].ballRadius + balls[1].ballRadius))
{
std::cout << "Collision\n";
balls[0].xSpeed = -balls[0].xSpeed;
balls[0].ySpeed = -balls[0].ySpeed;
balls[1].xSpeed = -balls[1].xSpeed;
balls[1].ySpeed = -balls[1].ySpeed;
}
else
{
std::cout << "No collision\n";
}
// Check if the ball exceeds the edges
if (balls[i].xPos > balls[i].xPosMax)
{
balls[i].xPos = balls[i].xPosMax;
balls[i].xSpeed = -balls[i].xSpeed;
}
else if (balls[i].xPos < balls[i].xPosMin)
{
balls[i].xPos = balls[i].xPosMin;
balls[i].xSpeed = -balls[i].xSpeed;
}
if (balls[i].yPos > balls[i].yPosMax) {
balls[i].yPos = balls[i].yPosMax;
balls[i].ySpeed = -balls[i].ySpeed;
}
else if (balls[i].yPos < balls[i].yPosMin)
{
balls[i].yPos = balls[i].yPosMin;
balls[i].ySpeed = -balls[i].ySpeed;
}
}
glutSwapBuffers(); // Swap front and back buffers (of double buffered mode)
}
**Note: Most of the function uses a for loop with numOfBalls as the counter, but to test collision, I'm only using 2 balls, hence the balls[0] and balls[1]
Here are some things to consider.
If the length of (xSpeed,ySpeed) and is roughly comparable with .ballRadius it is possible for two balls to travel "thru" each other between "ticks" of the simulation's clock (one step). Consider two balls which are traveling perfectly vertical, one up, one down, and 1 .ballRadius apart horizontally. In real life they would clearly collide but it would be easy for your simulation to miss this event if .ySpeed ~ .ballRadius.
Second, you change in the vector of the balls results in each ball coming to rest, since
balls[0].xSpeed -= balls[0].xSpeed;
is a really exotic way of writing
balls[0].xSpeed = 0;
For the physics almost correct stuff, you need to invert only the component perpendicular to the plane of contact.
In other words take collision_vector to be the vector between the center of the balls (just subtract one point's coordinates from the other's). Because you have spheres this also happens to be the normal of the collision plane.
Now for each ball in turn, you need to decompose their speeds. The A component will be the one aligned with the colision_vector you can obtain it by doing some vector arithmetic A = doc(Speed, collision_vector) * collision_vector. This will be the thing you want to invert. You also want to extract the B component that is parallel to the collision plane. Because it's parallel it won't change because of the collision. You obtain it by subtracting A from the speed vector.
Finally the new speed will be something like B - A. If you want to get the balls to spin you will need an angular momentum in the direction of A - B. If the balls have different mass then you will need use the weight ratio as a multiplier for A in the first formula.
This will make the collision look legit. The detection still needs to happen correctly. Make sure that the speeds are significantly smaller than the radius of the balls. For comparable or bigger speeds you will need more complex algorithms.
Note: most of the stuff above is vector arithmetics. Also It's late here so I might have mixed up some signs (sorry). Take a simple example on paper and work it out. It will also help you understand the solution better.
all, I am trying to multiply a matrix to a vector in OpenGL, but it turn out that the rendering result I got by calling my own multiplication function(OpenGLUtility::VectorMultiMatrix4d()) is different by calling the opengl function glMultMatrixf(). The two lines drawn on the screen is not same.
I put my code by calling two different ways as bellow.
The function OpenGLUtility::VectorMultiMatrix4d() is simple, just colume-major multiplication.
Can anyone give me some advices on this? Thanks a lot.
Code1:
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
double inverse[16];
OpenGLUtility::InverseMatrix(m_mat, inverse);
double tOrigin[4] = {
g_bottom_plane.m_Origin[0],
g_bottom_plane.m_Origin[1],
g_bottom_plane.m_Origin[2],
1.0 };
OpenGLUtility::VectorMultiMatrix4d(tOrigin,inversed);
double tNormal[4] = {
g_bottom_plane.m_Normal[0],
g_bottom_plane.m_Normal[1],
g_bottom_plane.m_Normal[2],
0.0 };
OpenGLUtility::VectorMultiMatrix4d(tNormal,inversed);
glBegin(GL_LINES);
glColor3f(0.0,1.0, 0.0);
glVertex4f(tOrigin[0], tOrigin[1], tOrigin[2], tOrigin[3]);
glVertex4f( tNormal[0]*tOrigin[3] + tOrigin[0],
tNormal[1]*tOrigin[3] + tOrigin[1],
tNormal[2]*tOrigin[3] + tOrigin[2],
tOrigin[3] );
glEnd();
//
void OpenGLUtility::VectorMultiMatrix4f(float* pVector, float* pMat)
{
pVector[0] = pMat[0]*pVector[0] + pMat[4]*pVector[1] + pMat[ 8]*pVector[2] + pMat[12]*pVector[3] ;
pVector[1] = pMat[1]*pVector[0] + pMat[5]*pVector[1] + pMat[ 9]*pVector[2] + pMat[13]*pVector[3] ;
pVector[2] = pMat[2]*pVector[0] + pMat[6]*pVector[1] + pMat[10]*pVector[2] + pMat[14]*pVector[3] ;
pVector[3] = pMat[3]*pVector[0] + pMat[7]*pVector[1] + pMat[11]*pVector[2] + pMat[15]*pVector[3] ;
}
Code 2
float inverse[16];
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMultMatrixf(m_mat);
OpenGLUtility::InverseMatrix(m_mat, inverse);
glMultMatrixf(inverse);
double tOrigin[4] = {
g_bottom_plane.m_Origin[0],
g_bottom_plane.m_Origin[1],
g_bottom_plane.m_Origin[2],
1.0 };
double tNormal[4] = {
g_bottom_plane.m_Normal[0],
g_bottom_plane.m_Normal[1],
g_bottom_plane.m_Normal[2],
0.0 };
glBegin(GL_LINES);
glColor3f(0.0,1.0, 0.0);
glVertex4f( tOrigin[0], tOrigin[1], tOrigin[2], tOrigin[3] );
glVertex4f( tNormal[0]*tOrigin[3] + tOrigin[0],
tNormal[1]*tOrigin[3] + tOrigin[1],
tNormal[2]*tOrigin[3] + tOrigin[2],
tOrigin[3] );
glEnd();
glMultMatrixf(m_mat);
The problem with your multiplication is that you modify your pVector before you are done with the multiplication. You need to store it in a temporary or different variable!
Change your function to:
void OpenGLUtility::VectorMultiMatrix4f(float* pVector, float* pMat)
{
float x = pMat[0]*pVector[0] + pMat[4]*pVector[1] + pMat[ 8]*pVector[2] + pMat[12]*pVector[3] ;
float y = pMat[1]*pVector[0] + pMat[5]*pVector[1] + pMat[ 9]*pVector[2] + pMat[13]*pVector[3] ;
float z = pMat[2]*pVector[0] + pMat[6]*pVector[1] + pMat[10]*pVector[2] + pMat[14]*pVector[3] ;
float w = pMat[3]*pVector[0] + pMat[7]*pVector[1] + pMat[11]*pVector[2] + pMat[15]*pVector[3] ;
pVector[0] = x;
pVector[1] = y;
pVector[2] = z;
pVector[3] = w;
}
So. after reformatting your code, I'll comment on it: First lets have a look at code 2
float inverse[16];
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
Okay, operating on the modelview matrix, pushing the modelview matrix stack.
glMultMatrixf(m_mat);
Multiplying with a matrix m_mat
OpenGLUtility::InverseMatrix(m_mat, inverse);
Inverting it…
glMultMatrixf(inverse);
And there you multiply it back, so you've done
M * M^-1 = I
of course this only works if mat_m was invertible at all. But if it is invertible then those two operations cancel out nothing will happen.
BTW: You're missing a closing glPopMatrix();
Then this:
glBegin(GL_LINES);
glColor3f(0.0,1.0, 0.0);
glVertex4f( tOrigin[0], tOrigin[1], tOrigin[2], tOrigin[3] );
glVertex4f( tNormal[0]*tOrigin[3] + tOrigin[0],
tNormal[1]*tOrigin[3] + tOrigin[1],
tNormal[2]*tOrigin[3] + tOrigin[2],
tOrigin[3] );
glEnd();
glMultMatrixf(m_mat);
What are you expecting that last glMultMatrix to do?
Let me guess? You somehow expect OpenGL to multiply that vector with the matrix (it does this) and returning it back to you (this is does not, at least not in this way).
Now here's some important thing for you to know:
OpenGL IS NOT A MATH LIBRARY
In fact all matrix manipulation functions have been removed from OpenGL-4, so that people no longer to things with OpenGL that it's not been purposed for. If the first code works for you: Fine! This is exactly how it's supposed to be done.
However it is not entirely clear what exactly you are trying to achieve; there might be a more elegant or straigtforward way of doing it.
While making a little Pong game in C++ OpenGL, I decided it'd be fun to create arcs (semi-circles) when stuff bounces. I decided to skip Bezier curves for the moment and just go with straight algebra, but I didn't get far. My algebra follows a simple quadratic function (y = +- sqrt(mx+c)).
This little excerpt is just an example I've yet to fully parameterize, I just wanted to see how it would look. When I draw this, however, it gives me a straight vertical line where the line's tangent line approaches -1.0 / 1.0.
Is this a limitation of the GL_LINE_STRIP style or is there an easier way to draw semi-circles / arcs? Or did I just completely miss something obvious?
void Ball::drawBounce()
{ float piecesToDraw = 100.0f;
float arcWidth = 10.0f;
float arcAngle = 4.0f;
glBegin(GL_LINE_STRIP);
for (float i = 0.0f; i < piecesToDraw; i += 1.0f) // Positive Half
{ float currentX = (i / piecesToDraw) * arcWidth;
glVertex2f(currentX, sqrtf((-currentX * arcAngle)+ arcWidth));
}
for (float j = piecesToDraw; j > 0.0f; j -= 1.0f) // Negative half (go backwards in X direction now)
{ float currentX = (j / piecesToDraw) * arcWidth;
glVertex2f(currentX, -sqrtf((-currentX * arcAngle) + arcWidth));
}
glEnd();
}
Thanks in advance.
What is the purpose of sqrtf((-currentX * arcAngle)+ arcWidth)? When i>25, that expression becomes imaginary. The proper way of doing this would be using sin()/cos() to generate the X and Y coordinates for a semi-circle as stated in your question. If you want to use a parabola instead, the cleaner way would be to calculate y=H-H(x/W)^2