admin管理员组文章数量:1414614
I'm trying to create a 3d rugby ball using ThreeJS and RTF and I'm struggling to create 4 faces on the ball where two subsequent faces on the ball will have two different images. Something similar to this.
I have been able to create the geometry and add one image on one face. But the other 3 images don't show up on the geometry.
This is the code that I have so far:
import React, { Suspense } from "react";
import { Canvas, useLoader } from "@react-three/fiber";
import {
OrbitControls,
useProgress,
Html,
} from "@react-three/drei";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import * as THREE from "three";
const RugbyBall: React.FC = () => {
// Load textures
const one = useLoader(THREE.TextureLoader, "/images/one.jpg");
const two = useLoader(THREE.TextureLoader, "/images/two.jpg");
const texture = useLoader(THREE.TextureLoader, "/images/texture.jpg");
// Create materials
const oneMaterial = new THREE.MeshPhysicalMaterial({
map: one,
bumpMap: texture,
bumpScale: 1,
roughness: 1,
metalness: 0,
// side: THREE.FrontSide,
});
const twoMaterial = new THREE.MeshPhysicalMaterial({
map: two,
bumpMap: texture,
bumpScale: 1,
roughness: 1,
metalness: 0,
// side: THREE.BackSide,
});
// Create the Sphere Geometry (a simple sphere)
const radius = 50; // Size of the ball
const widthSegments = 64; // Number of horizontal segments
const heightSegments = 64; // Number of vertical segments
const sphereGeometry = new THREE.SphereGeometry(
radius,
widthSegments,
heightSegments
);
// Scale the sphere to elongate it into a rugby ball shape
sphereGeometry.scale(1, 1.8, 1);
// Clear any pre-existing groups
sphereGeometry.clearGroups();
if (sphereGeometry.index && sphereGeometry.index.count) {
console.log("sphereGeometry.index.count: ", sphereGeometry.index.count);
// Split the sphere into two hemispheres
const middle = Math.floor(sphereGeometry.index.count / 2);
// Add group for the top hemisphere (one)
for (let i = 0; i < middle; i++) {
sphereGeometry.addGroup(i * 3, 3, 0); // Apply one material to the first half
}
// Add group for the bottom hemisphere (two)
for (let i = middle; i < sphereGeometry.index.count / 3; i++) {
sphereGeometry.addGroup(i * 3, 3, 1); // Apply two material to the second half
}
}
// Create the mesh and apply the materials
const materials = [oneMaterial, twoMaterial]; // Two materials: one and two
const rugbyBall = new THREE.Mesh(sphereGeometry, materials);
return <primitive object={rugbyBall} position={[0, 0, 0]} scale={2.5} rotation={[-(Math.PI / 2), 0, 0]} />;
};
const Loader = () => {
const { progress } = useProgress();
return <Html center>{Math.round(progress)}% loaded</Html>;
};
// Main Scene Component
const Scene = () => {
return (
<Canvas
camera={{ position: [0, 0, 500], fov: 75 }}
style={{
width: "100vw",
height: "30vh",
backgroundColor: "red",
}}
>
<Suspense fallback={<Loader />}>
{/* Lighting */}
<ambientLight intensity={1} />
<directionalLight position={[0, 1, 0]} intensity={2} />
{/* 3D Model */}
<RugbyBall />
{/* Controls */}
<OrbitControls autoRotate autoRotateSpeed={2} enableDamping dampingFactor={0.1} />
</Suspense>
</Canvas>
);
};
export default Scene;
I'm trying to create a 3d rugby ball using ThreeJS and RTF and I'm struggling to create 4 faces on the ball where two subsequent faces on the ball will have two different images. Something similar to this.
I have been able to create the geometry and add one image on one face. But the other 3 images don't show up on the geometry.
This is the code that I have so far:
import React, { Suspense } from "react";
import { Canvas, useLoader } from "@react-three/fiber";
import {
OrbitControls,
useProgress,
Html,
} from "@react-three/drei";
import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader";
import * as THREE from "three";
const RugbyBall: React.FC = () => {
// Load textures
const one = useLoader(THREE.TextureLoader, "/images/one.jpg");
const two = useLoader(THREE.TextureLoader, "/images/two.jpg");
const texture = useLoader(THREE.TextureLoader, "/images/texture.jpg");
// Create materials
const oneMaterial = new THREE.MeshPhysicalMaterial({
map: one,
bumpMap: texture,
bumpScale: 1,
roughness: 1,
metalness: 0,
// side: THREE.FrontSide,
});
const twoMaterial = new THREE.MeshPhysicalMaterial({
map: two,
bumpMap: texture,
bumpScale: 1,
roughness: 1,
metalness: 0,
// side: THREE.BackSide,
});
// Create the Sphere Geometry (a simple sphere)
const radius = 50; // Size of the ball
const widthSegments = 64; // Number of horizontal segments
const heightSegments = 64; // Number of vertical segments
const sphereGeometry = new THREE.SphereGeometry(
radius,
widthSegments,
heightSegments
);
// Scale the sphere to elongate it into a rugby ball shape
sphereGeometry.scale(1, 1.8, 1);
// Clear any pre-existing groups
sphereGeometry.clearGroups();
if (sphereGeometry.index && sphereGeometry.index.count) {
console.log("sphereGeometry.index.count: ", sphereGeometry.index.count);
// Split the sphere into two hemispheres
const middle = Math.floor(sphereGeometry.index.count / 2);
// Add group for the top hemisphere (one)
for (let i = 0; i < middle; i++) {
sphereGeometry.addGroup(i * 3, 3, 0); // Apply one material to the first half
}
// Add group for the bottom hemisphere (two)
for (let i = middle; i < sphereGeometry.index.count / 3; i++) {
sphereGeometry.addGroup(i * 3, 3, 1); // Apply two material to the second half
}
}
// Create the mesh and apply the materials
const materials = [oneMaterial, twoMaterial]; // Two materials: one and two
const rugbyBall = new THREE.Mesh(sphereGeometry, materials);
return <primitive object={rugbyBall} position={[0, 0, 0]} scale={2.5} rotation={[-(Math.PI / 2), 0, 0]} />;
};
const Loader = () => {
const { progress } = useProgress();
return <Html center>{Math.round(progress)}% loaded</Html>;
};
// Main Scene Component
const Scene = () => {
return (
<Canvas
camera={{ position: [0, 0, 500], fov: 75 }}
style={{
width: "100vw",
height: "30vh",
backgroundColor: "red",
}}
>
<Suspense fallback={<Loader />}>
{/* Lighting */}
<ambientLight intensity={1} />
<directionalLight position={[0, 1, 0]} intensity={2} />
{/* 3D Model */}
<RugbyBall />
{/* Controls */}
<OrbitControls autoRotate autoRotateSpeed={2} enableDamping dampingFactor={0.1} />
</Suspense>
</Canvas>
);
};
export default Scene;
Share
Improve this question
edited Feb 22 at 8:30
Łukasz Daniel Mastalerz
2,4482 gold badges7 silver badges28 bronze badges
asked Feb 21 at 4:56
SiddAjmeraSiddAjmera
39.5k6 gold badges76 silver badges113 bronze badges
1 Answer
Reset to default 0It doesn't work like that. You have two options: either modify the native geometry, e.g. SphereGeometry
, or create a model, e.g. in Blender
, name the appropriate sections and map them (which in my opinion is an easier and faster way). When you apply a texture to a native SphereGeometry
, the texture wraps around the entire sphere by default. If you want modify geometry you can assign different materials to each group, allowing for multiple colors or textures on different parts of the sphere, but like I wrote faster is create model in Blender
. You need to converting the geometry to non-indexed using the toNonIndexed()
method, where each face gets its own set of vertices. This allow assigning unique colors/textures to each face, because vertices are no longer shared between faces, so you can create vertical stripes (for example) on the rugby ball.
Try something like this...
<script type="importmap">
{
"imports": {
"three": "https://unpkg/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg/[email protected]/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(2);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 0, 15);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const radius = 3;
const widthSegments = 64;
const heightSegments = 64;
let sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
sphereGeometry.scale(1, 1.5, 1);
sphereGeometry = sphereGeometry.toNonIndexed();
const uvA = sphereGeometry.getAttribute("uv");
const posA = sphereGeometry.getAttribute("position");
const normalA = sphereGeometry.getAttribute("normal");
const faceCount = sphereGeometry.attributes.position.count / 3;
const stripeCount = 4;
const stripeGroups = Array.from({ length: stripeCount }, () => []);
for (let face = 0; face < faceCount; face++) {
const i0 = face * 3;
const i1 = face * 3 + 1;
const i2 = face * 3 + 2;
const u0 = uvA.getX(i0);
const u1 = uvA.getX(i1);
const u2 = uvA.getX(i2);
let avgU = (u0 + u1 + u2) / 3;
let stripeIndex = Math.floor(avgU * stripeCount);
if (stripeIndex === stripeCount) stripeIndex = stripeCount - 1;
stripeGroups[stripeIndex].push(face);
}
const newPositions = [];
const newNormals = [];
const newUVs = [];
let offset = 0;
const groupInfos = [];
for (let stripe = 0; stripe < stripeCount; stripe++) {
const faces = stripeGroups[stripe];
const groupStart = offset;
faces.forEach((face) => {
const i0 = face * 3;
const i1 = face * 3 + 1;
const i2 = face * 3 + 2;
for (let i of [i0, i1, i2]) {
newPositions.push(posA.getX(i), posA.getY(i), posA.getZ(i));
newNormals.push(normalA.getX(i), normalA.getY(i), normalA.getZ(i));
newUVs.push(uvA.getX(i), uvA.getY(i));
offset++;
}
});
const count = faces.length * 3;
if (count > 0) {
groupInfos.push({ start: groupStart, count: count, materialIndex: stripe });
}
}
const newGeometryRugby = new THREE.BufferGeometry();
newGeometryRugby.setAttribute(
"position",
new THREE.Float32BufferAttribute(newPositions, 3)
);
newGeometryRugby.setAttribute(
"normal",
new THREE.Float32BufferAttribute(newNormals, 3)
);
newGeometryRugby.setAttribute("uv", new THREE.Float32BufferAttribute(newUVs, 2));
groupInfos.forEach((info) => {
newGeometryRugby.addGroup(info.start, info.count, info.materialIndex);
});
const loader = new THREE.TextureLoader();
const t1 = loader.load("https://threejs./examples/textures/brick_diffuse.jpg");
const t2 = loader.load("https://threejs./examples/textures/uv_grid_opengl.jpg");
const t3 = loader.load("https://threejs./examples/textures/brick_diffuse.jpg");
const t4 = loader.load("https://threejs./examples/textures/uv_grid_opengl.jpg");
const m = [
new THREE.MeshPhongMaterial({ map: t1 }),
new THREE.MeshPhongMaterial({ map: t2 }),
new THREE.MeshPhongMaterial({ map: t3 }),
new THREE.MeshPhongMaterial({ map: t4 }),
];
const rugbyBall = new THREE.Mesh(newGeometryRugby, m);
scene.add(rugbyBall);
const l1 = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(l1);
const l2 = new THREE.DirectionalLight(0xffffff, 0.8);
l2.position.set(5, 10, 7.5);
scene.add(l2);
function a() {
requestAnimationFrame(a);
rugbyBall.rotation.y += 0.005;
renderer.render(scene, camera);
}
a();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
版权声明:本文标题:javascript - How to create a 3d Rugby ball with 2 different images on it's subsequent faces - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745166827a2645733.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论