feat: fin de séance 1

This commit is contained in:
Laureηt 2022-04-12 12:08:58 +02:00
commit 60f9b0e464
No known key found for this signature in database
GPG key ID: D88C6B294FD40994
36 changed files with 12717 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.class

11
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"**/*.class": true
}
}

65
DepthBuffer.java Normal file
View file

@ -0,0 +1,65 @@
import java.lang.Double;
/**
* The DepthBuffer class implements a DepthBuffer and its pass test.
*/
public class DepthBuffer {
private double[] buffer;
int width;
int height;
/**
* Constructs a DepthBuffer of size width x height.
* The buffer is initially cleared.
*/
public DepthBuffer (int width, int height) {
buffer = new double[width * height];
this.width = width;
this.height = height;
clear ();
}
/**
* Clears the buffer to infinite depth for all fragments.
*/
public void clear () {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
buffer[i * width + j] = Double.POSITIVE_INFINITY;
}
}
}
/**
* Test if a fragment passes the DepthBuffer test, i.e. is the fragment the
* closest at its position.
*/
public boolean testFragment (Fragment fragment) {
if ((fragment.getX () >= 0) && (fragment.getX () < width) && (fragment.getY () >= 0) && (fragment.getY () < height)) {
/* COMPLETER */
return false;
} else {
return false;
}
}
/**
* Writes the fragment depth to the buffer
*/
public void writeFragment (Fragment fragment) {
if ((fragment.getX () >= 0) && (fragment.getX () < width) && (fragment.getY () >= 0) && (fragment.getY () < height)) {
/* COMPLETER */
}
}
}

136
Fragment.java Normal file
View file

@ -0,0 +1,136 @@
import java.awt.*;
import algebra.*;
/**
* The Fragment class represents an attributed 'pixel' as generated
* by a Rasterizer.
* @author cdehais
*/
public class Fragment {
private int x;
private int y;
private int numAttributes;
/* attributes placement:
* 0: depth
* 1-3: color
* 4-6: normal
* 7-8: (u,v) texture coordinates
*/
double[] attributes;
public Fragment (int x, int y) { // int numAdditionalAttributes) {
this.x = x;
this.y = y;
numAttributes = 9;
attributes = new double[numAttributes];
}
public int getNumAttributes () {
return numAttributes;
}
/**
* Gets a scalar attribute at index.
*/
public double getAttribute (int index) {
return attributes[index];
}
/**
* Gets a vector attribute at the given starting location and with the given dimension.
*/
public double[] getAttribute (int index, int dimension) {
double[] attr = new double[dimension];
for (int i = 0; i < dimension; i++) {
attr[i] = attributes[index+i];
}
return attr;
}
public void setAttribute (int index, double value) {
attributes[index] = value;
}
/**
* Gets the x pixel coordinate of the Fragment
*/
public int getX () {
return x;
}
/**
* Gets the y pixel coordinate of the Fragment
*/
public int getY () {
return y;
}
/**
* Gets the pixel coordinates (x, y) of the Fragment as a size 2 array
*/
public int[] getPosition () {
int[] position = new int[2];
position[0] = x;
position[1] = y;
return position;
}
public void setPosition (int x, int y) {
this.x = x;
this.y = y;
}
public double getDepth () {
return attributes[0];
}
public void setDepth (double z) {
attributes[0] = z;
}
public Vector3 getNormal () {
return new Vector3 (attributes[0], attributes[1], attributes[2]);
}
public void setNormal (Vector normal) {
attributes[4] = normal.get (0);
attributes[5] = normal.get (1);
attributes[6] = normal.get (2);
}
public void setNormal (double nx, double ny, double nz) {
attributes[4] = nx;
attributes[5] = ny;
attributes[6] = nz;
}
public Color getColor () {
int r = (int) Math.min (255, Math.max (255 * attributes[1], 0));
int g = (int) Math.min (255, Math.max (255 * attributes[2], 0));
int b = (int) Math.min (255, Math.max (255 * attributes[3], 0));
return new Color (r, g, b);
}
public void setColor (Color color) {
attributes[1] = color.getRed ();
attributes[2] = color.getGreen ();
attributes[3] = color.getBlue ();
}
public void setColor (double r, double g, double b) {
attributes[1] = r;
attributes[2] = g;
attributes[3] = b;
}
public String toString () {
return "(" + x + "," + y + ")";
}
}

217
GraphicsWrapper.java Normal file
View file

@ -0,0 +1,217 @@
/**
* A "virtual" screen, where only "setPixel" is available
* (It is a JFrame, and JFrame.EXIT_ON_CLOSE is set)
* @author smondet
*/
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;
import java.lang.Math;
class ImageComponent extends Component {
BufferedImage renderedImage = null;
public ImageComponent (BufferedImage init) {
renderedImage = init;
}
public BufferedImage swapImage (BufferedImage bi) {
BufferedImage ret = renderedImage ;
renderedImage = bi ;
return ret ;
}
public void paint(Graphics g) {
if (renderedImage != null) {
((Graphics2D)g).drawImage(renderedImage, new AffineTransform(1f,0f,0f,1f,0,0), null);
}
}
}
public class GraphicsWrapper {
private int height = 0;
private int width = 0;
private int pixelSize = 0;
private JFrame myFrame ;
private ImageComponent drawComp = null;
private BufferedImage backBuffer = null ;
private BufferedImage frontBuffer = null ;
private void init() {
backBuffer = new BufferedImage (width * pixelSize, height * pixelSize, BufferedImage.TYPE_INT_ARGB) ;
frontBuffer = new BufferedImage (width * pixelSize, height * pixelSize, BufferedImage.TYPE_3BYTE_BGR) ;
/*
Graphics2D gd = initial.createGraphics ();
gd.setColor (Color.BLACK) ;
gd.fillRect (0,0, width * pixelSize, height * pixelSize) ;
gd = drawingImage.createGraphics ();
gd.setColor (Color.BLACK) ;
gd.fillRect (0,0, width * pixelSize, height * pixelSize) ;
*/
drawComp = new ImageComponent (frontBuffer);
drawComp.setPreferredSize (new Dimension (width * pixelSize, height * pixelSize)) ;
drawComp.setVisible (true);
myFrame = new JFrame("Simple Inverse Rasterization Renderer (TSI)");
myFrame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE) ;
myFrame.add("Center", drawComp );
myFrame.pack();
myFrame.setVisible(true);
}
/**
* Build a virtual screen of size width x height
* And set its window visible.
*/
public GraphicsWrapper(int width, int height) {
this.height = height;
this.width = width;
this.pixelSize = 1;
init();
}
/**
* Build a virtual screen of size width x height, where one virtual pixel is represented by
* a pixelSize x pixelSize square.
* And set its window visible.
*/
public GraphicsWrapper(int width, int height, int pixelSize) {
this.height = height;
this.width = width;
this.pixelSize = pixelSize ;
init();
}
/**
* Lights the pixel (x,y) with color (r, g, b) (values clamped to [0,1])
* on the current draw buffer.
* Does nothing for pixels out of the screen.
*/
public void setPixel (int x, int y, double r, double g, double b) {
r = Math.min (1.0, Math.max (0.0, r));
g = Math.min (1.0, Math.max (0.0, g));
b = Math.min (1.0, Math.max (0.0, b));
setPixel(x, y, (char) (r * 255), (char) (g * 255), (char) (b * 255));
}
/**
* Lights the pixel (x,y) with color (r, g, b) (values clamped to [0, 255])
* on the current draw buffer.
* Does nothing for pixels out of the screen.
*/
public void setPixel(int x, int y, char r, char g, char b) {
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
int argb = 0xFF000000 ;
argb += ((int)r) << (8 * 2) ;
argb += ((int)g) << (8 * 1) ;
argb += ((int)b);
for (int i = 0 ; i < pixelSize ; i++ ) {
for (int j = 0 ; j < pixelSize ; j++ ) {
backBuffer.setRGB(i + (x * pixelSize) , j + (y * pixelSize), argb) ;
}
}
}
}
/**
* Lights the pixel (x,y) with the given color.
* Does nothing for pixels out of the screen.
*/
public void setPixel(int x, int y, Color color) {
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
int rgb = color.getRGB ();
for (int i = 0 ; i < pixelSize ; i++ ) {
for (int j = 0 ; j < pixelSize ; j++ ) {
backBuffer.setRGB(i + (x * pixelSize) , j + (y * pixelSize), rgb);
}
}
}
}
/**
* Gets the pixel in the back buffer
*/
public Color getPixel (int x, int y) {
Color color;
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
color = new Color (backBuffer.getRGB (x, y), false);
} else {
color = Color.BLACK;
}
return color;
}
public Color getFrontPixel (int x, int y) {
Color color;
if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
color = new Color (frontBuffer.getRGB (x, y), false);
} else {
color = Color.BLACK;
}
return color;
}
/**
*
*/
int getWidth () {
return width;
}
int getHeight () {
return height;
}
/**
* Clear current draw-buffer (ie Paint it black)
*
*/
public void clearBuffer() {
Graphics2D gd = backBuffer.createGraphics ();
gd.setColor (Color.BLACK) ;
gd.fillRect (0,0, width * pixelSize, height * pixelSize) ;
}
/**
* Draw current draw-buffer on the window.
*
*/
public void swapBuffers() {
frontBuffer = drawComp.swapImage (backBuffer) ;
myFrame.repaint ();
}
/**
* Destroy window.
*/
public void destroy() {
myFrame.dispose();
}
}

