I have created a Javascript 3D model viewer where you can load a wavefront .obj model using a python program that I used and display it in your web browser.
I have a problem with z-clipping where if the camera gets too close to a polygon it disappears due to the no vertices/polygons behind the camera rule, how can I clip it?
Here is a very crude build of the project, ported to replit: replit/js-3d-2
Here is some of the code that is in the project that has to be corrected in order for this to work (again, I have just began my web-dev programming):
function findIntercept(x1, y1, x2, y2, line) {
return y1 + ((y2 - y1) / (x2 - x1)) * (line - x1);
}
function clipMinusPolygon(polygon, line) {
let clippedPolygon = [];
for (let i = 0; i < polygon.length; i += 2) {
let x1 = polygon[i];
let y1 = polygon[i + 1];
let x2 = polygon[(i + 2) % polygon.length];
let y2 = polygon[(i + 3) % polygon.length];
if (x1 > line && x2 > line) {
clippedPolygon.push(x1, y1);
} else if (x1 <= line && x2 <= line) {
// Skip the entire edge
} else {
let interceptY = findIntercept(x1, y1, x2, y2, line);
if (x1 > line) {
clippedPolygon.push(x1, y1);
clippedPolygon.push(line, interceptY);
} else {
clippedPolygon.push(line, interceptY);
clippedPolygon.push(x2, y2);
}
}
}
return clippedPolygon;
}
function clipPositivePolygon(polygon, line) {
let clippedPolygon = [];
for (let i = 0; i < polygon.length; i += 2) {
let x1 = polygon[i];
let y1 = polygon[i + 1];
let x2 = polygon[(i + 2) % polygon.length];
let y2 = polygon[(i + 3) % polygon.length];
if (x1 < line && x2 < line) {
clippedPolygon.push(x1, y1);
} else if (x1 >= line && x2 >= line) {
// Skip the entire edge
} else {
let interceptY = findIntercept(x1, y1, x2, y2, line);
if (x1 < line) {
clippedPolygon.push(x1, y1);
clippedPolygon.push(line, interceptY);
} else {
clippedPolygon.push(line, interceptY);
clippedPolygon.push(x2, y2);
}
}
}
return clippedPolygon;
}
function sortAndDraw() {
polygonDistance.sort(function(a, b) {return a - b;});
polygonDistance.reverse();
for (let i = 0; i < polygonDistance.length; i++) {
const key = polygonDistance[i];
let polygon = 0;
for (polygon = 0; polygon < polygonDistance.length; polygon++) {
if (allCoordinates[polygon][0] === key) {
break;
}
}
if (polygon < polygonDistance.length) {
ctx.beginPath();
let clippedPolygon = clipMinusPolygon(allCoordinates[polygon].slice(1,allCoordinates[polygon].length-1), 10);
clippedPolygon = clipPositivePolygon(clippedPolygon, canvas.width-10);
for (let j = 0; j < clippedPolygon.length; j += 2) {
ctx.lineTo(clippedPolygon[j], clippedPolygon[j+1]);
}
ctx.fillStyle = changeColor(allCoordinates[polygon][allCoordinates[polygon].length - 1], polygonDistance[i]/3);
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = changeColor(allCoordinates[polygon][allCoordinates[polygon].length - 1], polygonDistance[i]/3);
ctx.stroke();
}
}
}
If you know, please assist me! Thanks, xela.
Failed attempts:
- I tried to do the same clipping that I did to the x axis but replace them with z
- Following youtube/3d-graphics-javidx9, its helpfull but it’s C++ so it doesn’t exactly fit my needs.
- I tried following some wikipedia articles and websites.
xela is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
- Define the near and far clipping planes:
const nearPlane = 1; // Near clipping plane distance
const farPlane = 1000; // Far clipping plane distance
- Clip polygons against the z-plane:
function findInterceptZ(x1, y1, z1, x2, y2, z2, zPlane) {
const t = (zPlane - z1) / (z2 - z1);
return { x: x1 + t * (x2 - x1), y: y1 + t * (y2 - y1), z: zPlane };
}
function clipPolygon(polygon, zPlane, keepNear) {
let clippedPolygon = [];
const len = polygon.length;
for (let i = 0; i < len; i += 3) {
let x1 = polygon[i];
let y1 = polygon[i + 1];
let z1 = polygon[i + 2];
let x2 = polygon[(i + 3) % len];
let y2 = polygon[(i + 4) % len];
let z2 = polygon[(i + 5) % len];
const inside1 = keepNear ? z1 >= zPlane : z1 <= zPlane;
const inside2 = keepNear ? z2 >= zPlane : z2 <= zPlane;
if (inside1 && inside2) {
clippedPolygon.push(x1, y1, z1);
} else if (inside1 || inside2) {
const intercept = findInterceptZ(x1, y1, z1, x2, y2, z2, zPlane);
if (inside1) {
clippedPolygon.push(x1, y1, z1);
clippedPolygon.push(intercept.x, intercept.y, intercept.z);
} else {
clippedPolygon.push(intercept.x, intercept.y, intercept.z);
clippedPolygon.push(x2, y2, z2);
}
}
}
return clippedPolygon;
}
- Integrate clipping into your rendering pipeline:
function sortAndDraw() {
polygonDistance.sort((a, b) => b - a);
for (let key of polygonDistance) {
const polygonIndex = allCoordinates.findIndex(coord => coord[0] === key);
if (polygonIndex !== -1) {
let polygon = allCoordinates[polygonIndex].slice(1, -1);
polygon = clipPolygon(polygon, nearPlane, true);
polygon = clipPolygon(polygon, farPlane, false);
ctx.beginPath();
for (let i = 0; i < polygon.length; i += 3) {
ctx.lineTo(polygon[i], polygon[i + 1]);
}
ctx.closePath();
ctx.fillStyle = changeColor(allCoordinates[polygonIndex].slice(-1)[0], key / 3);
ctx.fill();
ctx.stroke();
}
}
}
This implementation handles z-clipping by checking if vertices are inside the near and far planes and clipping accordingly. Integrate this into your existing pipeline for effective z-clipping.