Writing a Raytracer, and cant get the image to be centred properly? - java

I'm writing a Raytracer in Java, I've gotten to the point where I can create objects, rays, test for intersections and then colour pixels. I've also got some basic anti aliasing done. My problem is that if a create a sphere, which should be in the centre of the world (i.e 0.0, 0.0, 0.0) and then draw the image, I end up with a picture like this.
When the red circle should be in the middle of the image.
Main method
public static void main(String[] args) {
System.out.println("Rendering...");
long start = System.nanoTime();
// Setting up the size of the image to be rendered
world = new World(1920, 1080, 1.0);
image = new Image("image.png");
sampler = new SimpleSampler(4);
projector = new OrthographicProjector();
// Main loop of program, goes through each pixel in image and assigns a colour value
for (int y = 0; y < world.viewPlane.height; y++) {
for (int x = 0; x < world.viewPlane.width; x++) {
// Render pixel colour
trace(x, y);
}
}
image.saveImage("PNG");
long end = System.nanoTime();
System.out.print("Loop Time = " + ((end - start)/1000000000.0f));
}
Trace method
public static void trace(int x, int y) {
Colour colour = new Colour();
//int colour = RayTracer.world.backgroundColour.toInteger();
for (int col = 0; col < sampler.samples; col++) {
for (int row = 0; row < sampler.samples; row++) {
Point2D point = sampler.sample(row, col, x, y);
Ray ray = projector.createRay(point);
double min = Double.MAX_VALUE;
Colour tempColour = new Colour();
for (int i = 0; i < world.worldObjects.size(); i++) {
double temp = world.worldObjects.get(i).intersect(ray);
if (temp != 0 && temp < min) {
min = temp;
tempColour = world.worldObjects.get(i).colour;
}
}
colour.add(tempColour);
}
}
colour.divide(sampler.samples*sampler.samples);
image.buffer.setRGB(x, y, colour.toInteger());
}
World.java
public class World {
public ViewPlane viewPlane;
public ArrayList<Renderable> worldObjects;
public Colour backgroundColour;
public World(int width, int height, double size) {
viewPlane = new ViewPlane(width, height, size);
backgroundColour = new Colour(0.0f, 0.0f, 0.0f);
worldObjects = new ArrayList<Renderable>();
worldObjects.add(new Sphere(new Point3D(0.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
//worldObjects.add(new Sphere(new Point3D(-150.0, 0.0, 0.0), 50, new Colour(1.0f, 0.0f, 0.0f)));
//worldObjects.add(new Sphere(new Point3D(0.0, -540.0, 0.0), 50, new Colour(0.0f, 1.0f, 0.0f)));
}
}
SimpleSampler.java
public class SimpleSampler extends Sampler {
public SimpleSampler(int samples) {
this.samples = samples;
}
public Point2D sample(int row, int col, int x, int y) {
Point2D point = new Point2D(
x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);
return point;
}
}
OrthographicProjector.java
public class OrthographicProjector extends Projector{
public Ray createRay(Point2D point) {
Ray ray = new Ray();
ray.origin = new Point3D(
RayTracer.world.viewPlane.size * point.x,
RayTracer.world.viewPlane.size * point.y,
100);
ray.direction = new Vector3D(0.0, 0.0, -1.0);
return ray;
}
}
I have a feeling that somewhere along the way I've mixed an x with a y and this has rotated the image, but I haven't been able to track down the problem. If you would like to see any more of my code I would be happy to show it.

In SimpleSampler.java:
Point2D point = new Point2D(
x - RayTracer.world.viewPlane.width / 2 + (col + 0.5) / samples,
y - RayTracer.world.viewPlane.width / 2 + (row + 0.5) / samples);
You use width for both coordinates. Maybe you should use width and height.

Related

How to animate multiple paths in sequence in Android canvas?

I want to animate multiple paths in sequence, for example, I have
Path[] line and Path[] circle
so when I do for example
line[0].moveTo(x, y);
graphPath[j].lineTo(x, y);
I want to animate this part first and when
finish I want to add circle in same x,y postion
circle[0].addCircle(x, y, 10, Path.Direction.CW);
what I have done so far inside onDraw()
#Override
protected void onDraw(Canvas canvas) {
for (int j = 0; j < 2; j++) {
if (dataPoints2[j] != null) {
float x = leftPadding;
float y = height * getDataPoint(0, j) + topPadding;
graphPath[j].moveTo(x, y);
circlePath[j].addCircle(x, y, 10, Path.Direction.CW);
for (int i = 1; i < dataPoints2[j].size(); i++) {
x = width * (((float) i ) / dataPoints2[j].size()) + leftPadding;
y = height * getDataPoint(i, j) + topPadding;
graphPath[j].lineTo(x, y);
circlePath[j].addCircle(x, y, 10, Path.Direction.CW);
}
}
}
PathMeasure measure = new PathMeasure(graphPath[0], false);
length = measure.getLength();
ObjectAnimator animator = ObjectAnimator.ofFloat(Chart.this, "phase", 1.0f, 0.0f);
animator.setDuration(8000);
animator.start();
for (int j = 0; j < graphPath.length; j++) {
canvas.drawPath(graphPath[j], linePaint[j]);
canvas.drawPath(circlePath[j], circlePaint[j]);
}
}
public void setPhase(float phase){
linePaint[0].setPathEffect(createPathEffect(length, phase, 0.0f));
invalidate();
}
private static PathEffect createPathEffect(float pathLength, float phase, float offset)
{
return new DashPathEffect(new float[] { pathLength, pathLength },
Math.max(phase * pathLength, offset));
}
this will draw the circle then animate the line connecting the circle points
what I need is to animate the line and draw circle when the line reaches every x,y points

Converting monochrome image to minimum number of 2d shapes

Basically, what I need to do is take a 2d array of bitflags and produce a list of 2d rectangles to fill the entire area with the minimum number of total shapes required to perfectly fill the space. I am doing this to convert a 2d top-down monochrome of a map into 2d rectangle shapes which perfectly represent the passed in image which will be used to generate a platform in a 3d world. I need to minimize the total number of shapes used, because each shape will represent a separate object, and flooding it with 1 unit sized squares for each pixel would be highly inefficient for that engine.
So far I have read in the image, processed it, and filled a two dimensional array of booleans which tells me if the pixel should be filled or unfilled, but I am unsure of the most efficient approach of continuing.
Here is what I have so far, as reference, if you aren't following:
public static void main(String[] args) {
File file = new File(args[0]);
BufferedImage bi = null;
try {
bi = ImageIO.read(file);
} catch (IOException ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
if (bi != null) {
int[] rgb = bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), new int[bi.getWidth() * bi.getHeight()], 0, bi.getWidth());
Origin origin = new Origin(bi.getWidth() / 2, bi.getHeight() / 2);
boolean[][] flags = new boolean[bi.getWidth()][bi.getHeight()];
for (int y = 0; y < bi.getHeight(); y++) {
for (int x = 0; x < bi.getWidth(); x++) {
int index = y * bi.getWidth() + x;
int color = rgb[index];
int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
if (type == 2) {
origin = new Origin(x, y);
}
flags[x][y] = type != 1;
}
}
List<Rectangle> list = new ArrayList();
//Fill list with rectangles
}
}
White represents no land. Black or Red represents land. The check for the red pixel marks the origin position of map, which was just for convenience and the rectangles will be offset by the origin position if it is found.
Edit: The processing script does not need to be fast, the produced list of rectangles will be dumped and that will be what will be imported and used later, so the processing of the image does not need to be particularly optimized, it doesn't make a difference.
I also just realized that expecting a 'perfect' solution is expecting too much, since this would qualify as a 'knapsack problem' of the multidimensionally constrained variety, if I am expecting exactly the fewest number of rectangles, so simply an algorithm that produces a minimal number of rectangles will suffice.
Here is a reference image for completion:
Edit 2: It doesn't look like this is such an easy thing to answer given no feedback yet, but I have started making progress, but I am sure I am missing something that would vastly reduce the number of rectangles. Here is the updated progress:
static int mapWidth;
static int mapHeight;
public static void main(String[] args) {
File file = new File(args[0]);
BufferedImage bi = null;
System.out.println("Reading image...");
try {
bi = ImageIO.read(file);
} catch (IOException ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
if (bi != null) {
System.out.println("Complete!");
System.out.println("Interpreting image...");
mapWidth = bi.getWidth();
mapHeight = bi.getHeight();;
int[] rgb = bi.getRGB(0, 0, mapWidth, mapHeight, new int[mapWidth * mapHeight], 0, mapWidth);
Origin origin = new Origin(mapWidth / 2, mapHeight / 2);
boolean[][] flags = new boolean[mapWidth][mapHeight];
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
int index = y * mapWidth + x;
int color = rgb[index];
int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
if (type == 2) {
origin = new Origin(x, y);
}
flags[x][y] = type != 1;
}
}
System.out.println("Complete!");
System.out.println("Processing...");
//Get Rectangles to fill space...
List<Rectangle> rectangles = getRectangles(flags, origin);
System.out.println("Complete!");
float rectangleCount = rectangles.size();
float totalCount = mapHeight * mapWidth;
System.out.println("Total units: " + (int)totalCount);
System.out.println("Total rectangles: " + (int)rectangleCount);
System.out.println("Rectangle reduction factor: " + ((1 - rectangleCount / totalCount) * 100.0) + "%");
System.out.println("Dumping data...");
try {
file = new File(file.getParentFile(), file.getName() + "_Rectangle_Data.txt");
if(file.exists()){
file.delete();
}
file.createNewFile();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
for(Rectangle rect: rectangles){
bw.write(rect.x + "," + rect.y + "," + rect.width + ","+ rect.height + "\n");
}
bw.flush();
bw.close();
} catch (Exception ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
System.out.println("Complete!");
}else{
System.out.println("Error!");
}
}
public static void clearRange(boolean[][] flags, int xOff, int yOff, int width, int height) {
for (int y = yOff; y < yOff + height; y++) {
for (int x = xOff; x < xOff + width; x++) {
flags[x][y] = false;
}
}
}
public static boolean checkIfFilled(boolean[][] flags, int xOff, int yOff, int width, int height) {
for (int y = yOff; y < yOff + height; y++) {
for (int x = xOff; x < xOff + width; x++) {
if (!flags[x][y]) {
return false;
}
}
}
return true;
}
public static List<Rectangle> getRectangles(boolean[][] flags, Origin origin) {
List<Rectangle> rectangles = new ArrayList();
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
if (flags[x][y]) {
int maxWidth = 1;
int maxHeight = 1;
Loop:
//The search size limited to 400x400 so it will complete some time this century.
for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
if (w * h > maxWidth * maxHeight) {
if (checkIfFilled(flags, x, y, w, h)) {
maxWidth = w;
maxHeight = h;
break Loop;
}
}
}
}
//Search also in the opposite direction
Loop:
for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
if (w * h > maxWidth * maxHeight) {
if (checkIfFilled(flags, x, y, w, h)) {
maxWidth = w;
maxHeight = h;
break Loop;
}
}
}
}
rectangles.add(new Rectangle(x - origin.x, y - origin.y, maxWidth, maxHeight));
clearRange(flags, x, y, maxWidth, maxHeight);
}
}
}
return rectangles;
}
My current code's search for larger rectangles is limited to 400x400 to speed up testing, and outputs 17,979 rectangles, which is a 99.9058% total reduction of rectangles if I treated each pixel as a 1x1 square(19,095,720 pixels). So far so good.