76
Lighting.java Normal file
View file

@ -0,0 +1,76 @@
import java.lang.*; import java.util.*; import algebra.*;
/* * The Lighting class describes a scene lighting environment
* @author: gmorin, smondet */
public class Lighting {
static final int NONE = 0 ; static final int AMBIENT = 1 ;
static final int POINT = 2 ;
List lights ;
/* Internal Class describing a light source */
private class Light {
public int type = NONE;
public double[] params;
public Light (int type, double []params) {
this.type = type; this.params = params;
}
}
public Lighting() {
lights = new LinkedList(); }
/* Adds a new ambient light source of intensity @ia to the environment. */
public void addAmbientLight (double ia) {
double [] v = new double[1];
v[0] = ia; lights.add (new Light (AMBIENT, v)); }
/** Addsa */
public void addPointLight (double x, double y, double z, double id)
{
double[] v = new double[5];
v[0] = x; v[1] = y; v[2] = z; v[3] = id;
lights.add (new Light (POINT, v)); }
/* Computes the illuminated color of a 3D points of given position, normal and color,
* and given the camera position and material parameters.
* Returns an array of size 3. */
public double[] applyLights (Vector3 position, Vector3 normal, double[] color,
Vector3 cameraPosition,
double ka, double kd, double ks, double s)
{
double[] litColor = new double[3];
/* total light intensity */
double I = 0.0;
Iterator it = lights.iterator ();
while (it.hasNext ()){
Light light = (Light) it.next ();
switch (light.type) {
case AMBIENT:
/* Ambient light : A COMPLETER */
/* I += ... */
break;
case POINT:
try {
/* vector from point to camera center */
Vector3 e = new Vector3 (cameraPosition);
e.subtract (position);
e.normalize ();
/* vector from point to light*/
Vector3 l = new Vector3 (light.params[0], light.params[1], light.params[2]);
l.subtract (position);
l.normalize ();
/* half-vector between e and l*/
Vector3 h = new Vector3 (e);
h.add (l);
h.normalize ();
/* diffuse contribution : A COMPLETER */
/* double Id = ... */
/* specular contribution : A COMPLETER */
/* double Is = ... */
/* I += Id + Is;*/
} catch (InstantiationException ex) { /* should not reach*/ }
catch (SizeMismatchException ex) { /* should not reach*/ }
break;
default:
/* ignore unknow lights */
break;
}
}
litColor[0] = I * color[0]; litColor[1] = I * color[1]; litColor[2] = I * color[2];
return litColor;
}
}

196
Mesh.java Normal file
View file

@ -0,0 +1,196 @@
import java.io.* ;
import algebra.*;
/**
* Defines a triangle based mesh.
* A mesh is constructed by interpretting the data given in an OFF file.
* @author smondet gg cdehais
*/
public class Mesh {
private Vector[] vertices;
private int[] faces;
private double[] colors;
private Vector3[] normals;
private double[] texCoords;
private static String nextLine (BufferedReader in) throws Exception {
String r = in.readLine ();
while (r.matches ("\\s*#.*")) {
r = in.readLine ();
}
return r;
}
/**
* Builds a Mesh object by reading in an OFF file.
* Does not support non triangular meshes.
* @filename path to OFF file.
*/
public Mesh (String filename) throws Exception {
BufferedReader in = new BufferedReader (new FileReader (filename));
String r = nextLine (in);
if (!r.equals ("OFF")) {
throw new IOException ("Invalid OFF file !");
}
r = nextLine(in);
String [] sar = r.split("\\s+");
/* Parse object properties */
int verts_nb = new Integer (sar[0]).intValue();
int faces_nb = new Integer (sar[1]).intValue();
int edges_nb = new Integer (sar[2]).intValue();
/* Parse vertices and attributes */
vertices = new Vector[verts_nb];
faces = new int[3*faces_nb];
colors = new double[3*verts_nb];
for (int i = 0; i < verts_nb; i++ ){
r = nextLine(in);
sar = r.split ("\\s+");
vertices[i] = new Vector ("v" + i, 4);
vertices[i].set (0, new Double (sar[0]).doubleValue());
vertices[i].set (1, new Double (sar[1]).doubleValue());
vertices[i].set (2, new Double (sar[2]).doubleValue());
vertices[i].set (3, 1.0);
colors[3 * i + 0] = new Double(sar[3]).doubleValue();
colors[3 * i + 1] = new Double(sar[4]).doubleValue();
colors[3 * i + 2] = new Double(sar[5]).doubleValue();
/* optionnal texture coordinates */
if (sar.length >= 8) {
if (texCoords == null) {
texCoords = new double[2*verts_nb];
}
texCoords[2*i] = new Double(sar[6]).doubleValue();
texCoords[2*i+1] = new Double(sar[7]).doubleValue();
}
}
/* Parse faces */
for (int i = 0; i < faces_nb; i++) {
r = nextLine(in);
sar = r.split("\\s+");
int en = new Integer(sar[0]).intValue();
if (en != 3) {
throw new IOException("Non-triangular meshes not supported.");
}
faces[3 * i + 0] = new Integer(sar[1]).intValue();
faces[3 * i + 1] = new Integer(sar[2]).intValue();
faces[3 * i + 2] = new Integer(sar[3]).intValue();
}
in.close();
}
/**
* Gets the number of vertices in the mesh
*/
public int getNumVertices () {
return vertices.length;
}
/**
* Gets the number of faces in the mesh
*/
public int getNumFaces () {
return faces.length / 3;
}
/**
* Constructs a normal for each vertex of the mesh
*/
private Vector3[] computeNormals () {
normals = new Vector3[vertices.length];
// Compute per face normals and set the vertex normal to the average normals across faces
// to the vertex.
try {
for (int i = 0; i < 3 * getNumFaces(); i += 3) {
/* A COMPLETER */
Vector3 n = new Vector3(); //?
// ajoute la normale calculee a chq sommet de la face
for (int j = 0; j < 3; j++) {
Vector nj = normals[faces[i+j]];
if (nj == null) {
normals[faces[i+j]] = new Vector3 (n);
normals[faces[i+j]].setName ("n" + faces[i+j]);
} else {
nj.add (n);
}
}
}
} catch (InstantiationException e) { System.out.println ("Should not reach 1"); }
catch (SizeMismatchException e) { System.out.println ("Should not reach 2"); }
// final round of normalization
for (int i = 0; i < normals.length; i++) {
if (normals[i] == null) { /* deals with orphans vertices */
normals[i] = new Vector3 ("n_orphan");
} else {
normals[i].normalize();
}
}
return normals;
}
/**
* Returns the vertices of the mesh
*/
public Vector[] getVertices () {
return vertices;
}
/**
* Return the normals associated to the vertices.
*/
public Vector3[] getNormals () {
if (normals == null) {
normals = computeNormals ();
}
return normals;
}
/**
* Returns the faces of the mesh. The returned array contains 3*n integers, with n the number of faces.
* Each integer is an index into the array of Vector.
*/
public int[] getFaces () {
return faces;
}
/**
* Returns the colors of each vertex in the mesh.
*/
public double[] getColors () {
return colors;
}
/**
* Returns the texture coordinates of each vertex in the mesh.
*/
public double[] getTextureCoordinates () {
return texCoords;
}
}

