How to detect object clicked in WebGL? - webgl

Firstly, I am not using 3Js in my Orbits app because I encountered a number of limitations including, but not limited to, issues with texture resolution and my requirement for complex lighting equations but I would like to implement something like 3Js' raycaster to allow me to detect the object clicked by the user.
I'm new to WebGL, but an "old hand" in software development so I'm looking for some hints about where to start.

The approach is as follows:
You generate your scene twice, once normally which is displayed and the second, with the objects uniquely coloured but not displayed. Then you use gl.readPixels from the second scene using the position on the first and decode the colour to identify the object.
Now I have to implement it myself.

Picking spheres
When picking spheres, or objects that are separated (not one inside another) you can use a simple distance from ray to very quickly get the closest object.
Example
The function returns a function that does the calculation. As it is only the closest you are interested in the distances can remain as squares. The distance from the camera is held as a unit distance along the ray.
function distanceFromRay() {
var dSqr, ox, oy, oz, vx, vy, vz;
function distanceSqr(px, py, pz) {
const ax = px - ox, ay = py - oy, az = pz - oz;
const u = (ax * vx + ay * vy + az * vz) / dSqr;
distanceSqr.unit = u;
if (u > 0) { // is past origin
const bx = ox + vx * u - px, by = oy + vy * u - py, bz = oz + vz * u - pz;
return bx * bx + by * by + bz * bz; // dist sqr to closest point on ray
}
return Infinity;
}
distanceSqr.unit = 0;
distanceSqr.setRay(x, y, z, xx, yy, zz) { // ray from origin x, y,z,
// infinite length along xx,yy,zz
(ox = x, oy = y, oz = z);
(vx = xx, vy = yy, vz = zz);
dSqr = vx * vx + vy * vy + vz * vz;
}
return distanceSqr;
}
Usage
There is a one time setup call;
// setup
const distToRay = distanceFromRay();
At the start of a frame that requires a pick, calculate the pick ray and set it. Also set the min distance from ray and eye.
// at start of frame set pick ray
distToRay.setRay(eye.x, eye.y, eye.z, pointer.ray.x, pointer.ray.y, pointer.ray.y);
var minDist = maxObjRadius * maxObjRadius;
var nearestObj = undefined;
var eyeDist = Infinity;
Then for each pickable object get the distance by passing the objects center and comparing it to any previous (in frame) found distance, objects radius, and distance from eye.
// per object
const dis = distToRay(obj.pos.x, obj.pos.y, obj.pos.z);
if (dis < obj.radius && dis < minDist && distToRay.unit > 0 && distToRay.unit < eyeDist ) {
minDist = dis;
eyeDist = distToRay.unit;
nearestObj = obj;
}
At the end of the frame if nearestObj is not undefined it will hold a reference to the picked object.
// end of frame
if (nearestObj) {
// you have the closest object
}

Related

Get Rho and Theta from Hough-Transform opencvsharp?

I have Hough-Transform implemented using Opencvsharp (opencv), and get the lines detected on my image in console application/windows-from-application:
lines = edgeImg.HoughLines2(storage, HoughLinesMethod.Probabilistic, 1, Math.PI / 180, 60, 100, 100);
for (int i = 0; i < lines.Total; i++)
{
CvLineSegmentPoint segP= lines.GetSeqElem<CvLineSegmentPoint>(i).Value;
double angle = Math.Atan2((segP.P2.Y) - (segP.P1.Y), (segP.P2.X) - (segP.P1.X)) * 180 / Math.PI;
if (Math.Abs(angle) <= 60)
continue;
if (segP.P1.Y > segP.P2.Y + 20 || segP.P1.Y < segP.P2.Y - 20)
src.Line(segP.P1, segP.P2, CvColor.blue, 2, LineType.AntiAlias, 0);
}
I have tried different methods for visualizing the rho-theta space. since "HoughLinesMethod" does all the transformation internally, I have tried to get these values from x,y in the reverse way:
double angle = Math.Atan2(dy, dx) * 180 / Math.PI;
double theta = 90 - angle;
var thetaRad = theta*Math.PI/180;
double rho = (x1 * Math.Cos(thetaRad) + y1 * Math.Sin(thetaRad));
my first question is if I need to get two values for rho/theta, both for x1,y1 and also x2,y2 ; or calculating only one "rho/theta" would be the right intersect?
Thanks!
second, how can I visualize them in the right format? (what I currently see on my outout image is some random white dots at the top left corner of my output)
third, is it rational to get rho,theta values back in this way or you would suggest to perform the hough transform by myself and reduce the complexity? (I used opencvsharp function for better and efficient performance!)

Ray tracing, translucent sphere has a dot in centre