Pixel collision detection in libgdx

I am creating a maze game in libGDX. The background texture is very similar to this one: http://www.wpclipart.com/recreation/games/maze/maze_square_medium.png
My moving character is on the maze, and I would like to do collision detection between the character and black walls by checking the colour of my maze texture, without creating rectangles and figuring out all the coordinates of the maze walls, if that is possible. I have researched a lot, but since I am quite new to libGDX and even java, I haven't really found anything that made a lot of sense to me for my case. If anyone has an answer for me on how I could do this or can refer me to a page that explains this well, it would be very greatly appreciated!
EDIT:
This is my code right now:
private static final int COLS = 4;
private static final int ROWS = 4;
Sprite sprChar, sprMaze;
SpriteBatch batch;
Texture Sprite;
TextureRegion[] frames;
TextureRegion CurrentFrame;
float fTime = 0f, fSpeed = 4;
Animation animation;
Texture txtMaze;
Pixmap pixmap;
int nX, nY, nPix, nR, nG, nB, nA;
Color color;
#Override
public void create() {
batch = new SpriteBatch();
Sprite = new Texture(Gdx.files.internal("snowwhite.png"));
sprChar = new Sprite(Sprite);
sprChar.setPosition(300, 200);
TextureRegion[][] tmp = TextureRegion.split(Sprite, Sprite.getWidth() / COLS, Sprite.getHeight(
frames = new TextureRegion[COLS * ROWS];
int index = 0;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
frames[index++] = tmp[i][j];
}
}
animation = new Animation(1f, frames);
sprChar.setPosition(10, 10);
pixmap = new Pixmap(Gdx.files.internal("Maze2.png"));
color = new Color();
}
#Override
public void render() {
if (fTime < 4) {
fTime += Gdx.graphics.getDeltaTime();
} else {
fTime = 0;
}
nX = Math.round(sprChar.getX());
nY = Math.round(sprChar.getY());
nPix = pixmap.getPixel(nX, nY);
Color.rgba8888ToColor(color, nPix);
nR = (int) (color.r * 255f);
nG = (int) (color.g * 255f);
nB = (int) (color.b * 255f);
nA = (int) (color.a * 255f);
if (nR == 0 && nG == 0 && nB == 0) {
System.out.println("is hit");
}
txtMaze = new Texture(pixmap);
sprMaze = new Sprite(txtMaze);
CurrentFrame = animation.getKeyFrame(0);
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
sprChar.setX(sprChar.getX() - fSpeed);
CurrentFrame = animation.getKeyFrame(4 + fTime);
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
sprChar.setX(sprChar.getX() + fSpeed);
CurrentFrame = animation.getKeyFrame(8 + fTime);
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
sprChar.setY(sprChar.getY() + fSpeed);
CurrentFrame = animation.getKeyFrame(12 + fTime);
}
if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
sprChar.setY(sprChar.getY() - fSpeed);
CurrentFrame = animation.getKeyFrame(0 + fTime);
}
batch.begin();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.draw(sprMaze, sprMaze.getX(), sprMaze.getY(), sprMaze.getWidth() / 2,
sprMaze.getHeight() / 2, sprMaze.getWidth(),
sprMaze.getHeight(), sprMaze.getScaleX(),
sprMaze.getScaleY(), sprMaze.getRotation());
batch.draw(CurrentFrame, sprChar.getX(), sprChar.getY(), 20, 20);
batch.end();
}
public static void updatePosition(Sprite spr, float fX, float fY) {
spr.setPosition(fX, fY);
}
I just want it to output "is hit" if it passes a black wall, but I don't believe I am getting correct responses. Also, if I try to resize the maze sprite, it seems to me that it still checks for pixels on the maze at the original size.
P.S. Please excuse my bad code, I am VERY new. So any help would be very greatly appreciated, as I am on a time crunch!! Thank you

