Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 9 years ago.
Improve this question
I'm trying to calculate multiple points on an angle (circle segment) so that I can store it as a VBO of Vector3 and render it in OpenGL.
Imagine each of those points on the dotted line as a coordinate I want to calculate
I know I can find the magnitude of the angle using the dot product, and in 2 dimensions I would be able to calculate the points on the angle just using sin and cos of this angle. But how do I apply this in 3 dimensions?
I thought maybe I should split the angle down into components, but then wasn't sure how to calculate magnitudes in that situation.
So what is the best method for calculating those points, and how do I do it?
I'm working in C# but of course pseudo code or just methods would do.
normalize and scale both vectors and then slerp between them
slerp stands for spherical linear interpolation and is referenced mostly for quaternions but is valid here as well
vec3 slerp(vec3 a, vec3 b, float t){
float dotp = dot(a,b);
if (dotp > DOT_THRESHOLD) {
// If the inputs are too close for comfort, linearly interpolate
// and normalize the result to avoid division by near 0
vec3 result = v0 + t*(v1 – v0);
result.normalize();
return result;
}
float theta = acos(dotp);
return (sin(theta*(1-t))*a + sin(theta*t)*b)/sin(theta);
}
The trick is to compute the dots as if your two vectors are the unit vectors. Just a half circle. But then instead of writing it as (x,y) = (0,0,1)*x + (0,1,0)*y... just put in your two red and green vectors as a new base.
Compute your 2d x,y circle points. Then the 3d point is redvector*x + greenvector*y.
Here is a C++ DLL for your C# project(tested but angles are not uniform but there is no division by zero as long as vectors have a magnitude):
Usage:
[DllImport("angle.dll")]
extern static void anglePoints(
float x1,
float y1,
float z1,
float x2,
float y2,
float z2,
float [] points
);
İnside of DLL:
class float3
{
public:
float x,y,z;
float3(float X, float Y, float Z)
{
x=X; y=Y; z=Z;
}
float3 sub(float X, float Y, float Z)
{
float3 tmp(0,0,0);
tmp.x=x-X;
tmp.y=y-Y;
tmp.z=z-Z;
return tmp;
}
float3 sub(float3 b)
{
float3 tmp(0,0,0);
tmp.x=x-b.x;
tmp.y=y-b.y;
tmp.z=z-b.z;
return tmp;
}
float3 add(float3 b)
{
float3 tmp(0,0,0);
tmp.x=x+b.x;
tmp.y=y+b.y;
tmp.z=z+b.z;
return tmp;
}
void normalize()
{
float r=sqrt(x*x+y*y+z*z);
x/=r;
y/=r;
z/=r;
}
void scale(float s)
{
x*=s;y*=s;z*=s;
}
void set(float3 v)
{
x=v.x;y=v.y;z=v.z;
}
};
extern "C" __declspec(dllexport) void anglePoints(
float x1,
float y1,
float z1,
float x2,
float y2,
float z2,
float * points
)
{
float3 A(x1,y1,z1);
float3 B(x2,y2,z2);
float3 tmp(0,0,0);
float3 diff(0,0,0);
for(int i=0;i<10;i++)
{
tmp.set(A);
diff.set(B.sub(A));
diff.scale(0.1*((float)i)); // simple and not efficient :P
diff.set(diff.add(tmp));
diff.normalize(); // normalized values so you can
points[i*3+0]=diff.x; // simply use them
points[i*3+1]=diff.y;
points[i*3+2]=diff.z;
}
}
Example:
float[] tmp = new float[30];
anglePoints(0,1,1,10,10,10,tmp);
for (int i = 0; i < 30; i++)
{
Console.WriteLine(tmp[i]);
}
Output:
0 // starts as 0,1,1 normalized
0,7071068
0,7071068
0,34879
0,6627011
0,6627011
0,4508348
0,6311687
0,6311687
0,4973818
0,6134375
0,6134375
0,5237828
0,6023502
0,6023502
0,540738
0,5948119
0,5948119
0,5525321
0,5893675
0,5893675
0,5612046
0,5852562
0,5852562
0,5678473
0,5820435
0,5820435
0,5730973 //ends as 10,10,10 but normalized
0,579465
0,579465
Related
I want to find XY of the center (red) of a convex-hull points (orange circles) set that is a result from collision detection.
Using separating-axis technique, I know for sure that the convex shape (pink) is relatively thin in Z-axis.
In >90% of my use cases, the amount of vertices is not more than 8.
My poor algorithm (AABB) ... MCVE
I tried to implement it by calculating the center point of AABB.
However, when I use it in real Physics simulation, the collision point (red) is not accurate enough for box-stack stability.
Here is the test case (the vertices are extruded in +y and -y to create volume) :-
int main(){
std::vector<Vec3> hullPoints;
hullPoints.push_back(Vec3(-0.5,-0.5,-0.1));
hullPoints.push_back(Vec3(-0.5,-0.5,0.1));
hullPoints.push_back(Vec3(-0.5,0.5,-0.1));
hullPoints.push_back(Vec3(-0.5,0.5,0.1));
hullPoints.push_back(Vec3(0.5,-0.5,-0.2));
hullPoints.push_back(Vec3(0.5,-0.5,0.2));
hullPoints.push_back(Vec3(0.5,0.5,-0.2));
hullPoints.push_back(Vec3(0.5,0.5,0.2));
//^^^^ INPUT
Vec3 centerOfHull;// approximate
Vec3 centerMax=Vec3(-100000,-100000,-100000);
Vec3 centerMin=Vec3(100000,100000,100000);
for(unsigned int n=0;n<hullPoints.size();n++){
Vec3 hullPoint=hullPoints[n];
for(unsigned int m3=0;m3<3;m3++){
centerMax[m3]=std::max( centerMax[m3],hullPoint[m3]);
centerMin[m3]=std::min( centerMin[m3],hullPoint[m3]);
}
}
centerOfHull=centerMax*0.5 + centerMin*0.5;
std::cout<<"centerOfHull="<< centerOfHull.toString()<<std::endl;
//it prints (0,0,0)
}
I wish it to return something like Vec3(a value between 0.05 and 0.45, 0, don't care).
References
I want a very fast algorithm that doesn't have to be very accurate.
There are some algorithm in the internet e.g.
Skeleton (so unrelated) : Better "centerpoint" than centroid
Just average all hull points. Its accuracy is too bad. (e.g. result of my example = Vec3(0,0,0))
It is even worse for unevenly-distributed vertices e.g.
Generate the whole convex hull (and all faces). It is too slow for unnecessary high precision.
Answers doesn't need to contain any C++ code.
Just a rough suggestion can be very useful.
Appendix (Vec3 library)
It is provided only for MCVE completeness.
#include <vector>
#include <iostream>
#include <string>
struct Vec3{
//modify from https://www.flipcode.com/archives/Faster_Vector_Math_Using_Templates.shtml
float x, y, z;
inline Vec3( void ) {}
inline Vec3( const float x, const float y, const float z )
{ this->x = x; this->y = y; this->z = z; }
inline Vec3 operator + ( const Vec3& A ) const {
return Vec3( x + A.x, y + A.y, z + A.z );
}
inline Vec3 operator *( const float& A ) const {
return Vec3( x*A, y*A,z*A);
}
inline float Dot( const Vec3& A ) const {
return A.x*x + A.y*y + A.z*z;
}
inline float& operator[]( int arr) {
switch(arr){
case 0: return x;
case 1: return y;
case 2: return z;
}
std::cout<<"error"<<std::endl;
return x;
}
std::string toString( ) const {
return "("+std::to_string(x)+","+std::to_string(y)+","+std::to_string(z)+")";
}
};
Problem
I am writing a ray tracer as a use case for a specific machine learning approach in Computer Graphics.
My problem is that, when I try to find the intersection between a ray and a surface, the result is not exact.
Basically, if I am scattering a ray from point O towards a surface located at (x,y,z), where z = 81, I would expect the solution to be something like S = (x,y,81). The problem is: I get a solution like (x,y,81.000000005).
This is of course a problem, because following operations depend on that solution, and it needs to be the exact one.
Question
My question is: how do people in Computer Graphics deal with this problem? I tried to change my variables from float to double and it does not solve the problem.
Alternative solutions
I tried to use the function std::round(). This can only help in specific situations, but not when the exact solution contains one or more significant digits.
Same for std::ceil() and std::floor().
EDIT
This is how I calculate the intersection with a surface (rectangle) parallel to the xz axes.
First of all, I calculate the distance t between the origin of my Ray and the surface. In case my Ray, in that specific direction, does not hit the surface, t is returned as 0.
class Rectangle_xy: public Hitable {
public:
float x1, x2, y1, y2, z;
...
float intersect(const Ray &r) const { // returns distance, 0 if no hit
float t = (y - r.o.y) / r.d.y; // ray.y = t* dir.y
const float& x = r.o.x + r.d.x * t;
const float& z = r.o.z + r.d.z * t;
if (x < x1 || x > x2 || z < z1 || z > z2 || t < 0) {
t = 0;
return 0;
} else {
return t;
}
....
}
Specifically, given a Ray and the id of an object in the list (that I want to hit):
inline Vec hittingPoint(const Ray &r, int &id) {
float t; // distance to intersection
if (!intersect(r, t, id))
return Vec();
const Vec& x = r.o + r.d * t;// ray intersection point (t calculated in intersect())
return x ;
}
The function intersect() in the previous snippet of code checks for every Rectangle in the List rect if I intersect some object:
inline bool intersect(const Ray &r, float &t, int &id) {
const float& n = NUMBER_OBJ; //Divide allocation of byte of the whole scene, by allocation in byte of one single element
float d;
float inf = t = 1e20;
for (int i = 0; i < n; i++) {
if ((d = rect[i]->intersect(r)) && d < t) { // Distance of hit point
t = d;
id = i;
}
}
// Return the closest intersection, as a bool
return t < inf;
}
The coordinate is then obtained using the geometric interpolation between a line and a surface in the 3D space:
Vec& x = r.o + r.d * t;
where:
r.o: it represents the ray origin. It's defined as a r.o : Vec(float a, float b, float c)
r.d : this is the direction of the ray. As before: r.d: Vec(float d, float e, float f).
t: float representing the distance between the object and the origin.
You could look into using std::numeric_limits<T>::epsilon for your float/double comparison. And see if your result is in the region +-epsilon.
An alternative would be to not ray trace towards a point. Maybe just place relatively small box or sphere there.
I HAVE to draw a circle with the following code
(check if point is on the circle).
int rad=10;
// i=x,j=y
for (int j=ymid-rad;j<=ymid+rad;j++){
for (int i=xmid-rad;i<=xmid+rad;i++){
if (((i-xmid)*(i-xmid)+(j-ymid)*(j-ymid)) == rad*rad)
Image1->Canvas->Pixels[i][j]=clRed;
}
}
However it only draws a few points of the circle.
What am I doing wrong?
Thank you.
You're doing all of this in integer arithmetic; there are very few integer solutions to x^2 + y^2 == r^2 (for a fixed r).
I suggest using something like the midpoint circle algorithm instead.
Range based version would work like this:
bool RangeCheck(float val, float r1, float r2) {
return val >= r1 && val <= r2;
}
bool Circle(float x, float y, float rad) {
return RangeCheck(sqrtf(x*x+y*y), rad-0.8, rad+0.8);
}
bool CircleWithCenter(float x, float y, float cx, float cy, float rad) {
x-=cx; y-=cy;
return Circle(x,y,rad);
}
This kind of range is how they can draw isolines in weather forecasts, but works also for circles.
Im working on my OpenGL task, and next stage is loading models and producing shadows using shadow volumes algorithm. I do it in 3 stages -
setConnectivity - finding
neighbours of each triangle and
storing their indices in neigh
parameter of each triangle,
markVisible(float* lp) - if lp
represents vector of light's
position, it marks triangles as
visible = true or visible =
false depending on dot production
of its normal vector and light
position,
markSilhoutte(float *lp) - marking silhouette edges and building the volume itself, extending silhouette to infinity(100 units is enough) in the direction opposite to light.
I checked all stages, and can definitely say that its all ok with first two, so the problem is in third function, which i included in my question. I use the algorithm introduced in this tutorial: http://www.3dcodingtutorial.com/Shadows/Shadow-Volumes.html
Briefly, edge is included in silhouette if it belongs to the visible triangle and non-visible triangle at the same time.
Here is a pair of screenshots to show you whats wrong:
http://prntscr.com/17dmg , http://prntscr.com/17dmq
As you can see, green sphere represents light's position, and these ugly green-blue polygons are faces of "shadow volume". You can also see, that im applying this function to the model of cube, and one of volume's side is missing(its not closed, but i should be). Can someone suggest whats wrong with my code and how can i fix it? Here goes the code i promised to include(variables names are self-explanatory, i suppose, but if you dont think so i can add description for each of them):
void Model::markSilhouette(float* lp){
glBegin(GL_QUADS);
for ( int i = 0; i < m_numMeshes; i++ )
{
for ( int t = 0; t < m_pMeshes[i].m_numTriangles; t++ )
{
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[t];
Triangle* pTri = &m_pTriangles[triangleIndex];
if (pTri->visible){
for(int j=0;j<3;j++){
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[pTri->neigh[j]-1];
Triangle* pTrk = &m_pTriangles[triangleIndex];
if(!pTrk->visible){
int p1j=pTri->m_vertexIndices[j];
int p2j=pTri->m_vertexIndices[(j+1)%3];
float* v1=m_pVertices[p1j].m_location;
float* v2=m_pVertices[p2j].m_location;
float x1=m_pVertices[p1j].m_location[0];
float y1=m_pVertices[p1j].m_location[1];
float z1=m_pVertices[p1j].m_location[2];
float x2=m_pVertices[p2j].m_location[0];
float y2=m_pVertices[p2j].m_location[1];
float z2=m_pVertices[p2j].m_location[2];
t=100;
float xl1=(x1-lp[0])*t;
float yl1=(y1-lp[1])*t;
float zl1=(z1-lp[2])*t;
float xl2=(x2-lp[0])*t;
float yl2=(y2-lp[1])*t;
float zl2=(z2-lp[2])*t;
glColor3f(0,0,1);
glVertex3f(x1 + xl1,
y1 + yl1,
z1 + zl1);
glVertex3f(x1,
y1,
z1);
glColor3f(0,1,0);
glVertex3f(x2 + xl2,
y2 + yl2,
z2 + zl2);
glVertex3f(x2,
y2,
z2);
}
}
}
}
}
glEnd();
}
I've found it. It looks like if you dont see an obvious algorithm mistake for a few days, then you've made a f*cking stupid mistake.
My triangle index variable is called t. Guess what? My extending vector length is also called t, and they are in the same scope, and i set t=100 after FIRST visible triangle :D So now volumes look like this:
outside http://prntscr.com/17l3n
inside http://prntscr.com/17l40
And it looks good for all light positions(acceptable by shadow volumes aglorithm, of course). So the working code for drawing a shadow volume is the following:
void Model::markSilouette(float* lp){
glDisable(GL_LIGHTING);
glPointSize(4.0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK,GL_FILL);
glBegin(GL_QUADS);
for ( int i = 0; i < m_numMeshes; i++ )
{
for ( int t = 0; t < m_pMeshes[i].m_numTriangles; t++ )
{
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[t];
Triangle* pTri = &m_pTriangles[triangleIndex];
if (pTri->visible){
for(int j=0;j<3;j++){
Triangle* pTrk;
if(pTri->neigh[j]){
int triangleIndex = m_pMeshes[i].m_pTriangleIndices[pTri->neigh[j]-1];
pTrk = &m_pTriangles[triangleIndex];
}
if((!pTri->neigh[j]) || !pTrk->visible){
int p1j=pTri->m_vertexIndices[j];
int p2j=pTri->m_vertexIndices[(j+1)%3];
float* v1=m_pVertices[p1j].m_location;
float* v2=m_pVertices[p2j].m_location;
float x1=m_pVertices[p1j].m_location[0];
float y1=m_pVertices[p1j].m_location[1];
float z1=m_pVertices[p1j].m_location[2];
float x2=m_pVertices[p2j].m_location[0];
float y2=m_pVertices[p2j].m_location[1];
float z2=m_pVertices[p2j].m_location[2];
float f=100; // THE PROBLEM WAS HERE
float xl1=(x1-lp[0])*f;
float yl1=(y1-lp[1])*f;
float zl1=(z1-lp[2])*f;
float xl2=(x2-lp[0])*f;
float yl2=(y2-lp[1])*f;
float zl2=(z2-lp[2])*f;
glColor3f(0,0,0);
glVertex3f(x1 + xl1,
y1 + yl1,
z1 + zl1);
glVertex3f(x1,
y1,
z1);
glVertex3f(x2,
y2,
z2);
glVertex3f(x2 + xl2,
y2 + yl2,
z2 + zl2);
}
}
}
}
}
glEnd();
}
I think everything is ok, you are just rendering volume without depth test =)
I'm no mathematician, but I need to draw a filled in circle.
My approach was to use someone else's math to get all the points on the circumference of a circle, and turn them into a triangle fan.
I need the vertices in a vertex array, no immediate mode.
The circle does appear. However, when I try and overlay circles strange things happen. They appear only a second and then disappear. When I move my mouse out of the window a triangle sticks out from nowhere.
Here's the class:
class circle
{
//every coordinate with have an X and Y
private:
GLfloat *_vertices;
static const float DEG2RAD = 3.14159/180;
GLfloat _scalex, _scaley, _scalez;
int _cachearraysize;
public:
circle(float scalex, float scaley, float scalez, float radius, int numdegrees)
{
//360 degrees, 2 per coordinate, 2 coordinates for center and end of triangle fan
_cachearraysize = (numdegrees * 2) + 4;
_vertices = new GLfloat[_cachearraysize];
for(int x= 2; x < (_cachearraysize-2); x = x + 2)
{
float degreeinRadians = x*DEG2RAD;
_vertices[x] = cos(degreeinRadians)*radius;
_vertices[x + 1] = sin(degreeinRadians)*radius;
}
//get the X as X of 0 and X of 180 degrees, subtract to get diameter. divide
//by 2 for radius and add back to X of 180
_vertices[0]= ((_vertices[2] - _vertices[362])/2) + _vertices[362];
//same idea for Y
_vertices[1]= ((_vertices[183] - _vertices[543])/2) + _vertices[543];
//close off the triangle fan at the same point as start
_vertices[_cachearraysize -1] = _vertices[0];
_vertices[_cachearraysize] = _vertices[1];
_scalex = scalex;
_scaley = scaley;
_scalez = scalez;
}
~circle()
{
delete[] _vertices;
}
void draw()
{
glScalef(_scalex, _scaley, _scalez);
glVertexPointer(2,GL_FLOAT, 0, _vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, _cachearraysize);
}
};
That's some ugly code, I'd say - lots of magic numbers et cetera.
Try something like:
struct Point {
Point(float x, float y) : x(x), y(y) {}
float x, y;
};
std::vector<Point> points;
const float step = 0.1;
const float radius = 2;
points.push_back(Point(0,0));
// iterate over the angle array
for (float a=0; a<2*M_PI; a+=step) {
points.push_back(cos(a)*radius,sin(a)*radius);
}
// duplicate the first vertex after the centre
points.push_back(points.at(1));
// rendering:
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2,GL_FLOAT,0, &points[0]);
glDrawArrays(GL_TRIANGLE_FAN,0,points.size());
It's up to you to rewrite this as a class, as you prefer. The math behind is really simple, don't fear to try and understand it.