30
PainterShader.java Normal file
View file

@ -0,0 +1,30 @@
import algebra.*;
import java.awt.*;
/**
* Simple shader that just copy the interpolated color to the screen,
* taking the depth of the fragment into acount.
* @author: cdehais
*/
public class PainterShader extends Shader {
DepthBuffer depth;
public PainterShader (GraphicsWrapper screen) {
super (screen);
depth = new DepthBuffer (screen.getWidth (), screen.getHeight ());
}
public void shade (Fragment fragment) {
if (depth.testFragment (fragment)) {
screen.setPixel (fragment.getX (), fragment.getY (), fragment.getColor ());
depth.writeFragment (fragment);
}
}
public void reset () {
depth.clear ();
}
}

View file

@ -0,0 +1,62 @@
import algebra.*;
import java.lang.Math.*;
/**
* The PerspectiveCorrectRasterizer class extends Rasterizer to perform Persepctive Correct interpolation
* of attributes.
*
* @author cdehais
*/
public class PerspectiveCorrectRasterizer extends Rasterizer {
public PerspectiveCorrectRasterizer (Shader shader) {
super(shader);
}
/**
* Rasterizes the triangular face made of the Fragment v1, v2 and v3
*/
public void rasterizeFace (Fragment v1, Fragment v2, Fragment v3) {
Matrix C = makeBarycentricCoordsMatrix (v1, v2, v3);
/* iterate over the triangle's bounding box */
int xmin = Math.min (v1.getX (), Math.min (v2.getX (), v3.getX ()));
int ymin = Math.min (v1.getY (), Math.min (v2.getY (), v3.getY ()));
int xmax = Math.max (v1.getX (), Math.max (v2.getX (), v3.getX ()));
int ymax = Math.max (v1.getY (), Math.max (v2.getY (), v3.getY ()));
Fragment fragment = new Fragment (0, 0);
int numAttributes = fragment.getNumAttributes ();
try {
for (int x = xmin; x <= xmax; x++) {
for (int y = ymin; y <= ymax; y++) {
/* setup position now to allow early clipping */
fragment.setPosition (x, y);
if (!shader.isClipped (fragment)) {
Vector3 v = new Vector3 (1.0, (double)x, (double)y);
Vector bar = C.multiply (v);
if ((bar.get (0) >= 0.0) && (bar.get (1) >= 0.0) && (bar.get (2) >= 0.0)) {
double oneOverZ = bar.get (0) / v1.getDepth () +
bar.get (1) / v2.getDepth () +
bar.get (2) / v3.getDepth ();
for (int i = 0; i < numAttributes; i++) {
double aOverZ = bar.get (0) * v1.getAttribute (i) / v1.getDepth () +
bar.get (1) * v2.getAttribute (i) / v2.getDepth () +
bar.get (2) * v3.getAttribute (i) / v3.getDepth ();
fragment.setAttribute (i, aOverZ / oneOverZ);
}
shader.shade (fragment);
}
}
}
}
} catch (SizeMismatchException e) {
/* should not reach */
e.printStackTrace ();
}
}
}

194
Rasterizer.java Normal file
View file

@ -0,0 +1,194 @@
import algebra.*;
import java.lang.Math.*;
/**
* The Rasterizer class is responsible for the discretization of geometric
* primitives
* (edges and faces) over the screen pixel grid and generates Fragment (pixels
* with
* interpolated attributes). Those Fragment are then passed to a Shader object,
* which will produce the final color of the fragment.
*
* @author morin, chambon, cdehais
*/
public class Rasterizer {
Shader shader;
public Rasterizer(Shader shader) {
this.shader = shader;
}
public void setShader(Shader shader) {
this.shader = shader;
}
/**
* Linear interpolation of a Fragment f on the edge defined by Fragment's v1 and
* v2
*/
private void interpolate2(Fragment v1, Fragment v2, Fragment f) {
int x1 = v1.getX();
int y1 = v1.getY();
int x2 = v2.getX();
int y2 = v2.getY();
int x = f.getX();
int y = f.getX();
double alpha;
if (Math.abs(x2 - x1) > Math.abs(y2 - y1)) {
alpha = (double) (x - x1) / (double) (x2 - x1);
} else {
if (y2 != y1) {
alpha = (double) (y - y1) / (double) (y2 - y1);
} else {
alpha = 0.5;
}
}
int numAttributes = f.getNumAttributes();
for (int i = 0; i < numAttributes; i++) {
f.setAttribute(i, (1.0 - alpha) * v1.getAttribute(i) + alpha * v2.getAttribute(i));
}
}
/*
* Swaps x and y coordinates of the fragment. Used by the Bresenham algorithm.
*/
private static void swapXAndY(Fragment f) {
f.setPosition(f.getY(), f.getX());
}
/**
* Rasterizes the edge between the projected vectors v1 and v2.
* Generates Fragment's and calls the Shader::shade() metho on each of them.
*/
public void rasterizeEdge(Fragment v1, Fragment v2) {
/* Coordinates of V1 and V2 */
int x1 = v1.getX();
int y1 = v1.getY();
int x2 = v2.getX();
int y2 = v2.getY();
/* For now : just display the vertices */
Fragment f = new Fragment(0, 0);
int size = 2;
for (int i = 0; i < v1.getNumAttributes(); i++) {
f.setAttribute(i, v1.getAttribute(i));
}
for (int i = -size; i <= size; i++) {
for (int j = -size; j <= size; j++) {
f.setPosition(x1 + i, y1 + j);
shader.shade(f);
}
}
/*
* tracé d'un segment avec l'algo de Bresenham
* int numAttributes = v1.getNumAttributes ();
* Fragment fragment = new Fragment (0, 0); //, numAttributes);
*
* boolean sym = (Math.abs (y2 - y1) > Math.abs (x2 - x1));
* if (sym) {
* int temp;
* temp = x1; x1 = y1 ; y1 = temp;
* temp = x2; x2 = y2 ; y2 = temp;
* }
* if (x1 > x2) {
* Fragment ftemp;
* int temp;
* temp = x1; x1 = x2; x2 = temp;
* temp = y1; y1 = y2; y2 = temp;
* ftemp = v1; v1 = v2; v2 = ftemp;
* }
*
* int ystep;
* if (y1 < y2) {
* ystep = 1;
* } else {
* ystep = -1;
* }
*
* int x = x1;
* float y_courant = y1;
* int y=y1;
* float delta_y = y2-y1;
* float delta_x = x2-x1;
* float m = delta_y/delta_x;
*
*
* for (int i=1;i<=delta_x;i++) {
* x = x+1;
* y_courant = y_courant + m;
* if ((ystep == 1)&&(y_courant < y+0.5)||((ystep == -1) && (y_courant > y
* -0.5))) {
* y = y;
* } else {
* y = y + ystep;
* }
*
* //envoi du fragment au shader
* fragment.setPosition (x, y);
*
* if (!shader.isClipped (fragment)) {
*
* //interpolation des attributs
* interpolate2 (v1, v2, fragment);
* if (sym) {
* swapXAndY (fragment);
* }
* shader.shade (fragment);
* }
* }
*/
}
static double triangleArea(Fragment v1, Fragment v2, Fragment v3) {
return (double) v2.getX() * v3.getY() - v2.getY() * v3.getX()
+ v3.getX() * v1.getY() - v1.getX() * v3.getY()
+ v1.getX() * v2.getY() - v2.getX() * v1.getY();
}
static protected Matrix makeBarycentricCoordsMatrix(Fragment v1, Fragment v2, Fragment v3) {
Matrix C = null;
try {
C = new Matrix(3, 3);
} catch (InstantiationException e) {
/* unreached */
}
double area = triangleArea(v1, v2, v3);
int x1 = v1.getX();
int y1 = v1.getY();
int x2 = v2.getX();
int y2 = v2.getY();
int x3 = v3.getX();
int y3 = v3.getY();
C.set(0, 0, (x2 * y3 - x3 * y2) / area);
C.set(0, 1, (y2 - y3) / area);
C.set(0, 2, (x3 - x2) / area);
C.set(1, 0, (x3 * y1 - x1 * y3) / area);
C.set(1, 1, (y3 - y1) / area);
C.set(1, 2, (x1 - x3) / area);
C.set(2, 0, (x1 * y2 - x2 * y1) / area);
C.set(2, 1, (y1 - y2) / area);
C.set(2, 2, (x2 - x1) / area);
return C;
}
/**
* Rasterizes the triangular face made of the Fragment v1, v2 and v3
*/
public void rasterizeFace(Fragment v1, Fragment v2, Fragment v3) {
Matrix C = makeBarycentricCoordsMatrix(v1, v2, v3);
/* iterate over the triangle's bounding box */
/* A COMPLETER */
}
}