Reflections not reflecting properly

I am writing a ray tracer.I am currently working on reflections.But the seem not to be reflecting correctly.I keep on getting StackOverflowError.I increased the memory and it runs now but the reflections are not like I thought the would be this.
(source: ageofarmour.com)
I thought it would Reflect the reflections!But it just ends up like this.
Note:This is after moved the normal off the object and changed the color calculations!Check the Cal_Reflection for new color calculation!
Here is my code for my tracer!
public class Tracer {
public boolean Tracing;
public Camera Cam;
public int Width, Height;
public BufferedImage Image;
public Color BackGroundColor;
public int StartX, StartY, EndX, EndY, RowCount, ColCount;
public double AmbientLight;
public double DiffuseLight;
public int MaxReflectionCount;
public ArrayList<GeometricObject> GeoObjects;
public ArrayList<LightObject> LightObjects;
public Tracer(Camera cam, int width, int height, BufferedImage image, Color backGroundColor, int startX, int startY, int endX, int endY, int rowCount, int colCount, double ambientLight, double diffuseLight, int maxReflectionCount, ArrayList<GeometricObject> geoObjects, ArrayList<LightObject> lightObjects) {
super();
Cam = cam;
Width = width;
Height = height;
Image = image;
BackGroundColor = backGroundColor;
StartX = startX;
StartY = startY;
EndX = endX;
EndY = endY;
RowCount = rowCount;
ColCount = colCount;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = geoObjects;
LightObjects = lightObjects;
}
public void TracePixelFast(int x, int y) {
Color color = new Color(BackGroundColor.r, BackGroundColor.g, BackGroundColor.b);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, 1, 1, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, 1, 1, RowCount, ColCount));
double hit = GO.hit(r);
if (hit != 0.0) {
color = Cal_Pixel(x, y);
Image.setRGB(x, y, color.toInt());
break;
}
}
}
public void TracePixelSmooth(int x, int y) {
Image.setRGB(x, y, Cal_Pixel(x, y).toInt());
}
public Color Cal_Pixel(int x, int y) {
Color color = new Color(BackGroundColor);
Color colorh = new Color(BackGroundColor);
Color bgc = new Color(BackGroundColor);
int HIT = 0;
int MISS = 0;
for (int row = 0; row < RowCount; row++) {
for (int col = 0; col < ColCount; col++) {
double min = Double.MAX_VALUE;
Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, row, col, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, row, col, RowCount, ColCount));
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
colorh = ShadePixel(0, GO, r, hit);
HIT++;
} else {
double min2 = Double.MAX_VALUE;
for (int o2 = 0; o2 < GeoObjects.size(); o2++) {
if (o != o2) {
GeometricObject GO2 = GeoObjects.get(o2);
double hit2 = GO2.hit(r);
if (hit2 != 0.0 && hit2 < min2) {
min2 = hit2;
bgc = ShadePixel(0, GO2, r, hit2);
}
}
}
MISS++;
}
}
}
}
for (int h = 0; h < HIT; h++) {
color.Add(colorh);
}
for (int m = 0; m < MISS; m++) {
color.Add(bgc);
}
color.Divide(RowCount * ColCount);
return color;
}
public Color ShadePixel(int ReflectionDepthCount, GeometricObject GO, Ray ray, double t) {
Normal normal = GO.Cal_Normal(ray, t);
if (GO.Reflectivity > 0) {
Color GoColor = new Color(Cal_Reflection(GO, ReflectionDepthCount, ray, normal));
Color finalcolor = new Color(Cal_Light(GoColor, normal));
return finalcolor;
} else {
;
Color finalcolor = new Color(Cal_Light(GO.Color, normal));
return finalcolor;
}
}
public Color Cal_Light(Color color, Normal normal) {
ArrayList<Color> PixelShade = new ArrayList<Color>();
Color Final = new Color();
for (int l = 0; l < LightObjects.size(); l++) {
LightObject light = LightObjects.get(l);
Vector3D r_Dir = light.Pos.Sub(normal.Origin);
r_Dir.normalize();
Ray raytolight = new Ray(normal.Origin, r_Dir);
int WAS_HIT = 0;
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject NGO = GeoObjects.get(o);
double hit = NGO.hit(raytolight);
if (hit != 0.0) {
WAS_HIT = 1;
}
}
PixelShade.add(light.ShadePixel(WAS_HIT, normal, r_Dir, color, AmbientLight, DiffuseLight));
}
for (int s = 0; s < PixelShade.size(); s++) {
Final.Add(PixelShade.get(s));
}
Final.Divide(PixelShade.size());
return Final;
}
public Color Cal_Reflection(GeometricObject OriginalObject, int ReflectionDepthCount, Ray InRay, Normal normal) {
if (ReflectionDepthCount <= MaxReflectionCount) {
GeometricObject LastGO = null;
Ray LastRay = null;
double LastT = 0.0;
double min = Double.MAX_VALUE;
Vector3D Origin = normal.Origin.Add(normal.Direction.Mul(1E-100));
Vector3D Direction = normal.Direction;
Direction.normalize();
Ray r = new Ray(Origin, Direction);
for (int o = 0; o < GeoObjects.size(); o++) {
GeometricObject GO = GeoObjects.get(o);
double hit = GO.hit(r);
if (hit != 0.0 && hit < min) {
min = hit;
LastGO = GO;
LastRay = r;
LastT = hit;
}
}
if (LastGO != null) {
System.out.println(ReflectionDepthCount);
Color Reflected = new Color(ShadePixel(ReflectionDepthCount++, LastGO, LastRay, LastT));
Color HitColor = new Color(LastGO.Color);
Color FinalColor = new Color(OriginalObject.Color);
Reflected.Mul(OriginalObject.Reflectivity);
HitColor.Mul(OriginalObject.Reflectivity);
FinalColor.Add(HitColor);
FinalColor.Add(Reflected);
FinalColor.Divide(2);
return FinalColor;
}
} else {
return BackGroundColor;
}
return OriginalObject.Color;
}
public void TraceArea(boolean SmoothTracing) {
Tracing = true;
if (SmoothTracing) {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelSmooth(x, y);
}
}
} else {
for (int x = StartX; x < EndX; x++) {
for (int y = StartY; y < EndY; y++) {
TracePixelFast(x, y);
}
}
}
}}
Here is my code for my sphere!
public class Sphere extends GeometricObject{
public Vector3D Center;
public double Radius;
public Sphere(Vector3D Center,Color Color,double Radius,double Reflectivity){
this.Center = Center;
this.Radius = Radius;
this.Color = Color;
this.Reflectivity = Reflectivity;
}
public double hit(Ray ray) {
double a = ray.Direction.Dot(ray.Direction);
double b = 2 * ray.Origin.Sub(Center).Dot(ray.Direction);
double c = ray.Origin.Sub(Center).Dot(ray.Origin.Sub(Center))-Radius*Radius;
double discreminant = b*b-4*a*c;
if(discreminant < 0.0f){
return 0.0;
}else{
double t = (-b - Math.sqrt(discreminant))/(2*a);
if(t > 10E-9){
return t;
}else{
return 0.0;
}
}
}
public Normal Cal_Normal(Ray ray,double t) {
Vector3D NPos = new Vector3D(ray.Origin.x + ray.Direction.x*t,ray.Origin.y + ray.Direction.y*t,ray.Origin.z + ray.Direction.z*t);
Vector3D NDir = NPos.Sub(Center).Div(Radius);
NDir.normalize();
return new Normal(NPos,NDir);
}}
And here is my launcher that controls the scene and tracer!
public class Launcher {
public static int Width = 600;
public static int Height = 600;
public static void main(String[] args) {
Scene scene = new Scene(Width, Height, 8, 8, new Color(0, 0, 0), new Camera(new Vector3D(0, 0, 30), new Vector3D(0.3, 0, -1), 1, true, Width, Height, 40), 0.1, 0.2, 2);
// scene.AddObject(new Sphere(new Vector3D(0,0,0),new
// Color(0,255,0),5,1));
// scene.AddObject(new Sphere(new Vector3D(-30,0,0),new
// Color(0,0,255),10,0.5));
// scene.AddObject(new Sphere(new Vector3D(30,0,0),new
// Color(255,0,0),10,0.5));
scene.AddObject(new Sphere(new Vector3D(15, 0, 0), new Color(255, 0, 0), 15, 1));
scene.AddObject(new Sphere(new Vector3D(-15, 0, 0), new Color(0, 0, 255), 15, 1));
scene.AddLight(new NonColoredLight(new Vector3D(0, 0, 20), 0.1));
long Start = System.currentTimeMillis();
BufferedImage Image = scene.Trace(false, 2);
long End = System.currentTimeMillis();
System.out.println("Milli Seconds To Render " + (End - Start));
File ImageFile = new File("TracedImage.png");
try {
ImageIO.write(Image, "PNG", ImageFile);
} catch (IOException e) {
e.printStackTrace();
}
}}
And here is the scene code!
public Scene(int width, int height, int row, int col, Color backGroundColor, Camera cam, double ambientLight, double diffuseLight, int maxReflectionCount) {
super();
Width = width;
Height = height;
Row = row;
Col = col;
BackGroundColor = backGroundColor;
Cam = cam;
AmbientLight = ambientLight;
DiffuseLight = diffuseLight;
MaxReflectionCount = maxReflectionCount;
GeoObjects = new ArrayList<GeometricObject>();
LightObjects = new ArrayList<LightObject>();
if (ambientLight > 1) {
AmbientLight = 1;
} else if (ambientLight < 0) {
AmbientLight = 0;
} else {
AmbientLight = ambientLight;
}
if (diffuseLight > 1) {
DiffuseLight = 1;
} else if (diffuseLight < 0) {
DiffuseLight = 0;
} else {
DiffuseLight = ambientLight;
}
}
public void AddObject(GeometricObject GO) {
GeoObjects.add(GO);
}
public void AddLight(LightObject Light) {
LightObjects.add(Light);
}
public BufferedImage Trace(boolean SmoothTracing, int ThreadCount) {
Image = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Tracer tracer = new Tracer(Cam, Width, Height, Image, BackGroundColor, 0, 0, Width, Height, Row, Col, AmbientLight, DiffuseLight, MaxReflectionCount, GeoObjects, LightObjects);
tracer.TraceArea(SmoothTracing);
return Image;
}}
If you need me to post any more of my code just let me know!
Thanks in advance!
Variables for first reflection!
AmbientLight 0.1
BackGroundColor Color (id=39)
b 0.0
g 0.0
r 0.0
Cam Camera (id=41)
Direction Vector3D (id=104)
x 0.3
y 0.0
z -1.0
Distance 357.526077778263
FOV 40.0
Height 600
Perspective true
PixelSize 1.0
Pos Vector3D (id=115)
x 0.0
y 0.0
z 30.0
u Vector3D (id=116)
x -0.9578262852211514
y 0.0
z -0.28734788556634544
v Vector3D (id=117)
x 0.0
y 1.0
z 0.0
w Vector3D (id=118)
x 0.2873478855663454
y 0.0
z -0.9578262852211513
Width 600
ColCount 8
DiffuseLight 0.1
EndX 600
EndY 600
GeoObjects ArrayList<E> (id=43)
[0] Sphere (id=26)
[1] Sphere (id=34)
Height 600
Image BufferedImage (id=50)
accelerationPriority 0.5
colorModel DirectColorModel (id=120)
imageType 1
osis null
properties null
raster IntegerInterleavedRaster (id=124)
surfaceManager null
LightObjects ArrayList<E> (id=59)
[0] NonColoredLight (id=88)
MaxReflectionCount 2
RowCount 8
StartX 0
StartY 0
Tracing true
Width 600
OriginalObject Sphere (id=26)
Center Vector3D (id=60)
Color Color (id=61)
Radius 15.0
Reflectivity 1.0
ReflectionDepthCount 0
InRay Ray (id=29)
Direction Vector3D (id=62)
Origin Vector3D (id=63)
normal Normal (id=31)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastGO Sphere (id=34)
Center Vector3D (id=64)
Color Color (id=65)
Radius 15.0
Reflectivity 1.0
LastRay Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
LastT 4.2468950498166125
min 4.2468950498166125
Origin Vector3D (id=36)
x 0.5809721247344103
y -0.023188722729640822
z 0.8135930637731328
Direction Vector3D (id=38)
x -0.32494278056317
y -0.02694400302823308
z 0.9453497818589108
r Ray (id=35)
Direction Vector3D (id=38)
Origin Vector3D (id=36)
Project Zip File
You are reflecting your ray exactly on the surface of the object. When checking intersections with the reflected ray, you hit on the same object again. You assume that checking if the distance is equal to 0.0 is enough to avoid this, but FP numbers are trickier than you think...