I'm building a ray tracer as an assignment. I'm trying to get refraction working for spheres and I got it half-working. The problem is I can't get rid of the black dot in the centre of the sphere
This is the code for the intersection:
double a = rayDirection.DotProduct(rayDirection);
double b = rayOrigin.VectAdd(sphereCenter.Negative()).VectMult(2).DotProduct(rayDirection);
double c = rayOrigin.VectAdd(sphereCenter.Negative()).DotProduct(rayOrigin.VectAdd(sphereCenter.Negative())) - (radius * radius);
double discriminant = b * b - 4 * a * c;
if (discriminant >= 0)
{
// the ray intersects the sphere
// the first root
double root1 = ((-1 * b - sqrt(discriminant)) / 2.0 * a) - 0.000001;
double root2 = ((-1 * b + sqrt(discriminant)) / 2.0 * a) - 0.000001;
if (root1 > 0.00001)
{
// the first root is the smallest positive root
return root1;
}
else
{
// the second root is the smallest positive root
return root2;
}
}
else
{
// the ray missed the sphere
return -1;
}
This is the code responsible for computing the direction of the new refracted ray:
double n1 = refractionRay.GetRefractiveIndex();
double n2 = sceneObjects.at(indexOfWinningObject)->GetMaterial().GetRefractiveIndex();
if (n1 == n2)
{
// ray inside the same material, means that it is going to be refracted outside,
n2 = 1.000293;
}
double n = n1 / n2;
Vect I = refractionRay.GetRayDirection();
Vect N = sceneObjects.at(indexOfWinningObject)->GetNormalAt(intersectionPosition);
double cosTheta1 = -N.DotProduct(I);
// we need the normal pointing towards the side the ray is coming from
if (cosTheta1 < 0)
{
N = N.Negative();
cosTheta1 = -N.DotProduct(I);
}
double cosTheta2 = sqrt(1 - (n * n) * (1 - (cosTheta1 * cosTheta1)));
Vect refractionDirection = I.VectMult(n).VectAdd(N.VectMult(n * cosTheta1 - cosTheta2));
Ray newRefractionRay(intersectionPosition.VectAdd(refractionDirection.VectMult(0.001)), refractionDirection, n2, refractionRay.GetRemainingIntersections());
When creating the new refracting ray, I tried adding the direction times a small value to the intersection position to make the origin of this new ray inside the sphere. The size of the black dot changes if I change that small value. If I make it too big the margins of the sphere start turning black as well.
If I add colour to the object it looks like this:
And if make that small constant bigger (0.1) this happens:
Is there a special condition I should take into account? Thank you!
You should remove the epsilon factors that you subtract when you calculate the two roots:
double root1 = ((-1 * b - sqrt(discriminant)) / 2.0 * a);
double root2 = ((-1 * b + sqrt(discriminant)) / 2.0 * a);
In my experience the only place you need a comparison against epsilon is when checking whether the found root is along the path of the ray and not at its origin, per your:
if (root1 > 0.00001)
NB: you could eke out a little more performance by only doing the square root calculation once, and also by only calculating root2 if root1 <= epsilon

Converting angular velocity to quaternion in OpenCV

I need the angular velocity expressed as a quaternion for updating the quaternion every frame with the following expression in OpenCV:
q(k)=q(k-1)*qwt;
My angular velocity is
Mat w; //1x3
I would like to obtain a quaternion form of the angles
Mat qwt; //1x4
I couldn't find information about this, any ideas?
If I understand properly you want to pass from this Axis Angle form to a quaternion.
As shown in the link, first you need to calculate the module of the angular velocity (multiplied by delta(t) between frames), and then apply the formulas.
A sample function for this would be
// w is equal to angular_velocity*time_between_frames
void quatFromAngularVelocity(Mat& qwt, const Mat& w)
{
const float x = w.at<float>(0);
const float y = w.at<float>(1);
const float z = w.at<float>(2);
const float angle = sqrt(x*x + y*y + z*z); // module of angular velocity
if (angle > 0.0) // the formulas from the link
{
qwt.at<float>(0) = x*sin(angle/2.0f)/angle;
qwt.at<float>(1) = y*sin(angle/2.0f)/angle;
qwt.at<float>(2) = z*sin(angle/2.0f)/angle;
qwt.at<float>(3) = cos(angle/2.0f);
} else // to avoid illegal expressions
{
qwt.at<float>(0) = qwt.at<float>(0)=qwt.at<float>(0)=0.0f;
qwt.at<float>(3) = 1.0f;
}
}
Almost every transformation regarding quaternions, 3D space, etc is gathered at this website.
You will find time derivatives for quaternions also.
I find it useful the explanation of the physical meaning of a quaternion, which can be seen as an axis angle where
a = angle of rotation
x,y,z = axis of rotation.
Then the conversion uses:
q = cos(a/2) + i ( x * sin(a/2)) + j (y * sin(a/2)) + k ( z * sin(a/2))
Here is explained thoroughly.
Hope this helped to make it clearer.
One little trick to go with this and get rid of those cos and sin functions. The time derivative of a quaternion q(t) is:
dq(t)/dt = 0.5 * x(t) * q(t)
Where, if the angular velocity is {w0, w1, w2} then x(t) is a quaternion of {0, w0, w1, w2}. See David H Eberly's book section 10.5 for proof