186
Renderer.java Normal file
View file

@ -0,0 +1,186 @@
import algebra.*;
/**
* The Renderer class drives the rendering pipeline: read in a scene, projects
* the vertices and rasterizes every faces / edges.
*
* @author: cdehais
*/
public class Renderer {
static Scene scene;
static Mesh mesh;
static Rasterizer rasterizer;
static GraphicsWrapper screen;
static Shader shader;
static Transformation xform;
static Lighting lighting;
static boolean lightingEnabled;
static void init(String sceneFilename) throws Exception {
scene = new Scene(sceneFilename);
mesh = new Mesh(scene.getMeshFileName());
screen = new GraphicsWrapper(scene.getScreenW(), scene.getScreenH());
screen.clearBuffer();
shader = new SimpleShader(screen);
// shader = new PainterShader (screen);
// rasterizer = new PerspectiveCorrectRasterizer (shader);
rasterizer = new Rasterizer(shader);
xform = new Transformation();
xform.setLookAt(scene.getCameraPosition(),
scene.getCameraLookAt(),
scene.getCameraUp());
xform.setProjection();
xform.setCalibration(scene.getCameraFocal(), scene.getScreenW(), scene.getScreenH());
lighting = new Lighting();
lighting.addAmbientLight(scene.getAmbientI());
double[] lightCoord = scene.getSourceCoord();
lighting.addPointLight(lightCoord[0], lightCoord[1], lightCoord[2], scene.getSourceI());
}
static Fragment[] projectVertices() {
Vector[] vertices = mesh.getVertices();
Vector3[] normals = mesh.getNormals();
double[] colors = mesh.getColors();
Fragment[] fragments = new Fragment[vertices.length];
try {
for (int i = 0; i < vertices.length; i++) {
Vector pVertex = xform.projectPoint(vertices[i]);
// Vector pNormal = xform.transformVector (normals[i]);
Vector3 pNormal = normals[i];
int x = (int) Math.round(pVertex.get(0));
int y = (int) Math.round(pVertex.get(1));
fragments[i] = new Fragment(x, y);
fragments[i].setDepth(pVertex.get(2));
fragments[i].setNormal(pNormal);
double[] texCoords = mesh.getTextureCoordinates();
if (texCoords != null) {
fragments[i].setAttribute(7, texCoords[2 * i]);
fragments[i].setAttribute(8, texCoords[2 * i + 1]);
}
if (!lightingEnabled) {
fragments[i].setColor(colors[3 * i], colors[3 * i + 1], colors[3 * i + 2]);
} else {
double[] color = new double[3];
color[0] = colors[3 * i];
color[1] = colors[3 * i + 1];
color[2] = colors[3 * i + 2];
double material[] = scene.getMaterial();
double[] litColor = lighting.applyLights(new Vector3(vertices[i]), pNormal, color,
scene.getCameraPosition(),
material[0], material[1], material[2], material[3]);
fragments[i].setColor(litColor[0], litColor[1], litColor[2]);
}
}
} catch (SizeMismatchException e) {
e.printStackTrace();
/* should not reach */
} catch (InstantiationException e) {
e.printStackTrace();
/* should not reach */
}
return fragments;
}
static void renderWireframe() {
Fragment[] fragment = projectVertices();
int[] faces = mesh.getFaces();
for (int i = 0; i < 3 * mesh.getNumFaces(); i += 3) {
for (int j = 0; j < 3; j++) {
Fragment v1 = fragment[faces[i + j]];
Fragment v2 = fragment[faces[i + ((j + 1) % 3)]];
rasterizer.rasterizeEdge(v1, v2);
}
}
}
static void renderSolid() {
Fragment[] fragments = projectVertices();
int[] faces = mesh.getFaces();
for (int i = 0; i < 3 * mesh.getNumFaces(); i += 3) {
Fragment v1 = fragments[faces[i]];
Fragment v2 = fragments[faces[i + 1]];
Fragment v3 = fragments[faces[i + 2]];
rasterizer.rasterizeFace(v1, v2, v3);
}
}
public static void setLightingEnabled(boolean enabled) {
lightingEnabled = enabled;
}
public static void wait(int sec) {
try {
Thread.sleep(sec * 1000);
} catch (Exception e) {
/* nothing */
}
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("usage: java Renderer <scene_file>");
} else {
try {
init(args[0]);
} catch (Exception e) {
System.out.println("Problem initializing Renderer: " + e);
e.printStackTrace();
return;
}
}
/* wireframe rendering */
renderWireframe();
screen.swapBuffers();
wait(10);
/* solid rendering, no lighting */
/*
* screen.clearBuffer ();
* shader.reset ();
* renderSolid ();
* screen.swapBuffers ();
* wait (3);
*/
/* solid rendering, with lighting */
/*
* screen.clearBuffer ();
* shader.reset ();
* setLightingEnabled (true);
* renderSolid ();
* screen.swapBuffers ();
* wait (3);
*/
/* solid rendering, with texture */
/*
* screen.clearBuffer ();
* TextureShader texShader = new TextureShader (screen);
* texShader.setTexture ("data/brick.jpg");
* shader = texShader;
* rasterizer.setShader (texShader);
* setLightingEnabled (true);
* renderSolid ();
* screen.swapBuffers ();
* wait (3);
*/
screen.destroy();
System.exit(0);
}
}

95
Scene.java Normal file
View file

@ -0,0 +1,95 @@
import java.io.*;
import algebra.*;
/**
* Class that describes a simple 3D Scene:
* This description is meant to be read form a scene description file (.scene extension)
*
* @author: cdehais based on smondet, gmorin
*/
public class Scene {
private static String nextLine (BufferedReader in) throws Exception {
String r = in.readLine();
while (r.matches("(\\s*#.*)|(\\s*$)")) {
r = in.readLine() ;
}
return r;
}
String meshFilename;
Vector3 cameraPosition = new Vector3 ("cam_pos");
Vector3 cameraLookAt = new Vector3 ("cam_lookat");
Vector3 cameraUp = new Vector3 ("cam_up");
double cameraFocal;
int screenW;
int screenH;
double ambientI;
double sourceI;
double[] sourceCoord = new double[3] ;
double[] material = new double[4] ;
public Scene (String filename) throws Exception {
BufferedReader in = new BufferedReader(new FileReader(filename));
meshFilename = nextLine(in);
String r = nextLine(in);
String [] sar = r.split("\\s+");
cameraPosition.set (new Double(sar[0]).doubleValue(),
new Double(sar[1]).doubleValue(),
new Double(sar[2]).doubleValue());
r = nextLine(in);
sar = r.split("\\s+");
cameraLookAt.set (new Double(sar[0]).doubleValue(),
new Double(sar[1]).doubleValue(),
new Double(sar[2]).doubleValue());
r = nextLine(in);
sar = r.split("\\s+");
cameraUp.set (new Double(sar[0]).doubleValue(),
new Double(sar[1]).doubleValue(),
new Double(sar[2]).doubleValue());
r = nextLine(in);
cameraFocal = new Double(r).doubleValue();
r = nextLine(in);
sar = r.split("\\s+");
screenW = new Integer(sar[0]).intValue();
screenH = new Integer(sar[1]).intValue();
r = nextLine(in);
ambientI = new Double(r).doubleValue();
r = nextLine(in);
sar = r.split ("\\s+");
for (int i = 0; i < sourceCoord.length; i++) {
sourceCoord[i] = new Double(sar[i]).doubleValue();
}
sourceI = new Double(sar[3]).doubleValue();
r = nextLine(in);
sar = r.split ("\\s+");
for (int i = 0; i < material.length; i++) {
material[i] = new Double(sar[i]).doubleValue();
}
}
public String getMeshFileName () { return meshFilename ; }
public Vector3 getCameraPosition () { return cameraPosition ; }
public Vector3 getCameraLookAt () { return cameraLookAt ; }
public Vector3 getCameraUp () { return cameraUp ; }
public double getCameraFocal () { return cameraFocal ; }
public int getScreenW () { return screenW ; }
public int getScreenH () { return screenH ; }
public double getAmbientI () { return ambientI ; }
public double getSourceI () { return sourceI ; }
public double[] getSourceCoord () { return sourceCoord ; }
public double[] getMaterial () { return material ; }
}