Nesting issue in Processing

I'm having an issue with the animation I'm making. The principal idea is that 6 equilateral triangles revolve around a central point, while also rotating about their own selves.
When I run the code, each instance of a triangle uses the previous instance as a reference point, rather than the centre. This causes a cool spiral effect, but it's not what I'm after.
Code follows:
//Declare
tri myTri1;
tri myTri2;
tri myTri3;
tri myTri4;
tri myTri5;
tri myTri6;
void setup() {
size(600, 600);
smooth();
//Initialise
myTri1 = new tri();
myTri2 = new tri();
myTri3 = new tri();
myTri4 = new tri();
myTri5 = new tri();
myTri6 = new tri();
}
void draw() {
background(0);
//Call Functions
myTri1.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri2.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri3.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri4.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri5.run();
translate(width/2,height/2);
rotate(PI/3);
translate(-width/2,-height/2);
myTri6.run();
}
Second tab:
class tri {
//Variables
float ax, ay, bx, by, cx, cy; //triangle point coordinates
float theta; //triangle angle
float pi = PI; //pi reference
//Construct
tri() {
theta = PI/6;
ax = 0;
ay = 0;
bx = -50*(sin(theta));
by = +50*(cos(theta));
cx = +50*(sin(theta));
cy = +50*(cos(theta));
}
//Functions
void run() {
translate(width/2, height/2);
revolve(); //revolve triangle about centre
spin(); //spin triangle about itself
pulse(); //move triangle in/out
display(); //show triangle
translate(-width/2, -height/2);
}
void spin() {
translate(0, by/2); //sets rotation axis to centre of triangle
rotate(millis()*-0.0005*pi);
translate(0, -by/2); //resets axis to centre of window
}
void revolve() {
translate(-2*by, 0);
ax = ax + 2*sin(millis()*0.005);
ay = ay + 4*cos(millis()*0.005);
bx = bx + 2*sin(millis()*0.005);
by = by + 4*cos(millis()*0.005);
cx = cx + 2*sin(millis()*0.005);
cy = cy + 4*cos(millis()*0.005);
translate(2*by, 0);
}
void pulse() {
ay = ay + 5*sin(millis()*0.005);
by = by + 5*sin(millis()*0.005);
cy = cy + 5*sin(millis()*0.005);
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(ax, ay, bx, by, cx, cy);
}
}
If anyone can point out where I'm going wrong with this it would be awesome, and if you can suggest any optimisations RE the formation of the hexagon (instead of the mess of translations) I would be incredibly happy.
Franchesca's suggestion good. You should have an idea of where the origin is and how the coordinate space transformations you apply affect that, at least until you get a feel for it and you're in complete control.
I also warmly recommend this Processing tutorial on 2d transformations
Now, back to your code :)
First thing you can improve is getting used to for loops and arrays.
They may look scary at first, but once you get the hang of them they're quite easy.
Wherever you can think of a situation where repetition is needed, you can use a for loop to make your life easier.
In your case, generating the triangles and storing them can be done using loops and arrays.
For loop have the following syntax:
for keyword (3 elements: a start point,an end point(condition) and an increment,(separated by the ; character)
Let's say you want to move from a(0) to b(10) one step at a time:
for(int currentPos = 0 ; currentPos < 10; currentPos++){
println("step: " + currentPos);
}
If you can walk, you can also skip :)
for(int currentPos = 0 ; currentPos < 10; currentPos+=2){
println("step: " + currentPos);
}
even backwards if you want:
for(int currentPos = 10 ; currentPos > 0; currentPos--){
println("step: " + currentPos);
}
This is very useful when traversing all sort of data(triangles in a scene, vertices in a triangle, etc.)
How do you organize your data ? You place it in a list or array.
An array contains elements of the same type and has a set length.
The syntax to declare an array is like so:
ObjectType[] nameOfArray;
and you can initialize an empty array:
int[] fiveNumbers = new int[5];//new keyword then the data type and length in sq.brackets
or you can initialize the array with values:
String[] words = {"ini","mini","miny","moe"};
You access elements in an array using square brackets and the index of the object in the list you want to access. Arrays have a length property so you can easily count objects.
background(255);
String[] words = {"ini","mini","miny","moe"};
for(int i = 0 ; i < words.length; i++){
fill(map(i,0,words.length, 0,255));
text(words[i],10,10*(i+1));
}
Now back to your original question.
Here is your main code simplified using for loops and arrays:
//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
void setup() {
size(600, 600);
smooth();
//Initialise
for(int i = 0 ; i < numTri; i++){
triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
}
}
void draw() {
background(0);
translate(width * .5, height * .5);//move everything to the centre
for(int i = 0 ; i < numTri; i++){
pushMatrix();
rotate(angleIncrement * i);//rotate from the last offset(centre)
translate(radius,0);//move on (rotated) X axis away from the centre
triangles[i].run();
popMatrix();
}
}
void drawAxes(int size){
pushStyle();
stroke(255,0,0);
line(0,0,size,0);
stroke(0,255,0);
line(0,0,0,size);
popStyle();
}
Notice I've indented the code within push/pop matrix calls.
It's not necessary but I've added that so you can get a feel for how coordinate spaces nest.
These call are very useful as they deal with the nitty gritty math part behind the scenes for you. Notice how I'm placing the symbols in a circle without using the polar to cartesian conversion formula (cos(angle) * radius, sin(angle) * radius).
You can test that with this code from your other tab:
class tri {
//Variables
float ax, ay, bx, by, cx, cy; //triangle point coordinates
float theta; //triangle angle
float pi = PI; //pi reference
//Construct
tri() {
theta = PI/6;
ax = 0;
ay = 0;
bx = -50*(sin(theta));
by = +50*(cos(theta));
cx = +50*(sin(theta));
cy = +50*(cos(theta));
}
//Functions
void run() {
pushMatrix();
revolve(); //revolve triangle about centre
// pulse(); //move triangle in/out
display(); //show triangle
popMatrix();
}
void revolve() {
translate(-2*by, 0);
float angle = millis()*0.005;
float cos = cos(angle);
float sin = sin(angle);
ax = ax + 2*sin;
ay = ay + 4*cos;
bx = bx + 2*sin;
by = by + 4*cos;
cx = cx + 2*sin;
cy = cy + 4*cos;
translate(2*by, 0);
}
void pulse() {
ay = ay + 5*sin(millis()*0.005);
by = by + 5*sin(millis()*0.005);
cy = cy + 5*sin(millis()*0.005);
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(ax, ay, bx, by, cx, cy);
}
}
Also notice I've added a drawAxes function. That's just a utility to make it easier to understand in what coordinate space your drawing.
Again, going back to arrays and for loops, here's a modified version of your code:
class tri {
//Variables
float ai = TWO_PI/3;//angle increment
float r = 50;
float sr = r * 1.5;//spin radius
float vt = 5;//vertical translation(for pulse)
PVector[] verts = new PVector[3];
boolean rotateAroundCentre = true;
boolean translateAroundCentre = false;
boolean translateVertically = false;
//Construct
tri() {
for(int i = 0 ; i < 3; i++){
verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
}
}
//Functions
void run() {
pushMatrix();
float angle = millis()*0.0005;
if(rotateAroundCentre) rotate(angle);
if(translateVertically) translate(sin(angle)*vt,0);
if(translateAroundCentre){
// translate(cos(angle) * sr,sin(angle) * r);
// or
rotate(angle);
translate(sr,0);
}
display(); //show triangle
popMatrix();
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
drawAxes(10);
}
}
Feel free to play with the boolean rotateAroundCentre,translateAroundCentre,translateVertically variables and have fun playing with coordinates and geometry :)
For example here's a version of the sketch that you can toggle the 3 options above using the 1/2/3 keys on your keyboard:
//Declare
int numTri = 6;//number of triangles
tri[] triangles = new tri[numTri];//a list/an array of tri objects (currently empty)
float angleIncrement = TWO_PI/numTri;
float radius = 100;
boolean[] options = {false,false,false};
void setup() {
size(600, 600);
smooth();
//Initialise
for(int i = 0 ; i < numTri; i++){
triangles[i] = new tri();//allocate/initialise each tri object into it's 'slot' in the list/array
}
}
void draw() {
background(0);
translate(width * .5, height * .5);//move everything to the centre
for(int i = 0 ; i < numTri; i++){
pushMatrix();
rotate(angleIncrement * i);//rotate from the last offset(centre)
translate(radius,0);//move on (rotated) X axis away from the centre
drawAxes(20);
triangles[i].run();
popMatrix();
}
}
void drawAxes(int size){
pushStyle();
stroke(255,0,0);
line(0,0,size,0);
stroke(0,255,0);
line(0,0,0,size);
popStyle();
}
void keyReleased(){
for(int i = 0 ; i < 3; i++) if(key == (49+i)) options[i] = !options[i];//quick'n'dirty option toggling
for(int i = 0; i < numTri; i++) {
triangles[i].rotateAroundCentre = options[0];
triangles[i].translateAroundCentre = options[1];
triangles[i].translateVertically = options[2];
}
}
class tri {
//Variables
float ai = TWO_PI/3;//angle increment
float r = 50;
float sr = r * 1.5;//spin radius
float vt = 5;//vertical translation(for pulse)
PVector[] verts = new PVector[3];
boolean rotateAroundCentre = false;
boolean translateAroundCentre = false;
boolean translateVertically = false;
//Construct
tri() {
for(int i = 0 ; i < 3; i++){
verts[i] = new PVector(cos(ai * i) * r,sin(ai * i) * r);
}
}
//Functions
void run() {
pushMatrix();
float angle = millis()*0.0005;
if(rotateAroundCentre) rotate(angle);
drawAxes(30);
if(translateVertically) translate(sin(angle)*vt,0);
drawAxes(40);
if(translateAroundCentre){
// translate(cos(angle) * sr,sin(angle) * r);
// or
rotate(angle);
drawAxes(40);
translate(sr,0);
}
display(); //show triangle
popMatrix();
}
void display() {
fill(255);
strokeWeight(0.8);
triangle(verts[0].x, verts[0].y, verts[1].x, verts[1].y, verts[2].x, verts[2].y);
drawAxes(10);
}
}

Categories

Resources