I have been working on procedural generation of 3d models using threeJS. I have been able to successfully procure a model with the help of marching cubes algorithm and smoothened it with the help of Gaussian filter. However when I try to view the model on the window the surface on the front view is completely culled out and the insides of the model is seen through it. The code used to view the model is as follows
const marchingCubes = (scalarField, dims, threshold) => {
const geometry = new THREE.BufferGeometry();
const vertices = [];
const colors = [];
const normals = [];
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const cB = new THREE.Vector3();
const aB = new THREE.Vector3();
const color = new THREE.Color();
for(let z = 0; z < dims[2] - 1; z++) {
for(let y = 0; y < dims[1] - 1; y++) {
for(let x = 0; x < dims[0] - 1; x++) {
const cube = getCube(scalarField, x, y, z);
const cubeIndex = getCubeIndex(cube, threshold);
const edges = EdgeMasks[cubeIndex];
if(edges === 0) {
continue;
}
const interpolatedVertices = interpolateEdges(cube, edges, threshold);
const triangles = triangleTable[cubeIndex]
let i = 0;
while(triangles[i] != -1) {
const v1 = interpolatedVertices[triangles[i]];
const v2 = interpolatedVertices[triangles[i + 1]];
const v3 = interpolatedVertices[triangles[i + 2]];
pA.set(v1[0], v1[1], v1[2]);
pB.set(v2[0], v2[1], v2[2]);
pC.set(v3[0], v3[1], v3[2]);
cB.subVectors(pC, pB);
aB.subVectors(pA, pB);
cB.cross(aB);
cB.normalize();
const nx = cB.x;
const ny = cB.y;
const nz = cB.z;
const n0 = [nx, ny, nz];
const vx = (x / dims[0]) + 0.5;
const vy = (y / dims[1]) + 0.5;
const vz = (z / dims[2]) + 0.5;
color.setRGB(vx, vy, vz);
const c0 = [color.r, color.g, color.b];
vertices.push(...v1, ...v2, ...v3);
normals.push(...n0, ...n0, ...n0);
colors.push(...c0, ...c0, ...c0);
i += 3;
}
}
}
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometryputeBoundingSphere();
return geometry;
}
const WebApplication = () => {
const Ref = useRef(null);
const handleButtonClick = async () => {
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0x444444, 3));
const light1 = new THREE.DirectionalLight(0xffffff, 1.5);
light1.position.set(1, 1, 1);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 4.5);
light2.position.set(0, -1, 0);
scene.add(light2);
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0.5);
renderer.setPixelRatio(window.devicePixelRatio);
if(Ref.current) {
Ref.current.innerHTML = '';
Ref.current.appendChild(renderer.domElement);
}
const camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.5;
controls.update();
.
.
.
const scalarField = generateScalarField(int16Array, dims);
const threshold = 0.166666666665;
const size = 5;
const sigma = 1.0;
const kernel = createGaussianKernel(size, sigma);
const gaussianFilter = applyGaussianFilter(scalarField, dims, kernel);
let geometry = marchingCubes(gaussianFilter, dims, threshold);
let material = new THREE.MeshBasicMaterial({color: 0xaaaaaa, specular: 0xffffff, shininess: 250, side: THREE.FrontSide, vertexColors: true})
const cubes = new THREE.Mesh(geometry, material);
cubes.position.set(Math.trunc(-(dims[0] / 2)), Math.trunc(-(dims[1] / 2)), Math.trunc(-(dims[2] / 2)));
scene.add(cubes);
camera.position.z = Math.max(dims[0], dims[1], dims[2]) * 1.5;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
return (
<div>
<button onClick={handleButtonClick}>vroom vroom</button>
<div ref={Ref} style={{ width: "100vw", height: "100vh" }}></div>
</div>
);
};
I am a beginner to a lot of these and in this code the color and normal attributes are copied off of a code block and are not verified (all code is copied off but mostly verified on the working). I don't know what portion is causing the error or what else should have I added to the code to make it work. All suggestions and remarks are welcome :)
The model in here is actually rendered in the code itself. I know it is not a good thing to do on the frontend but I am kinda brute forcing the whole thing in one file. After the model is created I downloaded it and checked whether if its rendered with no issues in blender and the model had no issues. I tried custom shaders on the model, didn't work. I don't know shader code much and I tried my best with it couldn't do it. The only material that gave any color to the model was MeshBasicMaterial
but it didn't fix my current issue. Tried THREE.FrontSide
, no effect. the last thing i tried was the vertex normals. I am a bit on the fot side on "Vectors", so the way i understood it was that the normals decide to which side the triangle is facing and that didn't happen in the result.
I have been working on procedural generation of 3d models using threeJS. I have been able to successfully procure a model with the help of marching cubes algorithm and smoothened it with the help of Gaussian filter. However when I try to view the model on the window the surface on the front view is completely culled out and the insides of the model is seen through it. The code used to view the model is as follows
const marchingCubes = (scalarField, dims, threshold) => {
const geometry = new THREE.BufferGeometry();
const vertices = [];
const colors = [];
const normals = [];
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const cB = new THREE.Vector3();
const aB = new THREE.Vector3();
const color = new THREE.Color();
for(let z = 0; z < dims[2] - 1; z++) {
for(let y = 0; y < dims[1] - 1; y++) {
for(let x = 0; x < dims[0] - 1; x++) {
const cube = getCube(scalarField, x, y, z);
const cubeIndex = getCubeIndex(cube, threshold);
const edges = EdgeMasks[cubeIndex];
if(edges === 0) {
continue;
}
const interpolatedVertices = interpolateEdges(cube, edges, threshold);
const triangles = triangleTable[cubeIndex]
let i = 0;
while(triangles[i] != -1) {
const v1 = interpolatedVertices[triangles[i]];
const v2 = interpolatedVertices[triangles[i + 1]];
const v3 = interpolatedVertices[triangles[i + 2]];
pA.set(v1[0], v1[1], v1[2]);
pB.set(v2[0], v2[1], v2[2]);
pC.set(v3[0], v3[1], v3[2]);
cB.subVectors(pC, pB);
aB.subVectors(pA, pB);
cB.cross(aB);
cB.normalize();
const nx = cB.x;
const ny = cB.y;
const nz = cB.z;
const n0 = [nx, ny, nz];
const vx = (x / dims[0]) + 0.5;
const vy = (y / dims[1]) + 0.5;
const vz = (z / dims[2]) + 0.5;
color.setRGB(vx, vy, vz);
const c0 = [color.r, color.g, color.b];
vertices.push(...v1, ...v2, ...v3);
normals.push(...n0, ...n0, ...n0);
colors.push(...c0, ...c0, ...c0);
i += 3;
}
}
}
}
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometryputeBoundingSphere();
return geometry;
}
const WebApplication = () => {
const Ref = useRef(null);
const handleButtonClick = async () => {
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0x444444, 3));
const light1 = new THREE.DirectionalLight(0xffffff, 1.5);
light1.position.set(1, 1, 1);
scene.add(light1);
const light2 = new THREE.DirectionalLight(0xffffff, 4.5);
light2.position.set(0, -1, 0);
scene.add(light2);
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x000000, 0.5);
renderer.setPixelRatio(window.devicePixelRatio);
if(Ref.current) {
Ref.current.innerHTML = '';
Ref.current.appendChild(renderer.domElement);
}
const camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 1, 3500);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.5;
controls.update();
.
.
.
const scalarField = generateScalarField(int16Array, dims);
const threshold = 0.166666666665;
const size = 5;
const sigma = 1.0;
const kernel = createGaussianKernel(size, sigma);
const gaussianFilter = applyGaussianFilter(scalarField, dims, kernel);
let geometry = marchingCubes(gaussianFilter, dims, threshold);
let material = new THREE.MeshBasicMaterial({color: 0xaaaaaa, specular: 0xffffff, shininess: 250, side: THREE.FrontSide, vertexColors: true})
const cubes = new THREE.Mesh(geometry, material);
cubes.position.set(Math.trunc(-(dims[0] / 2)), Math.trunc(-(dims[1] / 2)), Math.trunc(-(dims[2] / 2)));
scene.add(cubes);
camera.position.z = Math.max(dims[0], dims[1], dims[2]) * 1.5;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
}
return (
<div>
<button onClick={handleButtonClick}>vroom vroom</button>
<div ref={Ref} style={{ width: "100vw", height: "100vh" }}></div>
</div>
);
};
I am a beginner to a lot of these and in this code the color and normal attributes are copied off of a code block and are not verified (all code is copied off but mostly verified on the working). I don't know what portion is causing the error or what else should have I added to the code to make it work. All suggestions and remarks are welcome :)
The model in here is actually rendered in the code itself. I know it is not a good thing to do on the frontend but I am kinda brute forcing the whole thing in one file. After the model is created I downloaded it and checked whether if its rendered with no issues in blender and the model had no issues. I tried custom shaders on the model, didn't work. I don't know shader code much and I tried my best with it couldn't do it. The only material that gave any color to the model was MeshBasicMaterial
but it didn't fix my current issue. Tried THREE.FrontSide
, no effect. the last thing i tried was the vertex normals. I am a bit on the fot side on "Vectors", so the way i understood it was that the normals decide to which side the triangle is facing and that didn't happen in the result.
1 Answer
Reset to default 0The reason why the model was seen as inside out when rendered was because the side
was the wrong one and since there was a large number of vertices joined together simply assigning THREE.FrontSide
didn't help with it. So from the information I gathered, in THREE.BufferGeometry
whether a triangle is rendered as FrontSide or BackSide depends on the winding order of the vertices. So I flipped all the values in the triangleTable
(the triangleTable consist of three edges of the triangle, may contain multiple triangle values so flipped here doesn't mean total reversal just the triangle edges, so 3 each until the end). If the winding order is in Counter Clock Wise then FrontSide will be shown and BackSide when the order is Clock Wise.
I used the lookup table from https://gist.github/dwilliamson/c041e3454a713e58baf6e4f8e5fffecd
and ran this code block to flip the values
const transform = [];
let i = 0;
let j = 0;
for(i = 0; i < 256; i++){
transform[i] = [];
for(j = 0; triangleTable[i][j] != -1; j += 3) {
transform[i][j] = triangleTable[i][j + 2];
transform[i][j + 1] = triangleTable[i][j + 1];
transform[i][j + 2] = triangleTable[i][j];
}
transform[i][j] = -1;
}
console.log(transform);