35
Shader.java Normal file
View file

@ -0,0 +1,35 @@
/**
* The Shader class is responsible for writing final pixel color
* to the screen (GraphicWrapper), from a Fragment.
* Subclass this base class and implement the ::shade() method.
* @author cdehais
*/
public abstract class Shader {
protected GraphicsWrapper screen;
public Shader (GraphicsWrapper screen) {
this.screen = screen;
}
/**
* Common entry point to ree-initialize the shader
*/
public void reset () { }
/**
* Computes the fragment color and write the result to the screen.
*/
public abstract void shade (Fragment fragment);
/**
* Test whether the fragment falls onto the screen.
*/
public boolean isClipped (Fragment fragment) {
return ((fragment.getX () < 0) || (fragment.getX () >= screen.getWidth ()) ||
(fragment.getY () < 0) || (fragment.getY () >= screen.getHeight ()));
}
}

21
SimpleShader.java Normal file
View file

@ -0,0 +1,21 @@
import algebra.*;
import java.awt.*;
/**
* Simple shader that just copy the interpolated color to the screen.
* @author: cdehais
*/
public class SimpleShader extends Shader {
public SimpleShader (GraphicsWrapper screen) {
super (screen);
}
public void shade (Fragment fragment) {
//System.out.println (fragment.getX () + "," + fragment.getY ());
//System.out.println ("color " + fragment.getColor ());
screen.setPixel (fragment.getX (), fragment.getY (), fragment.getColor ());
}
}

130
TestAlgebra.java Normal file
View file

@ -0,0 +1,130 @@
import algebra.*;
public class TestAlgebra {
public static void test() throws Exception {
System.out.println ("Algebra\n# Test Start");
Vector v = new Vector (3);
Vector v1 = new Vector (1);
v1.setName ("up");
try {
Vector v0 = new Vector (0);
} catch (Exception e) {
System.out.println ("Wrong size exception caught OK");
}
try {
Vector v0 = new Vector ("named", 0);
} catch (Exception e) {
System.out.println ("Wrong size exception caught OK");
}
System.out.println (v);
System.out.println (v1);
Vector u1 = new Vector ("u1", 3);
u1.set(0, 1.0);
u1.set(1, 2.0);
u1.set(2, 3.0);
Vector u2 = new Vector (3);
u2.set (new double[3]);
u2.set(1, 2.0);
System.out.println (u1);
System.out.println ("norm(u1) = " + u1.norm());
System.out.println (u2);
System.out.println ("u1.u2 = " + u1.dot (u2));
try {
u1 = new Vector ("u1", 3);
u2 = new Vector ("u2", 4);
u1.dot (u2);
} catch (SizeMismatchException e) {
System.out.println ("Caught exception: " + e);
}
Vector3 r1 = new Vector3 ("u1", 1.0, 2.0, 3.0);
Vector3 r2 = new Vector3 ("u2", 1.0, 3.0, 0.0);
Vector3 r = r1.cross (r2);
System.out.println (r1);
System.out.println (r2);
System.out.println ("r1.r2 = " + r1.dot (r2));
System.out.println ("r1.r2 = " + r1.dot ((Vector) r2) + " (as Vector)");
System.out.println ("r1 x r2 = " + r);
System.out.println ("norm(" + r.getName() + ") = " + r.norm());
r.normalize ();
System.out.println ("norm(" + r.getName() + ") (after ::normalize()) = " + r.norm());
//-------------------------------------
System.out.println ("--\n-- Matrix tests\n--");
Matrix M = new Matrix (3,5);
System.out.println (M);
Matrix M1 = new Matrix ("M1", 2, 5);
M1.set (0, 0, 2.0);
M1.set (1, 4, -8.0);
M1.set (1, 2, 5.0);
try {
Matrix M2 = new Matrix ("M2", 0, 2);
} catch (Exception e) {
System.out.println ("Wrong size exception caught OK");
}
try {
M.multiply (M1);
} catch (SizeMismatchException e) {
System.out.println ("Caught exception: " + e);
}
Matrix M2 = Matrix.createIdentity (5);
System.out.println (M2);
M = M1.multiply (M2);
System.out.println (M1);
System.out.println (M2);
System.out.println (M);
M1 = M1.transpose();
M1.name = "M1'";
System.out.println (M1);
Vector u = new Vector ("u", 5);
u.ones();
v = M.multiply (u);
System.out.println (M);
System.out.println (u);
System.out.println ("M.u = " + v);
try {
u = new Vector ("u", 3);
v = M.multiply (u);
} catch (SizeMismatchException e) {
System.out.println ("Caught exception: " + e);
}
Matrix I = Matrix.createIdentity (5);
Matrix S = I.getSubMatrix (0, 2, 3, 3);
System.out.println (I);
System.out.println ("submatrix:\n" + S);
}
public static void main(String[] args) {
try {
test() ;
System.out.println ("SUCCESS.") ;
} catch (Exception e) {
System.out.println ("FAIL: " + e) ;
e.printStackTrace () ;
}
}
}

141
TestGraphicWrapper.java Normal file
View file

@ -0,0 +1,141 @@
import java.util.*;
import java.awt.*;
import java.lang.Math.*;
public class TestGraphicWrapper {
static int width = 128;
static int height = 128;
static GraphicsWrapper screen;
private static void checker (int polarity, double r, double g, double b) {
screen.clearBuffer();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (((i / 8) % 2 == 1) == ((j / 8) % 2 == polarity)) {
screen.setPixel (j, i, r, g, b);
}
}
}
}
static void init () {
screen = new GraphicsWrapper (width, height, 1);
}
static int countNeighbours (int x0, int y0) {
int count = 0;
for (int y = y0 - 1; y <= y0 + 1; y++) {
for (int x = x0 - 1; x <= x0 + 1; x++) {
if ((x != x0) || (y != y0)) {
int _x = x % width;
int _y = y % height;
Color pix = screen.getFrontPixel (_x, _y);
if (pix.getRed () != 0) {
count++;
}
}
}
}
return count;
}
static void evolve () {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int c = countNeighbours (x, y);
Color pix = screen.getFrontPixel (x, y);
Color bpix = screen.getPixel (x, y);
if (pix.getRed () == 0) {
//System.out.println (x + " " + y + " : dead (" + c + " nbrs) "
// + ((bpix.getRed () == 0) ? "dead" : "alive")
// );
if (c == 3) {
/* born */
screen.setPixel (x, y, 255, 255, 255);
} else {
screen.setPixel (x, y, 0, 0, 0);
}
} else {
//System.out.println (x + " " + y + " : alive (" + c + " nbrs) "
// + ((bpix.getRed() == 0) ? "dead" : "alive"));
if ((c >= 2) && (c <= 3)) {
/* survive */
screen.setPixel (x, y, 255, 255, 255);
} else {
/* die */
screen.setPixel (x, y, 0, 0, 0);
}
}
}
}
}
public static void testChecker () throws Exception {
for (int k = 0; k < 10; k++) {
checker (1, 1.0, 1.0, 1.0);
screen.swapBuffers ();
Thread.sleep (100);
checker (0, 1.0, 1.0, 1.0);
screen.swapBuffers ();
Thread.sleep (100);
}
}
public static void testConway () throws Exception {
screen.clearBuffer ();
//screen.swapBuffers ();
//screen.clearBuffer ();
//screen.swapBuffers ();
/*
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (Math.random() < 0.3) {
screen.setPixel (x, y, 255, 255, 255);
}
}
}
*/
screen.setPixel (20, 10, 255, 255, 255);
screen.setPixel (21, 11, 255, 255, 255);
screen.setPixel (19, 12, 255, 255, 255);
screen.setPixel (20, 12, 255, 255, 255);
screen.setPixel (21, 12, 255, 255, 255);
screen.swapBuffers ();
Thread.sleep (1000);
for (int k = 0; k < 100; k++) {
evolve ();
screen.swapBuffers ();
Thread.sleep (30);
}
screen.destroy() ;
System.exit(0);
}
public static void main(String[] args) {
try {
init ();
//testChecker () ;
testConway () ;
} catch (Exception e) {
System.out.println("EXCEPTION: " + e) ;
e.printStackTrace() ;
}
}
}