Draw Perpendicular line to a line in opencv

I better explain my problem with an Image
I have a contour and a line which is passing through that contour.
At the intersection point of contour and line I want to draw a perpendicular line at the intersection point of a line and contour up to a particular distance.
I know the intersection point as well as slope of the line.
For reference I am attaching this Image.
If the blue line in your picture goes from point A to point B, and you want to draw the red line at point B, you can do the following:
Get the direction vector going from A to B. This would be:
v.x = B.x - A.x; v.y = B.y - A.y;
Normalize the vector:
mag = sqrt (v.x*v.x + v.y*v.y); v.x = v.x / mag; v.y = v.y / mag;
Rotate the vector 90 degrees by swapping x and y, and inverting one of them. Note about the rotation direction: In OpenCV and image processing in general x and y axis on the image are not oriented in the Euclidian way, in particular the y axis points down and not up. In Euclidian, inverting the final x (initial y) would rotate counterclockwise (standard for euclidean), and inverting y would rotate clockwise. In OpenCV it's the opposite. So, for example to get clockwise rotation in OpenCV: temp = v.x; v.x = -v.y; v.y = temp;
Create a new line at B pointing in the direction of v:
C.x = B.x + v.x * length; C.y = B.y + v.y * length;
(Note that you can make it extend in both directions by creating a point D in the opposite direction by simply negating length.)
This is my version of the function :
def getPerpCoord(aX, aY, bX, bY, length):
vX = bX-aX
vY = bY-aY
#print(str(vX)+" "+str(vY))
if(vX == 0 or vY == 0):
return 0, 0, 0, 0
mag = math.sqrt(vX*vX + vY*vY)
vX = vX / mag
vY = vY / mag
temp = vX
vX = 0-vY
vY = temp
cX = bX + vX * length
cY = bY + vY * length
dX = bX - vX * length
dY = bY - vY * length
return int(cX), int(cY), int(dX), int(dY)

How to move 2 xna sprites away from eachother dynamically?

I have two items, lets call them Obj1 and Obj2... Both have a current position pos1 and pos2.. Moreover they have current velocity vectors speed1 and speed2 ... How can I make sure that if their distances are getting closer (with checking current and NEXT distance), they will move farther away from eachother ?
I have a signed angle function that gives me the signed angle between 2 vectors.. How can I utilize it to check how much should I rotate the speed1 and speed2 to move those sprites from eachother ?
public float signedAngle(Vector2 v1, Vector2 v2)
{
float perpDot = v1.X * v2.Y - v1.Y * v2.X;
return (float)Math.Atan2(perpDot, Vector2.Dot(v1, v2));
}
I check the NEXT and CURRENT distances like that :
float currentDistance = Vector2.Distance(s1.position, s2.position);
Vector2 obj2_nextpos = s2.position + s2.speed + s2.drag;
Vector2 obj1_nextpos = s1.position + s1.speed + s1.drag;
Vector2 s2us = s2.speed;
s2us.Normalize();
Vector2 s1us = s1.speed;
s1us.Normalize();
float nextDistance = Vector2.Distance(obj1_nextpos , obj2_nextpos );
Then depending whether they are getting bigger or smaller I want to move them away (either by increasing their current speed at the same direction or MAKING THEM FURTHER WHICH I FAIL)...
if (nextDistance < currentDistance )
{
float angle = MathHelper.ToRadians(180)- signedAngle(s1us, s2us);
s1.speed += Vector2.Transform(s1us, Matrix.CreateRotationZ(angle)) * esc;
s2.speed += Vector2.Transform(s2us, Matrix.CreateRotationZ(angle)) * esc;
}
Any ideas ?
if objects A and B are getting closer, one of the object components (X or Y) is opposite.
in this case Bx is opposite to Ax, so only have to add Ax to the velocity vector of object B, and Bx to velocity vector of object A
If I understood correctly, this is the situation and you want to obtain the two green vectors.
The red vector is easy to get: redVect = pos1 - pos2. redVect and greenVect2 will point to the same direction, so the only step you have is to scale it so its length will match speed2's one: finalGreenVect2 = greenvect2.Normalize() * speed2.Length (although I'm not actually sure about this formula). greenVect1 = -redVect so finalGreenVect1 = greenVect1.Normalize() * speed1.Length. Then speed1 = finalGreenVect1 and speed2 = finalGreenVect2. This approach will give you instant turn, if you prefer a smooth turn you want to rotate the speed vector by:
angle = signedAngle(speed) + (signedAngle(greenVect) - signedAngle(speed)) * 0.5f;
The o.5f is the rotation speed, adjust it to any value you need. I'm afraid that you have to create a rotation matrix then Transform() the speed vector with this matrix.
Hope this helps ;)

Resources