3D STUDIO VIEWPORT · r186

에셋 상세 설명
국내 도로 안내 표지판 지주(Pole), 가로보, 반사판 패널을 자유롭게 조합할 수 있는 모듈형 3D 키트입니다. 그리드 스냅에 맞춘 규격 설계로 교차로와 고속도로 환경을 빠르게 구축할 수 있습니다.
이 에셋을 만들 때 쓰는 프롬프트
한국 도로 안내 표지 모듈 키트. 지주, 가로보, 반사 패널을 그리드에 맞게 분리한다. 아연도금 금속 PBR. 실물 도로 가구 스케일. glTF Binary, Y-up, 미터 단위. 공개 삼각형 수는 각 에셋 페이지의 수치를 따른다.
three.js r186 코드
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
const glbUrl = "https://assetkitdb-cdn.min-inter.co.kr/originals/kr_road-sign_modular-kit_v1/v1/download/kr_road-sign_modular-kit_v1.glb";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.NeutralToneMapping;
renderer.toneMappingExposure = 1;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFShadowMap;
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
document.body.append(renderer.domElement);
const scene = new THREE.Scene();
const pmrem = new THREE.PMREMGenerator(renderer);
scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture;
const camera = new THREE.PerspectiveCamera(38, window.innerWidth / window.innerHeight, 0.01, 200);
camera.position.set(1.6, 1.1, 2.2);
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0.4, 0);
const key = new THREE.DirectionalLight(0xfffaf0, 2.2);
key.position.set(2.8, 4.2, 2.6);
key.castShadow = true;
scene.add(key);
const fill = new THREE.DirectionalLight(0x90a8ff, 0.85);
fill.position.set(-3, 2, 1.5);
scene.add(fill);
const rim = new THREE.DirectionalLight(0xc4b5fd, 1.6);
rim.position.set(0, 2.4, -3.2);
scene.add(rim);
scene.background = new THREE.Color(0x1c1e28);
const floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.ShadowMaterial({ opacity: 0.42 }));
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
const loader = new GLTFLoader();
loader.setCrossOrigin("anonymous");
loader.load(glbUrl, (gltf) => {
const box = new THREE.Box3().setFromObject(gltf.scene);
const size = box.getSize(new THREE.Vector3());
const center = box.getCenter(new THREE.Vector3());
gltf.scene.position.x -= center.x;
gltf.scene.position.y -= box.min.y;
gltf.scene.position.z -= center.z;
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
const span = Math.max(size.x, size.y, size.z, 0.2);
const distance = (span * 0.5) / Math.tan((camera.fov * Math.PI) / 360) / 0.65;
const pitch = 0.45;
const yaw = Math.PI / 4;
camera.position.set(Math.sin(yaw) * Math.cos(pitch) * distance, size.y * 0.45 + Math.sin(pitch) * distance, Math.cos(yaw) * Math.cos(pitch) * distance);
controls.target.set(0, size.y * 0.45, 0);
key.shadow.camera.left = -span;
key.shadow.camera.right = span;
key.shadow.camera.top = span;
key.shadow.camera.bottom = -span;
key.shadow.camera.near = Math.max(span / 50, 0.01);
key.shadow.camera.far = Math.max(span * 8, 12);
key.shadow.camera.updateProjectionMatrix();
scene.add(gltf.scene);
});
function resize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
resize();
window.addEventListener("resize", resize);
renderer.setAnimationLoop(() => {
controls.update();
renderer.render(scene, camera);
});
\n\n\n\n\n