64
TestMesh.java Normal file
View file

@ -0,0 +1,64 @@
import algebra.*;
/**
* Test class for the Mesh class
* @author: cdehais based on gmorin, smondet
*/
public class TestMesh {
public static void test() throws Exception {
System.out.println("OFF\n# Test Start");
Mesh mesh = new Mesh ("data/cube_multi_color.off") ;
// Transform rend = new Transform();
Vector[] vertices = mesh.getVertices ();
int[] faces = mesh.getFaces ();
double[] colors = mesh.getColors ();
Vector[] normals = mesh.getNormals();
System.out.println("** vertices + colors: ") ;
for (int i = 0 ; i < vertices.length; i++) {
System.out.println (vertices[i] + " - (" + colors[3 * i + 0] + ","
+ colors[3 * i + 1] + ","
+ colors[3 * i + 2] + ")");
}
System.out.println ("** faces: ") ;
for (int i = 0 ; i < mesh.getNumFaces (); i++) {
System.out.println (faces[3 * i] + " " + faces[3 * i + 1] + " " + faces[3 * i + 2]);
}
/* A decommenter lorsque les normales seront calculees */
/* System.out.println("** computed per vertex normals: ");
for (int i = 0 ; i < vertices.length; i++) {
System.out.println (normals[i]);
}*/
/*
System.out.println("vertices after change of basis: ") ;
for (int i = 0 ; i < vertices.length / 3 ; i++ ){
System.out.println(
vertices[ 3 * i + 0 ] + " " +
vertices[ 3 * i + 1 ] + " " +
vertices[ 3 * i + 2 ] + " " +
colors [ 3 * i + 0 ] + " " +
colors [ 3 * i + 1 ] + " " +
colors [ 3 * i + 2 ]);
}
*/
}
public static void main (String[] args) {
try {
test() ;
} catch (Exception e) {
System.out.println("EXCEPTION: " + e) ;
e.printStackTrace() ;
}
}
}

47
TestRasterizer.java Normal file
View file

@ -0,0 +1,47 @@
import algebra.*;
/**
* Test class for the Rasterizer class
* @author: cdehais
*/
public class TestRasterizer {
static class TestShader extends Shader {
public TestShader (GraphicsWrapper screen) {
super (screen);
}
public void shade (Fragment fragment) {
System.out.println (" fragment: (" + fragment.getX () + ", " + fragment.getY () + ")"
+ " - color = (" + fragment.getColor() + ")");
}
}
public static void test() throws Exception {
System.out.println("OFF\n# Test Start");
TestShader shader = new TestShader (new GraphicsWrapper (256, 256));
Rasterizer rasterizer = new Rasterizer (shader);
System.out.println ("Rasterizing edge");
Fragment v1 = new Fragment (0, 20);
v1.setColor (0, 0, 0);
Fragment v2 = new Fragment (5, -35);
v2.setColor (50, 100, 0);
rasterizer.rasterizeEdge (v1, v2);
}
public static void main (String[] args) {
try {
test() ;
} catch (Exception e) {
System.out.println("EXCEPTION: " + e) ;
e.printStackTrace() ;
}
}
}

36
Texture.java Normal file
View file

@ -0,0 +1,36 @@
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
/**
* 2D Texture class.
*/
public class Texture {
int width;
int height;
BufferedImage image;
/**
* Constructs a new Texture with the content of the image at @path.
*/
public Texture (String path) throws Exception {
image = ImageIO.read (new File (path));
width = image.getWidth ();
height = image.getHeight ();
}
/**
* Samples the texture at texture coordinates (u,v), using nearest neighboor interpolation
* u and v and wrapped around to [0,1].
*/
public Color sample (double u, double v) {
/* A COMPLETER */
return new Color (0,0,0);
}
}

58
TextureShader.java Normal file
View file

@ -0,0 +1,58 @@
import algebra.*;
import java.awt.*;
/**
* Simple shader that just copy the interpolated color to the screen,
* taking the depth of the fragment into acount.
* @author: cdehais
*/
public class TextureShader extends Shader {
DepthBuffer depth;
Texture texture;
boolean combineWithBaseColor;
public TextureShader (GraphicsWrapper screen) {
super (screen);
depth = new DepthBuffer (screen.getWidth (), screen.getHeight ());
texture = null;
}
public void setTexture (String path) {
try {
texture = new Texture (path);
} catch (Exception e) {
System.out.println ("Could not load texture " + path);
e.printStackTrace ();
texture = null;
}
}
public void setCombineWithBaseColor (boolean combineWithBaseColor) {
this.combineWithBaseColor = combineWithBaseColor;
}
public void shade (Fragment fragment) {
if (depth.testFragment (fragment)) {
/* The Fragment may not have texture coordinates */
try {
/* à compléter */
} catch (ArrayIndexOutOfBoundsException e) {
screen.setPixel (fragment.getX (), fragment.getY (), fragment.getColor ());
}
depth.writeFragment (fragment);
}
}
public void reset () {
depth.clear ();
}
}

133
Transformation.java Normal file
View file

@ -0,0 +1,133 @@
import algebra.*;
/**
* author: cdehais
*/
public class Transformation {
Matrix worldToCamera;
Matrix projection;
Matrix calibration;
public Transformation() {
try {
worldToCamera = new Matrix("W2C", 4, 4);
projection = new Matrix("P", 3, 4);
calibration = Matrix.createIdentity(3);
calibration.setName("K");
} catch (InstantiationException e) {
/* should not be reached */
}
}
public void setLookAt(Vector3 cam, Vector3 lookAt, Vector3 up) {
try {
// compute rotation
Vector3 e3c = new Vector3(lookAt);
e3c.subtract(cam);
e3c.normalize();
Vector3 e1c = up.cross(e3c);
e1c.normalize();
Vector3 e2c = e3c.cross(e1c);
// insertion de la matrice de rotation (transposée), dans worldToCamera
worldToCamera.set(0, 0, e1c.get(0));
worldToCamera.set(0, 1, e1c.get(1));
worldToCamera.set(0, 2, e1c.get(2));
worldToCamera.set(1, 0, e2c.get(0));
worldToCamera.set(1, 1, e2c.get(1));
worldToCamera.set(1, 2, e2c.get(2));
worldToCamera.set(2, 0, e3c.get(0));
worldToCamera.set(2, 1, e3c.get(1));
worldToCamera.set(2, 2, e3c.get(2));
// le vecteur de zeros sous la rotation, dans worldToCamera
worldToCamera.set(3, 0, 0);
worldToCamera.set(3, 1, 0);
worldToCamera.set(3, 2, 0);
// le 1 en bas à droite, dans worldToCamera
worldToCamera.set(3, 3, 1);
// compute translation
Matrix M = worldToCamera.getSubMatrix(0, 0, 3, 3);
Vector t = M.multiply(cam);
// insertion du vecteur translation, dans worldToCamera
worldToCamera.set(0, 3, -t.get(0));
worldToCamera.set(1, 3, -t.get(1));
worldToCamera.set(2, 3, -t.get(2));
} catch (Exception e) {
/* unreached */
}
System.out.println("Modelview matrix:\n" + worldToCamera);
}
public void setProjection() {
// matrice "identitée" 3x4
projection.set(0, 0, 1);
projection.set(1, 1, 1);
projection.set(2, 2, 1);
System.out.println("Projection matrix:\n" + projection);
}
public void setCalibration(double focal, double width, double height) {
// focal sur la diagonale, sauf en bas à droite
calibration.set(0, 0, focal);
calibration.set(1, 1, focal);
// translation de w/2 et h/2
calibration.set(0, 2, width / 2);
calibration.set(1, 2, height / 2);
System.out.println("Calibration matrix:\n" + calibration);
}
/**
* Projects the given homogeneous, 4 dimensional point onto the screen.
* The resulting Vector as its (x,y) coordinates in pixel, and its z coordinate
* is the depth of the point in the camera coordinate system.
*/
public Vector3 projectPoint(Vector p) throws SizeMismatchException, InstantiationException {
Vector ps = new Vector(3);
// projection du point dans le plan
ps = calibration.multiply(projection.multiply(worldToCamera.multiply(p)));
double depth = ps.get(2);
// "normalisation" des coordonnées du point
ps.set(0, ps.get(0) / depth);
ps.set(1, ps.get(1) / depth);
// ps.scale(1 / depth);
return new Vector3(ps);
}
/**
* Transform a vector from world to camera coordinates.
*/
public Vector3 transformVector(Vector3 v) throws SizeMismatchException, InstantiationException {
/* Doing nothing special here because there is no scaling */
Matrix R = worldToCamera.getSubMatrix(0, 0, 3, 3);
Vector tv = R.multiply(v);
return new Vector3(tv);
}
}

