projet-rendu/Rasterizer.java

272 lines
8.3 KiB
Java
Raw Normal View History

2022-04-12 10:08:58 +00:00
import algebra.*;
/**
* 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());
}
2022-04-19 09:19:30 +00:00
public void rasterizeVertice(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);
}
}
}
2022-04-12 10:08:58 +00:00
/**
* 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();
// tracé d'un segment avec l'algo de Bresenham
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;
2022-04-12 10:08:58 +00:00
}
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;
2022-04-12 10:08:58 +00:00
}
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;
2022-04-14 20:42:57 +00:00
if ((ystep == 1) && (y_courant < y + 0.5) || ((ystep == -1) && (y_courant > y - 0.5))) {
y = y; // dafuk ?
} 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);
}
}
2022-04-12 10:08:58 +00:00
}
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;
}
private int min3(int a, int b, int c) {
return Math.min(a, Math.min(b, c));
}
private int max3(int a, int b, int c) {
return Math.max(a, Math.max(b, c));
}
2022-04-12 10:08:58 +00:00
/**
* Rasterizes the triangular face made of the Fragment v1, v2 and v3
*/
public void rasterizeFace(Fragment v1, Fragment v2, Fragment v3) {
2022-04-19 20:25:27 +00:00
// calcul de la matrice barycentrique
2022-04-12 10:08:58 +00:00
Matrix C = makeBarycentricCoordsMatrix(v1, v2, v3);
2022-04-19 20:25:27 +00:00
// calcul de la bounding box
int x_hg = min3(v1.getX(), v2.getX(), v3.getX());
int y_hg = min3(v1.getY(), v2.getY(), v3.getY());
int x_bd = max3(v1.getX(), v2.getX(), v3.getX());
int y_bd = max3(v1.getY(), v2.getY(), v3.getY());
2022-04-19 20:25:27 +00:00
// parcours des pixels à l'intérieur de la bounding box
for (int px = x_hg; px < x_bd; px++) {
boucle: for (int py = y_hg; py < y_bd; py++) {
Vector3 point = new Vector3(1, px, py);
Vector barycentre = null;
try {
2022-04-19 20:25:27 +00:00
// calcul du barycentre du triangle
barycentre = C.multiply(point);
} catch (SizeMismatchException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
// si une des coordonnées barycentrique est négative,
// le pixel n'est pas dans le triangle
2022-04-14 20:35:42 +00:00
if (barycentre.get(i) < -1e-3) {
2022-04-19 20:25:27 +00:00
// alors, on passe au pixel suivant
continue boucle;
}
}
2022-04-19 20:25:27 +00:00
// Sinon, le pixel est dans le triangle
// On créé un fragment
2022-04-19 20:25:27 +00:00
Fragment fragment = new Fragment(px, py);
2022-04-19 20:25:27 +00:00
// on interpole ses attributs: couleur, normale, depth...
for (int i = 0; i < fragment.getNumAttributes(); i++) {
2022-04-14 14:49:46 +00:00
double pondere = 0;
pondere += v1.getAttribute(i) * barycentre.get(0);
pondere += v2.getAttribute(i) * barycentre.get(1);
pondere += v3.getAttribute(i) * barycentre.get(2);
2022-04-19 20:25:27 +00:00
fragment.setAttribute(i, pondere);
}
if (Renderer.Thong) {
double[] material = Renderer.scene.getMaterial();
Vector3 cameraPosition = Renderer.scene.getCameraPosition();
Lighting lighting = Renderer.lighting;
// on calcule la couleur illuminée
double[] litColor = lighting.applyLights(fragment.getVertice(), fragment.getNormal(),
fragment.getColor(), cameraPosition, material[0], material[1], material[2], material[3]);
// on applique la couleur au fragment
fragment.setColor(litColor[0] / 255, litColor[1] / 255, litColor[2] / 255);
}
// on affiche le pixel
2022-04-19 20:25:27 +00:00
shader.shade(fragment);
}
}
2022-04-12 10:08:58 +00:00
}
}