I am trying to write Ray Tracing in the LWJGL to go from the mouse position to the end of the display. I have written the code to find the mouse positions and to create the Vector3's for the start and end position of the rays, but how do I 'fire' the ray and detect an object?
I have tried using Camera's for the positioning, but my friend is the one who wrote the main Display system and its pretty gargled up and hard to read. Instead I opted for just firing the ray from the mouse position inside of the Display to the back Z of the display.
public class Picker {
static Vector3f startRay;
static Vector3f endRay;
public Picker() {
startRay = new Vector3f();
endRay = new Vector3f();
}
private static Vector2f getMouseCoords() {
float x = (2f* Mouse.getX()) / Display.getWidth() - 1;
float y = (2f * Mouse.getY()) / Display.getHeight() - 1f;
return new Vector2f(x, y);
}
public static String getRayStr() {
float x = (2f* Mouse.getX()) / Display.getWidth() - 1;
float y = (2f * Mouse.getY()) / Display.getHeight() - 1f;
String str = "Mouse X: " + x + " Mouse Y: " + y;
str += "\nStart X: " + startRay.x + " Start Y: " + startRay.y + " Start Z: " + startRay.z;
str += "\nEnd X: " + endRay.x + " End Y: " + endRay.y + " End Z: " + endRay.z;
return str;
}
public static void setStartRay() {
Vector2f mousePos = getMouseCoords();
Vector3f tempStartPos = new Vector3f();
tempStartPos.x = mousePos.x;
tempStartPos.y = mousePos.y;
tempStartPos.z = 0;
startRay = tempStartPos;
}
public static void setEndRay() {
Vector2f mousePos = getMouseCoords();
Vector3f tempEndPos = new Vector3f();
tempEndPos.x = mousePos.x;
tempEndPos.y = mousePos.y;
tempEndPos.z = -1;
endRay = tempEndPos;
}
}
I want to be able to detect an object directly inbetween startRay and endRay. The object I want to detect is a 3d model inside of the Display area.
Returning the startRay and endRay Vector3 positions has already confirmed to work, just needing to know about how to:
1) Cast the ray
2) Detect an object between the two points
Thank you very much for your assistance.
Related
I am trying to detect and crop circullar/elliptical shapes of different sizes.
This is an example of an image I am trying to do the detection and croping.
Input Image
The result I am trying to get in the aforementioned image is 3 cropped images
looking like this:
segmented part 1, segmented part 2, segmented part 3
Another image could look like this: different image
Just like the previous image, I am trying to do the same to this one.
The shapes are dramatically smaller from the first one.
Can this be achieved algorithmically or should I look for a machine learning-like solution?
Note: The final image has been applied by the following filters: Gaussian Blur, Grayscale, Threshold, Contour and Morphological Dilation.
[EDIT]
The code I have written(not working as intended):
findReference() finds a shape in the middle of the image and returns its rectangle.
private Rect findReference(Mat inputImage) {
// clone the image
Mat original = inputImage.clone();
// find the center of the image
double[] centers = {(double)inputImage.width()/2, (double)inputImage.height()/2};
Point image_center = new Point(centers);
// finding the contours
ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(inputImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// finding best bounding rectangle for a contour whose distance is closer to the image center that other ones
double d_min = Double.MAX_VALUE;
Rect rect_min = new Rect();
for (MatOfPoint contour : contours) {
Rect rec = Imgproc.boundingRect(contour);
// find the best candidates
if (rec.height > inputImage.height()/2 & rec.width > inputImage.width()/2){
continue;
}
Point pt1 = new Point((double)rec.x, (double)rec.y);
Point center = new Point(rec.x+(double)(rec.width)/2, rec.y + (double)(rec.height)/2);
double d = Math.sqrt(Math.pow((double)(pt1.x-image_center.x),2) + Math.pow((double)(pt1.y -image_center.y), 2));
if (d < d_min)
{
d_min = d;
rect_min = rec;
}
}
// showReference( rect_min, original);
return rect_min;
}
I use the rectangle as reference and create a bigger one and a smaller one, so that similar shapes fit in the dimensions of the smaller and bigger rectangle.
findAllEllipses() tries to find similar shapes fitting in the smaller and bigger rectangles. After that it draws ellipses around the found shapes.
private Mat findAllEllipses(Rect referenceRect, Mat inputImage) {
float per = 0.5f;
float perSquare = 0.05f;
Rect biggerRect = new Rect();
Rect smallerRect = new Rect();
biggerRect.width = (int) (referenceRect.width / per);
biggerRect.height = (int) (referenceRect.height / per);
smallerRect.width = (int) (referenceRect.width * per);
smallerRect.height = (int) (referenceRect.height * per);
System.out.println("reference rectangle height: " + referenceRect.height + " width: " + referenceRect.width);
System.out.println("[" + 0 +"]: biggerRect.height: " + biggerRect.height + " biggerRect.width: " + biggerRect.width);
System.out.println("[" + 0 +"]: smallerRect.height: " + smallerRect.height + " smallerRect.width: " + smallerRect.width);
//Finding Contours
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchey = new Mat();
Imgproc.findContours(inputImage, contours, hierarchey, Imgproc.RETR_TREE,
Imgproc.CHAIN_APPROX_SIMPLE);
System.out.println("the numbers of found contours is: " + contours.size());
int sum = 0;
//Empty rectangle
RotatedRect[] rec = new RotatedRect[contours.size()];
for (int i = 0; i < contours.size(); i++) {
rec[i] = new RotatedRect();
if(contours.get(i).toArray().length >= 5 ){
Rect foundRect = Imgproc.boundingRect(contours.get(i));
// Rect foundBigger = new Rect();
// Rect foundSmaller = new Rect();
//
// foundBigger.width = (int) (foundBigger.width + foundBigger.width * per);
// foundBigger.height = (int) (foundBigger.height + foundBigger.height * per);
//
// foundSmaller.width = (int) (foundRect.width - foundRect.width * per);
// foundSmaller.height = (int) (foundRect.height - foundRect.height * per);
if (
(biggerRect.height >= foundRect.height && biggerRect.width >= foundRect.width)
&& (smallerRect.height <= foundRect.height && smallerRect.width <= foundRect.width)
&& (((foundRect.width - foundRect.width * perSquare) <= foundRect.height) && ((foundRect.width + foundRect.width * perSquare) >= foundRect.height))
&& (((foundRect.height - foundRect.height * perSquare) <= foundRect.width) && ((foundRect.height + foundRect.height * perSquare) >= foundRect.width))
) {
System.out.println("[" + i +"]: foundRect.width: " + foundRect.width + " foundRect.height: " + foundRect.height);
System.out.println("----------------");
rec[i] = Imgproc.fitEllipse(new MatOfPoint2f(contours.get(i).toArray()));
sum++;
}
}
Scalar color_elli = new Scalar(190, 0, 0);
Imgproc.ellipse(inputImage, rec[i], color_elli, 5);
}
System.out.println("found ellipses: " + sum);
// trytest(ImageUtils.doResizeMat(outputImage),0,0);
return inputImage;
}
Unfortuantelly there are several variables that are hardcoded into the method.
This is used to make the smaller and bigger rectangles (used as a percentage)
float per = 0.5f;
perSquare is used to get shapes closer to a square (fluctuated width height)
float perSquare = 0.05f;
This code might work in some images, while on others will not find a single shape, like I mentioned the shapes are circullar/elliptical and of different sizes.
I am trying to get the following image to be projected to a sphere using mercator:
I have gotten this far using the formula from this : how map 2d grid points (x,y) onto sphere as 3d points (x,y,z)
My code is the following to generate the coordinates from (X,Y):
public void generateSphericalCoords(){
int R = 400; // Image Radius
int S = 400; // Sphere Radius
float longitude = (float)(this.x)/R;
float latitude = (float) (2*Math.atan(Math.exp((double)(this.y)/R)) - Math.PI/2);
sphericalX = (int) (S*Math.cos(latitude) * Math.cos(longitude)) + 300;
sphericalY = (int) (S*Math.cos(latitude) * Math.sin(longitude)) + 300;
sphericalZ = (int) (S*Math.sin(longitude));
//System.out.println(sphericalX + " " + sphericalY + " " + sphericalZ);
}
However, instead of getting a perfect sphere, I get this:
What am I doing wrong? Any help would be greatly appreciated.
EDIT:
I have gotten to the following formula:
float longitude = (float) ((float)(Math.PI*this.x)/R - Math.PI/2);
float latitude = (float) (Math.PI*2*Math.atan(Math.exp((float)(this.y)/R)));
sphericalX = (int) (S*Math.cos(latitude) * Math.cos(longitude)) + 300;
sphericalY = (int) (S*Math.cos(latitude) * Math.sin(longitude)) + 300;
sphericalZ = (int) (S*Math.sin(longitude));
However, I have gotten an odd ring along the outside edge, as shown:
I'm not really sure how to even describe this problem so please excuse the terrible title. I have a simple model ( it is actually a tile but I made it a cube to better illustrate the issue ) that is 2 units high, wide and deep. To draw a continuous field of these I simply increment X and Z by 2 appropriately and they all render nicely next to one another. If I want to create a step up so my flat field has a new level to it I add 2 to the Y value for a segment of the field expecting that the bottom of the top level would then align perfectly with the top of the lower level.
What actually happens is the top level renders a fair distance above the lower level. Why? What would cause this? I ran some tests and found that I'd have to increment Y by a number somewhere between 0.6 and 0.7 for the bottom to align properly with the top.
I thought maybe it was the viewport but I think that is fine. The models don't look warped. Has anyone run into something like this before?
See the attached image for an example of what I'm talking about. The red line illustrates this strange separation of the top and bottom layers.
The Render function
public void draw() throws Exception {
float x = 0;
double y = 0;
float z = 0;
int cidx = 0;
boolean firstCube = true;
glfwSwapBuffers(window); // swap the color buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
//calc rotate camera
if (updatecamera == true){
updateCamera();
}
glUseProgram(shader.iProgram);
//some lighting...
Vector4f lp = new Vector4f(lightX, lightY, lightZ,1.0f);
//float[] lp = {xa, ya + 100, za - 120,1.0f}; //set light source to same as camera eye for now.
shader.setUniform(iLightCam, camera);
shader.setUniform(iLightVec, lp);
//get picking ray
if (worldClicked == true){
pick = makeRay(pick, cursorX, (DISPLAY_HEIGHT - ((DISPLAY_HEIGHT - VP_HEIGHT) / 2)) - cursorY);
}
for(Iterator<Quad> qd = quads.iterator(); qd.hasNext(); ) {
//init cull check
frust.cullIn = 0;
frust.cullOut = 0;
quad = qd.next();
pickthisQuad = false;
firstCube = true; //the first cube is used to set the values of the Quad OBB.
for(Iterator<Cube> i = quad.cubes.iterator(); i.hasNext(); ) {
cb = i.next();
x = cb.x;
z = cb.z;
//y = cb.y;
//testing odd Y behaviour
if ( y == 0) {
y = lightX;
}else{
y = 0;
}
System.out.println(" y: " + y);
//init
model.setIdentity();
//ROTATE
//set translate
vTrans.set(transx + x, (float) (transy + y), transz + z);
Matrix4f.translate(vTrans, model1, model);
vTrans.set(-(transx + x), (float) (-transy + y), -(transz + z));
Matrix4f.translate(vTrans, model, model);
Matrix4f.rotate((float) Math.toRadians(rotY), new Vector3f(0,1,0), model, model);
vTrans.set((transx + x), (float) (transy + y), (transz + z));
Matrix4f.translate(vTrans, model, model);
Matrix4f.mul(model, camera, modelview);
shader.setUniform(iModelView, modelview);
Matrix3f norm = new Matrix3f();
norm.m00 = modelview.m00;
norm.m01 = modelview.m01;
norm.m02 = modelview.m02;
norm.m10 = modelview.m10;
norm.m11 = modelview.m11;
norm.m12 = modelview.m12;
norm.m20 = modelview.m20;
norm.m21 = modelview.m21;
norm.m22 = modelview.m22;
shader.setUniform(iNorm, norm);
shader.setUniform(iProj, projection);
shader.setUniform(iCam, camera);
shader.setUniform(iModel, model);
test_renderFrustumandCrosslines();
manageTextures(cb);
render();
cidx++;
}//cubes
cidx = 0;
}//quads
/**
* TESTING
*/
glUseProgram(shaderLine.iProgram);
Matrix4f mvp = new Matrix4f();
mvp.setIdentity();
Matrix4f.mul(projection, camera, mvp);
shaderLine.setUniform(iMVPLine, mvp);
renderLine();
renderCross();
worldClicked = false;
glFinish();
}
Is there any special thoughts about the 2 first translates in the rotation code? The x ans z translations will cancel each other out but not the y axis. Which could be the source of the problem.
vTrans.set(transx + x, (float) (transy + y), transz + z);
Matrix4f.translate(vTrans, model1, model);
vTrans.set(-(transx + x), (float) (-transy + y), -(transz + z));
Matrix4f.translate(vTrans, model, model);
What happens if you remove these 4 lines? You still do the translation after the rotation.
I am currently making an AWT game engine, a simple one just getting conversant with AWT. I am currently working on the physics of the game. I am trying to implement a function that moves the entity forward on it's direction from the north line, or the x axis. This is the code I am using to achieve this:
public void move(double d) {
oldX = x;
oldY = y;
double angle = Math.toRadians(getDirection());
int dx = (int) Math.round(Math.cos(angle) * d);
int dy = (int) Math.round(Math.sin(angle) * d);
setLocation(x + dx, y + dy);
System.out.println("x = " + x);
System.out.println("y = " + y);
System.out.println("direction = " + angle);
System.out.println("----------");
}
and I call this code in this snippet:
for(Entity e : entities) {
if(!e.getLevel().equals(this)) {
return;
}
e.move(3); //testing the code atm
e.changeDirection(e.getDirection() + 3);
}
The problem is that it won't go in a circle, or oval trajectory as I would expect when you have a vector forward (move(int i)) method and changing the direction by 3 every tick as the force acting on the vector, or gravity if we were talking about Earth orbiting objects. This is the expected behaviour.
However, this is what actually happens:
gif demonstration - imgur
Could you tell me why this behavior is occurring and how I could solve this? Thanks
The actual problem, as #harold says, is not the maths but the rendering. Here is what the fixed code to render the 2d entity looks like:
for(Entity e : entities) {
AffineTransform trans = new AffineTransform();
trans.setTransform(identity);
Point mouse = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointToScreen(mouse, this);
trans.rotate(Math.toRadians(e.getDirection()), e.getX(), e.getY());
//changed from trans.rotate(Me.getDirection(), e.getX() + e.image.getWidth(null)/2, e.getY() + e.image.getHeight(null)/2);
trans.translate(e.getX(), e.getY());
g2d.drawImage(e.getImage(), trans, this);
}
This is going to be quite long winded as I'm still relatively very new to Java as a language.
This program has a Class called Point. This creates Points for vertices of shapes.
public class Point {
private int x;
private int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
In this class, there are methods such as distance (between 2 points), scale(increases by factor), translate(takes an x and y coordinate and adds it to this.x and x.y) and so on.
I then have 3 more classes to create shapes. Triangle, Rectangle and Circle, where the constructors look like this.
public class Triangle {
private int sides;
private Point[] points;
public Triangle(Point[] vertices) {
this.sides = 3;
this.points = vertices;
}
.
public class Rectangle {
private int sides;
private Point topCorner;
private int width;
private int length;
public Rectangle(Point corner, int w, int l){
this.sides = 4;
this.topCorner = corner;
this.width = w;
this.length = l;
}
and
public class Circle {
private Point center;
private int radius;
public Circle(Point center, int radius){
this.center = center;
this.radius = radius;
}
All these classes have the same methods translate, and scale. An example of which is shown here.
//For the Rectangle Class
public void translate(int dx, int dy){
topCorner.translate(dx, dy);
}
My problem is in my main class. What happens is I created 3 points, and used all 3 as an array to create the triangle, and 2 others as points for the rectangle and for the circle. Now when I call the translate method on all 3 objects in one go, it ends up doubling the value, as I believe my mistake is that I have passed a reference to "p1" "p2" and "p3" somewhere, and I am changing their values instead of the objects values. Here is the code, and I will explain the output after
public class GraphicsDemo {
public static void main(String[] args) {
Point p1 = new Point(0,20);
Point p2 = new Point(20,0);
Point p3 = new Point(30,30);
Point[] vertices = {p1,p2,p3};
Triangle t = new Triangle(vertices);
Rectangle r = new Rectangle(p1,10,15);
Circle c = new Circle (p2, 25);
System.out.println("Triangle" + "\n" + t.toString() + "\n" + "area:" + t.area() + "\n" + "perimeter: " + t.perimeter());
System.out.println("Reactangle" + "\n" + r.toString() + "\n" + "area:" + r.area() + "\n" + "perimeter: " + r.perimeter());
System.out.println("Circle" + "\n" + c.toString() + "\n" + "area:" + c.area() + "\n" + "perimeter: " + c.perimeter());
c.translate(10, 15);
t.translate(10, 15);
r.translate(10, 15);
System.out.println("Triangle" + "\n" + t.toString() + "\n" + "area:" + t.area() + "\n" + "perimeter: " + t.perimeter());
System.out.println("Reactangle" + "\n" + r.toString() + "\n" + "area:" + r.area() + "\n" + "perimeter: " + r.perimeter());
System.out.println("Circle" + "\n" + c.toString() + "\n" + "area:" + c.area() + "\n" + "perimeter: " + c.perimeter());;
The output for this is that instead of adding 10 and 15 to a shapes coordinates, 20 and 30 are added if the point is used in more than one shape. I'm sure that this is because I'm actually changing the value of the points instead of the objects, but I have no idea how to change this.
Thank you to anyone that can help in advance. This is the first time in my experience where I have tried a lot of things and am up against a wall.
You missed to show the most important part of your code, however it looks like your analysis is correct. In your translation code you actually need to generate new point instances (and replace them).
This is generally a good idea to not modify objects but keep them immutable. Best way to ensure this is to skip the setters:
class Point {
Point(int x, int y) { this.x = x; this.y = y; }
int getX() { return x; } intGetY() { return y; }
/** Create a new point displaced by given coordinates. */
Point translate(int deltaX, int deltaY)
{
return new Point(this.getX() + deltaX, this.getY() + deltaY);
}
}
They are indeed the same object. I.e Triangle t has p1. If you change Rectangle p1 you are changing triangle p1 as well.
You should do deep clones in Point's for them to be independent.
You assumption is correct, you are passing references. Circle, Triangle, Rectangle are all taking one or more points in common with the other shapes in their constructor. See rectangle which takes the same point (p1) as triangle.
Point[] vertices = {p1,p2,p3};
Triangle t = new Triangle(vertices);
Rectangle r = new Rectangle(p1,10,15);
Circle c = new Circle (p2, 25);
A simple solution is to clone the points in the constructors as I did in the Triangle class below. This means the two shapes will share the same x and y to start with, but changes to one shape will no longer directly affect the other.
A class is an object reference. So you need to create a new Object reference to create separate objects to work on. This is what the .clone() method I added to you point class does, it copies the internals of point and creates an exact copy. The constructors of the other methods then invoke the clone method on the points you feed them, so that they are only working on copies of the points you give them.
class Point{
public int x;
public int y;
Point(int x, int y){
this.x = x;
this.y = y;
}
public Point clone(){
return new Point(x,y);
}
}
class Triangle{
final static int m_sides = 3; //constant
Point[] m_points;
Triangle(Point[] points){
//You need to clone your ponits.
m_points = new Point[]{
points[0].clone(),
points[1].clone(),
points[2].clone()};
}