211
algebra/Matrix.java Normal file
View file

@ -0,0 +1,211 @@
/*
* @author: cdehais
*/
package algebra;
import java.lang.Math;
public class Matrix {
protected Matrix() {
}
protected Matrix(String name) {
this.name = name;
}
/**
* Creates a named Matrix of size nRows x nCols.
*/
public Matrix(String name, int nRows, int nCols) throws java.lang.InstantiationException {
this(nRows, nCols);
this.name = name;
}
/**
* Creates a Matrix of size nRows x nCols.
*/
public Matrix(int nRows, int nCols) throws java.lang.InstantiationException {
allocValues(nRows, nCols);
}
/**
* Creates an identity matrix of size @size
*/
public static Matrix createIdentity(int size) throws java.lang.InstantiationException {
Matrix id = new Matrix(size, size);
for (int i = 0; i < size; i++) {
id.values[size * i + i] = 1.0;
}
id.name = "I" + size;
return id;
}
/**
* Extracts a submatrix of size nRows x nCols with top left corner at
* (offsetRow, offsetCol)
*/
public Matrix getSubMatrix(int offsetRow, int offsetCol, int nRows, int nCols)
throws InstantiationException {
if ((offsetRow < 0) || (offsetCol < 0) || (nRows < 1) || (nCols < 1) ||
(offsetRow + nRows > this.nRows) || (offsetCol + nCols > this.nCols)) {
throw new InstantiationException("Invalid submatrix");
}
Matrix sub = new Matrix(nRows, nCols);
for (int i = 0; i < nRows; i++) {
for (int j = 0; j < nCols; j++) {
sub.set(i, j, this.get(offsetRow + i, offsetCol + j));
}
}
return sub;
}
/**
* Transposes the square Matrix.
*/
public Matrix transpose() {
Matrix trans;
try {
trans = new Matrix(this.nCols, this.nRows);
} catch (java.lang.InstantiationException e) {
/* unreached */
return null;
}
for (int i = 0; i < nCols; i++) {
for (int j = i + 1; j < nRows; j++) {
trans.values[i * nCols + j] = this.values[j * nCols + i];
trans.values[j * nCols + i] = this.values[i * nCols + j];
}
}
return trans;
}
/**
* Matrix/Matrix multiplication
*/
public Matrix multiply(Matrix M) throws SizeMismatchException {
if (nCols != M.nRows) {
throw new SizeMismatchException(this, M);
}
Matrix R;
try {
R = new Matrix(this.nRows, M.nCols);
} catch (java.lang.InstantiationException e) {
/* unreached */
return null;
}
for (int i = 0; i < R.nRows; i++) {
for (int j = 0; j < R.nCols; j++) {
for (int k = 0; k < this.nCols; k++) {
R.values[i * R.nCols + j] += this.values[i * nCols + k] * M.values[k * nRows + j];
}
}
}
return R;
}
/**
* Matrix/vector multiplication
*/
public Vector multiply(Vector v) throws SizeMismatchException {
if (nCols != v.size()) {
throw new SizeMismatchException(this, v);
}
Vector u = null;
try {
u = new Vector(nRows);
} catch (java.lang.InstantiationException e) {
/* unreached */
}
for (int i = 0; i < u.size(); i++) {
double e = 0.0;
for (int k = 0; k < this.nCols; k++) {
e += values[i * nCols + k] * v.get(k);
}
u.set(i, e);
}
return u;
}
/**
* Sets the element on row @i and column @j to the given value @value.
*/
public void set(int i, int j, double value) {
values[i * nCols + j] = value;
}
/**
* Gets the element on row @i and column @j.
*/
public double get(int i, int j) {
return values[i * nCols + j];
}
/**
* Sets the matrix name
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns a Matlab compatible representation of the Matrix.
*/
public String toString() {
String repr = name + " = [";
int spacing = repr.length();
for (int i = 0; i < nRows; i++) {
if (i > 0) {
for (int j = 0; j < spacing; j++) {
repr += " ";
}
}
for (int j = 0; j < nCols; j++) {
repr += values[nCols * i + j] + " ";
}
repr += ";\n";
}
repr += "];";
return repr;
}
protected void allocValues(int nRows, int nCols) throws java.lang.InstantiationException {
int size = nRows * nCols;
if (size < 1) {
throw new java.lang.InstantiationException("Both matrix dimensions must be strictly positive");
}
this.values = new double[size];
this.nRows = nRows;
this.nCols = nCols;
}
public String getName() {
return name;
}
public int nRows() {
return nRows;
}
public int nCols() {
return nCols;
}
public String name = "M";
protected double values[];
private int nRows;
private int nCols;
}

View file

@ -0,0 +1,29 @@
package algebra;
/**
* Exception class for incorrect dimensions in arithmetic operations on Matrix and Vector
*/
public class SizeMismatchException extends Exception {
public SizeMismatchException () {
}
public SizeMismatchException (Vector v1, Vector v2) {
super (v1.name + "[" + v1.size () + "] != " + v2.name + "[" + v2.size () + "]");
}
public SizeMismatchException (Matrix M1, Matrix M2) {
super (M1.name + "[*," + M1.nCols () + "] != " + M2.name + "[" + M2.nRows () + ",*]");
}
public SizeMismatchException (Matrix M, Vector v) {
super (M.name + "[*," + M.nCols () + "] != " + v.name + "[" + v.size () + ",*]");
}
public SizeMismatchException (String msg) {
super (msg);
}
}

198
algebra/Vector.java Normal file
View file

@ -0,0 +1,198 @@
/**
* @author: cdehais
*
* Basic linear algebra methods
*/
package algebra;
import java.lang.Math;
public class Vector implements Cloneable {
protected int size;
protected double values[];
public String name = "v";
protected Vector() {
}
/**
* Creates a named vector of size @size
*/
public Vector(String name, int size) throws java.lang.InstantiationException {
this(size);
this.name = name;
}
/**
* Creates a vector of size @size
*/
public Vector(int size) throws java.lang.InstantiationException {
allocValues(size);
}
/**
* Compute the norm of the vector
*/
public double norm() {
double r = 0.0;
for (int i = 0; i < this.size; i++) {
r += this.values[i] * this.values[i];
}
return Math.sqrt(r);
}
/**
* Makes the Vector unitary.
*/
public void normalize() {
double norm = norm();
for (int i = 0; i < size; i++) {
values[i] /= norm;
}
}
/**
* Multiplies the Vector by the given constant.
*/
public void scale(double f) {
for (int i = 0; i < size; i++) {
values[i] *= f;
}
}
/**
* Computes the vector dot product between the Vector and another Vector.
* Both must be the same size.
*/
public double dot(Vector v) throws SizeMismatchException {
if (size != v.size) {
throw new SizeMismatchException(this, v);
}
double d = 0.0;
for (int i = 0; i < size; i++) {
d += this.values[i] * v.values[i];
}
return d;
}
/**
* Adds the given Vector to the Vector
*/
public void add(Vector v) throws SizeMismatchException {
if (size != v.size) {
throw new SizeMismatchException(this, v);
}
for (int i = 0; i < size; i++) {
values[i] += v.values[i];
}
}
/**
* Subtracts the given Vector to the Vector
*/
public void subtract(Vector v) throws SizeMismatchException {
if (size != v.size) {
throw new SizeMismatchException(this, v);
}
for (int i = 0; i < size; i++) {
values[i] -= v.values[i];
}
}
/**
* Returns a string representation of the Vector.
* Using Matlab compatible output for easy debugging.
*/
public String toString() {
String repr = name + " = [";
for (int i = 0; i < size - 1; i++) {
repr += values[i] + ", ";
}
repr += values[size - 1] + "]';";
return repr;
}
/**
* Sets the name of the Vector
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the Vector's name
*/
public String getName() {
return this.name;
}
/**
* Sets the @i-th coordinate to the given value @value.
*/
public void set(int i, double value) {
this.values[i] = value;
}
/**
* Sets the values of the vector to the values contained in the given array
*/
public void set(double values[]) throws Exception {
if (values.length != this.size) {
throw new Exception("Bad size");
}
this.values = values;
}
/**
* Sets all elements of the vector to 0
*/
public void zeros() {
for (int i = 0; i < size; i++) {
values[i] = 0.0;
}
}
/**
* Sets all elements of the vector to 1
*/
public void ones() {
for (int i = 0; i < size; i++) {
values[i] = 1.0;
}
}
/**
* Gets the @i-th coordinate of the Vector.
*/
public double get(int i) {
return this.values[i];
}
/**
* Returns the Vector size
*/
public int size() {
return this.size;
}
protected void allocValues(int size) throws java.lang.InstantiationException {
if (size < 1) {
throw new java.lang.InstantiationException("Vector size must be strictly positive");
}
this.values = new double[size];
this.size = size;
}
}

125
algebra/Vector3.java Normal file
View file

@ -0,0 +1,125 @@
/**
* @author: cdehais
*/
package algebra;
import java.lang.Math;
public class Vector3 extends Vector {
public Vector3(double x, double y, double z) {
super();
try {
allocValues(3);
} catch (java.lang.InstantiationException e) {
/* unreached */
}
this.values[0] = x;
this.values[1] = y;
this.values[2] = z;
}
public Vector3() {
this(0.0, 0.0, 0.0);
}
public Vector3(String name) {
this(0.0, 0.0, 0.0);
this.setName(name);
}
/**
* Creates a new named 3D vector with coordinates (x, y, z).
*/
public Vector3(String name, double x, double y, double z) {
super();
try {
allocValues(4);
} catch (java.lang.InstantiationException e) {
/* unreached */
}
this.values[0] = x;
this.values[1] = y;
this.values[2] = z;
}
/**
* Copy constructor from a Vector of size 3 or 4.
* For a vector of size 4, divide the 3 first coordinates by the fourth.
*/
public Vector3(Vector v) throws InstantiationException {
this();
if ((v.size != 3) && (v.size != 4)) {
throw new InstantiationException("Can only build 3D vector from vector of size 3 or 4");
}
if (v.size == 3) {
set(v.get(0), v.get(1), v.get(2));
} else {
double w = v.get(3);
set(v.get(0) / w, v.get(1) / w, v.get(2) / w);
}
}
/**
* Makes the x, y, and z coordinates of the Vector3 cartesian, by dividing them
* by
* the homogeneous coordinate w.
*/
public void makeCartesian() throws java.lang.ArithmeticException {
this.values[0] /= this.values[3];
this.values[1] /= this.values[3];
this.values[2] /= this.values[3];
this.values[3] = 1.0;
}
public void set(double x, double y, double z) {
this.values[0] = x;
this.values[1] = y;
this.values[2] = z;
}
/**
* Computes the cross product between the Vector3 and the given vector.
*/
public Vector3 cross(Vector3 v) {
double rx = this.getY() * v.getZ() - this.getZ() * v.getY();
double ry = this.getZ() * v.getX() - this.getX() * v.getZ();
double rz = this.getX() * v.getY() - this.getY() * v.getX();
return new Vector3(rx, ry, rz);
}
public double dot(Vector3 v) {
double r = 0.0;
return (values[0] * v.values[0] + values[1] * v.values[1] + values[2] * v.values[2]);
}
public double norm() {
double r = (values[0] * values[0] + values[1] * values[1] + values[2] * values[2]);
return Math.sqrt(r);
}
/**
* Gets the x coordinates of the Vector3
*/
public double getX() {
return this.values[0];
}
/**
* Gets the w coordinates of the Vector3
*/
public double getY() {
return this.values[1];
}
/**
* Gets the w coordinates of the Vector3
*/
public double getZ() {
return this.values[2];
}
}

BIN
data/brick.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

1330
data/colored_stfdbunny.off Normal file

File diff suppressed because it is too large Load diff

23
data/cube_multi_color.off Normal file
View file

@ -0,0 +1,23 @@
OFF
8 12 0
# fichier OFF non standard pour ajouter les couleurs (RGB):
0 0 0 0.9 0.9 0.9
1 0 0 0.8 0. 0
1 1 0 0.8 0.8 0
0 1 0 0. 0.8 0.
0 0 1 0 0 0.8
1 0 1 0.8 0. 0.8
1 1 1 0.8 0.8 0.8
0 1 1 0 0.8 0.8
3 0 1 4
3 1 5 4
3 1 2 5
3 2 6 5
3 2 3 6
3 3 7 6
3 3 0 7
3 0 4 7
3 4 5 7
3 5 6 7
3 3 2 0
3 2 1 0

23
data/cube_trigs_color.off Normal file
View file

@ -0,0 +1,23 @@
OFF
8 12 0
# fichier OFF non standard pour ajouter les couleurs (RGB):
0 0 0 0.8 0 0
1 0 0 0.8 0 0
1 1 0 0.8 0 0
0 1 0 0.8 0 0
0 0 1 0.8 0 0
1 0 1 0.8 0 0
1 1 1 0.8 0 0
0 1 1 0.8 0 0
3 0 1 4
3 1 5 4
3 1 2 5
3 2 6 5
3 2 3 6
3 3 7 6
3 3 0 7
3 0 4 7
3 4 5 7
3 5 6 7
3 3 2 0
3 2 1 0

19
data/example0.scene Normal file
View file

@ -0,0 +1,19 @@
# object filename:
data/cube_multi_color.off
# camera position:
3.0 5.0 5.0
# camera looks at:
0.0 0.0 0.0
# camera's y vector:
0.0 -1.0 0.0
# focal length:
512
# w and h (pixels):
512 512
# ambient light intensity Ia
1.8
# point light : 3D coordinates and ligth intensity (x, y, z, Id)
0.0 0.0 8.0 9.0
# object material parameters (Ka, Kd, Ks, s)
0.5 0.5 0.5 1.0

21
data/example1.scene Normal file
View file

@ -0,0 +1,21 @@
# object filename:
data/colored_stfdbunny.off
# camera position:
2.0 2.0 3.0
# camera looks at:
0.0 0.1 0.0
# camera's y vector:
0.0 -1.0 0.0
# focal length:
3500
# w and h (pixels):
640 480
# ambient light intensity Ia
0.5
# point light : 3D coordinates and ligth intensity (x, y, z, Id)
5.0 8.0 8.0 4.0
# object material parameters (Ka, Kd, Ks, s)
0.5 0.5 0.5 1.0

19
data/example2.scene Normal file
View file

@ -0,0 +1,19 @@
# object filename:
data/monkey2.off
# camera position:
-1.0 3.0 4.0
# camera looks at:
0.0 0.0 0.0
# camera's y vector:
0.0 -1.0 0.0
# focal length (pixels):
2000
# w and h (pixels):
800 600
# ambient (white) light intensity Ia
1.0
# point light : 3D coordinates and ligth intensity (x, y, z, Id)
0.0 0.0 8.0 0.8
# object material parameters (Ka, Kd, Ks, s)
0.5 0.5 0.5 3.0

View file

@ -0,0 +1,19 @@
# object filename:
data/textured_facet.off
# camera position:
0.75 1.25 0.5
# camera looks at:
0.5 0.5 0.0
# camera's y vector:
0.0 0.0 1.0
# focal length (pixels):
256
# w and h (pixels):
512 512
# ambient light intensity Ia
0.1
# point light : 3D coordinates and ligth intensity (x, y, z, Id)
0.0 0.0 8.0 0.9
# object material parameters (Ka, Kd, Ks, s)
0.5 0.5 0.5 10.0

8757
data/monkey2.off Normal file

File diff suppressed because it is too large Load diff

9
data/textured_facet.off Normal file
View file

@ -0,0 +1,9 @@
OFF
4 2 0
# fichier OFF non standard pour ajouter les couleurs (RGB) et les uv
0 0 0 1.0 0.0 0.0 0.0 0.0
1 0 0 0.0 1.0 0.0 2.0 0.0
1 1 0 0.0 0.0 1.0 2.0 4.0
0 1 0 0.8 0.1 0.1 0.0 4.0
3 0 1 3
3 1 2 3

BIN
data/world_map.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB