Gayrimenkul Sanal Turları: WebGL ve 3D Web Teknolojisi
İstatistikler kendi adına konuşuyor: sanal turlara sahip mülkler, %87 daha fazla görüntüleme Yalnızca statik fotoğraflı olanlarla karşılaştırıldığında, bunları benimseyen kurumlar niteliksiz fiziki ziyaretleri azaltmaktadır. Nitelikli potansiyel müşteri sayısını %40 artırırken %60 oranında artırdık. Ancak çoğu uygulama mevcut pahalı ve özelleştirilebilirliği düşük SaaS çözümlerine dayanır. Bu yazıda eksiksiz bir sistem oluşturacağız tamamen tarayıcıda emlak sanal turlarının avantajlarından yararlanarak WebGL, Üç.js ölçeklenebilir ve herhangi bir listeleme platformuyla entegre edilebilecek sürükleyici deneyimler yaratmak için modern web teknolojileri.
360 derecelik panoramaların oluşturulmasından odalar arasında gezinmeye, bilgi noktalarından ölçümlere kadar etkileşimli: Üretime hazır TypeScript koduyla tüm teknik hususları ele alacağız.
Ne Öğreneceksiniz
- Three.js ve React Three Fiber ile sanal turlar için WebGL görüntüleyici mimarisi
- Eşdikdörtgen panoramaların yüklenmesi ve optimize edilmesi (360 fotoğraf/video)
- Sorunsuz geçişler ve etkileşimli erişim noktalarıyla çok odalı navigasyon
- Işın dökümü ve 3 boyutlu geometri ile etkileşimli ölçümler
- Performans optimizasyonu: LOD, doku akışı ve yavaş yükleme
- Uyumlu cihazlarda VR/AR için WebXR ile entegrasyon
- Otomatik 360 derece görüntü işleme için CI/CD hattı
- Mevcut listeleme platformlarına yerleştirme ve API entegrasyonu
Gayrimenkul için WebGL ve Three.js'nin Temelleri
WebGL ve doğrudan yüksek performanslı 2D ve 3D görüntülemeyi mümkün kılan bir JavaScript API'si eklenti olmadan tarayıcıda, cihazın GPU'sundan yararlanarak. Üç.js soyut karmaşıklık Sahneleri, kameraları, ışıkları, malzemeleri ve geometriyi yöneten üst düzey bir API ile WebGL'nin. Turlar için sanal gayrimenkul, temel kalıp ve Küre panoraması: Ters çevrilmiş dev bir küre kamera merkeze konumlandırılmış olarak, eşdikdörtgen doku içe doğru yansıtılmıştır.
Önerilen Teknoloji Yığını
- Üç.js 0.170+: çekirdek 3D oluşturma
- Üç Fiberle Reaksiyona Girin: Three.js için Bildirime Dayalı React bağlamaları
- Drei: yardımcılar ve önceden oluşturulmuş soyutlamalar (PointerLockControls, Html, vb.)
- GSAP: Sorunsuz geçiş animasyonları
- Panel: Saf panoramalar için hafif alternatif
- Keskin (Node.js): sunucu tarafında 360 derece görüntü işleme
- FFmpeg: 360 derece video işleme
Sistem Mimarisi
Bir üretim sanal tur sistemi üç farklı seviyeden oluşur: işlem hattı sunucu tarafı (dönüştürme, optimizasyon, kutucuk oluşturma), dağıtım katmanı (CDN, akış uyarlanabilir) ve işleme motoru müşteri tarafı. Bu ayrımın sağlanması önemlidir her cihazda optimum performans.
// Struttura dati per un tour virtuale
interface VirtualTour {
id: string;
propertyId: string;
name: string;
rooms: TourRoom[];
startRoomId: string;
metadata: TourMetadata;
}
interface TourRoom {
id: string;
name: string; // "Soggiorno", "Camera principale"
panoramaUrl: string; // URL immagine equirettangolare (8192x4096px)
thumbnailUrl: string;
hotspots: Hotspot[];
floorPlanPosition: { x: number; y: number };
cameraDefaultYaw: number; // orientamento iniziale (gradi)
cameraDefaultPitch: number;
}
interface Hotspot {
id: string;
type: 'navigation' | 'info' | 'measurement' | 'media';
position: { yaw: number; pitch: number }; // coordinate sferiche
targetRoomId?: string; // per type='navigation'
label?: string;
description?: string;
mediaUrl?: string;
}
interface TourMetadata {
property: {
address: string;
squareMeters: number;
price: number;
currency: string;
};
capturedAt: Date;
camera: string;
floorPlanUrl?: string;
measurementsEnabled: boolean;
}
Three.js Görüntüleyicinin Uygulanması
Sistemin kalbi WebGL görüntüleyicidir. Bir tane kullanalım SphereGeometry büyük yarıçaplı,
ters geometri (içe doğru doku) e OrbitControls simüle etmek için yapılandırılmış
panoramik kameranın davranışı (yalnızca döndürme, aşırı kaydırma/yakınlaştırma yok).
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
export class PanoramaViewer {
private scene: THREE.Scene;
private camera: THREE.PerspectiveCamera;
private renderer: THREE.WebGLRenderer;
private controls: OrbitControls;
private sphere: THREE.Mesh;
private hotspotGroup: THREE.Group;
private currentRoom: TourRoom | null = null;
private textureLoader: THREE.TextureLoader;
constructor(private container: HTMLElement) {
this.scene = new THREE.Scene();
// Camera con FOV tipico per panoramiche (75-90 gradi)
this.camera = new THREE.PerspectiveCamera(
75,
container.clientWidth / container.clientHeight,
0.1,
1000
);
this.camera.position.set(0, 0, 0.1);
// Renderer con antialiasing e supporto HDR
this.renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: false,
});
this.renderer.setSize(container.clientWidth, container.clientHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.renderer.outputColorSpace = THREE.SRGBColorSpace;
container.appendChild(this.renderer.domElement);
// Controlli orbitali configurati per panoramiche
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enablePan = false;
this.controls.enableZoom = true;
this.controls.zoomSpeed = 0.3;
this.controls.rotateSpeed = -0.3; // Inverso per comportamento naturale
this.controls.minDistance = 0;
this.controls.maxDistance = 0;
this.controls.minPolarAngle = Math.PI * 0.1;
this.controls.maxPolarAngle = Math.PI * 0.9;
// Sfera panoramica
const geometry = new THREE.SphereGeometry(500, 60, 40);
// Capovolgi la geometria per renderizzare verso l'interno
geometry.scale(-1, 1, 1);
const material = new THREE.MeshBasicMaterial({
side: THREE.BackSide, // Faccia interna visibile
});
this.sphere = new THREE.Mesh(geometry, material);
this.scene.add(this.sphere);
this.hotspotGroup = new THREE.Group();
this.scene.add(this.hotspotGroup);
this.textureLoader = new THREE.TextureLoader();
this.setupResizeObserver();
this.animate();
}
async loadRoom(room: TourRoom): Promise<void> {
this.currentRoom = room;
// Carica texture con progressive loading (prima bassa ris, poi alta)
const lowResTexture = await this.loadTexture(
room.panoramaUrl.replace('.jpg', '_low.jpg')
);
(this.sphere.material as THREE.MeshBasicMaterial).map = lowResTexture;
(this.sphere.material as THREE.MeshBasicMaterial).needsUpdate = true;
// Carica alta risoluzione in background
const highResTexture = await this.loadTexture(room.panoramaUrl);
(this.sphere.material as THREE.MeshBasicMaterial).map = highResTexture;
(this.sphere.material as THREE.MeshBasicMaterial).needsUpdate = true;
// Pulisci e ricrea hotspot
this.clearHotspots();
room.hotspots.forEach(hotspot => this.addHotspot(hotspot));
// Ruota la camera alla posizione di default della stanza
this.setInitialOrientation(room.cameraDefaultYaw, room.cameraDefaultPitch);
}
private loadTexture(url: string): Promise<THREE.Texture> {
return new Promise((resolve, reject) => {
this.textureLoader.load(url, resolve, undefined, reject);
});
}
private sphericalToCartesian(yaw: number, pitch: number, radius = 100): THREE.Vector3 {
const phi = (90 - pitch) * (Math.PI / 180);
const theta = yaw * (Math.PI / 180);
return new THREE.Vector3(
radius * Math.sin(phi) * Math.cos(theta),
radius * Math.cos(phi),
radius * Math.sin(phi) * Math.sin(theta)
);
}
private addHotspot(hotspot: Hotspot): void {
const position = this.sphericalToCartesian(
hotspot.position.yaw,
hotspot.position.pitch
);
// Sprite per hotspot (sempre orientato verso la camera)
const spriteMaterial = new THREE.SpriteMaterial({
map: this.getHotspotTexture(hotspot.type),
transparent: true,
});
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.copy(position);
sprite.scale.set(8, 8, 1);
sprite.userData = { hotspot };
this.hotspotGroup.add(sprite);
}
private animate = (): void => {
requestAnimationFrame(this.animate);
this.controls.update();
this.renderer.render(this.scene, this.camera);
};
private setupResizeObserver(): void {
const observer = new ResizeObserver(() => {
const w = this.container.clientWidth;
const h = this.container.clientHeight;
this.camera.aspect = w / h;
this.camera.updateProjectionMatrix();
this.renderer.setSize(w, h);
});
observer.observe(this.container);
}
dispose(): void {
this.renderer.dispose();
this.controls.dispose();
}
}
Geçişli Çok Odalı Navigasyon Sistemi
Odalar arasında gezinme sorunsuz ve sezgisel olmalıdır. Fade-out/fade-in geçişini uygulayalım Algılanan bekleme sürelerini ortadan kaldırmak için arka planda bir sonraki oda canlandırılıyor ve önceden yükleniyor.
export class TourNavigator {
private viewer: PanoramaViewer;
private tour: VirtualTour;
private roomCache: Map<string, THREE.Texture> = new Map();
private isTransitioning = false;
constructor(viewer: PanoramaViewer, tour: VirtualTour) {
this.viewer = viewer;
this.tour = tour;
// Preloading preventivo delle stanze adiacenti
this.preloadAdjacentRooms(tour.startRoomId);
}
async navigateToRoom(
roomId: string,
transition: 'fade' | 'blur' | 'zoom' = 'fade'
): Promise<void> {
if (this.isTransitioning) return;
this.isTransitioning = true;
const room = this.tour.rooms.find(r => r.id === roomId);
if (!room) {
this.isTransitioning = false;
return;
}
// Fase 1: fade out
await this.animateOverlay(0, 1, 300, transition);
// Fase 2: carica nuova stanza (probabilmente già in cache)
await this.viewer.loadRoom(room);
// Fase 3: preloading stanze adiacenti in background
this.preloadAdjacentRooms(roomId);
// Fase 4: fade in
await this.animateOverlay(1, 0, 300, transition);
this.isTransitioning = false;
// Analytics: traccia navigazione
this.trackRoomVisit(roomId);
}
private async preloadAdjacentRooms(currentRoomId: string): Promise<void> {
const currentRoom = this.tour.rooms.find(r => r.id === currentRoomId);
if (!currentRoom) return;
const navigationHotspots = currentRoom.hotspots
.filter(h => h.type === 'navigation' && h.targetRoomId)
.map(h => h.targetRoomId!);
// Preload in parallelo con bassa priorità
for (const roomId of navigationHotspots) {
if (!this.roomCache.has(roomId)) {
const room = this.tour.rooms.find(r => r.id === roomId);
if (room) {
// Usa Intersection Observer API per priorità basata sulla visibilità
requestIdleCallback(() => {
this.preloadRoomTexture(room);
});
}
}
}
}
private animateOverlay(
fromOpacity: number,
toOpacity: number,
durationMs: number,
effect: string
): Promise<void> {
return new Promise(resolve => {
const overlay = document.getElementById('tour-overlay')!;
overlay.style.transition = `opacity ${durationMs}ms ease-in-out`;
overlay.style.opacity = String(toOpacity);
setTimeout(resolve, durationMs);
});
}
private trackRoomVisit(roomId: string): void {
// Integrazione con Google Analytics 4
if (typeof gtag !== 'undefined') {
gtag('event', 'virtual_tour_room_visit', {
tour_id: this.tour.id,
room_id: roomId,
property_id: this.tour.propertyId,
});
}
}
}
İnteraktif Erişim Noktaları ve Ölçümler
Erişim noktaları deneyimin temel unsurlarıdır: odalar arasında gezinmenize, gösteri yapmanıza olanak tanırlar. bağlamsal bilgiler sağlar ve daha gelişmiş uygulamalarda alan ölçümleri gerçekleştirir. Panoramik küre üzerinde hassas tıklama tespiti için bir ışın döküm sistemi uyguluyoruz.
// Sistema di interazione con hotspot via raycasting
export class HotspotInteractionManager {
private raycaster = new THREE.Raycaster();
private mouse = new THREE.Vector2();
private onHotspotClick: (hotspot: Hotspot) => void;
constructor(
private camera: THREE.PerspectiveCamera,
private hotspotGroup: THREE.Group,
private canvas: HTMLCanvasElement,
onHotspotClick: (hotspot: Hotspot) => void
) {
this.onHotspotClick = onHotspotClick;
this.setupEventListeners();
}
private setupEventListeners(): void {
// Supporto touch e mouse
this.canvas.addEventListener('click', this.handleClick);
this.canvas.addEventListener('touchend', this.handleTouch);
}
private handleClick = (event: MouseEvent): void => {
const rect = this.canvas.getBoundingClientRect();
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
this.checkIntersection();
};
private handleTouch = (event: TouchEvent): void => {
if (event.changedTouches.length === 0) return;
const touch = event.changedTouches[0];
const rect = this.canvas.getBoundingClientRect();
this.mouse.x = ((touch.clientX - rect.left) / rect.width) * 2 - 1;
this.mouse.y = -((touch.clientY - rect.top) / rect.height) * 2 + 1;
this.checkIntersection();
};
private checkIntersection(): void {
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObjects(
this.hotspotGroup.children,
true
);
if (intersects.length > 0) {
const hotspotData = intersects[0].object.userData['hotspot'] as Hotspot;
if (hotspotData) {
this.onHotspotClick(hotspotData);
}
}
}
}
// Misurazione interattiva: calcolo distanza tra due punti sulla sfera
export class MeasurementTool {
private measurePoints: THREE.Vector3[] = [];
private measureLines: THREE.Line[] = [];
addMeasurePoint(yaw: number, pitch: number, realWorldRadius: number): void {
const point = this.sphericalToCartesian(yaw, pitch, 1);
this.measurePoints.push(point);
if (this.measurePoints.length === 2) {
const [p1, p2] = this.measurePoints;
// Angolo tra i due vettori in radianti
const angle = p1.angleTo(p2);
// Distanza approssimativa in metri (richiede calibrazione con dati reali)
const estimatedDistance = angle * realWorldRadius * 2;
console.log(`Distanza stimata: ${estimatedDistance.toFixed(2)} m`);
this.measurePoints = [];
}
}
private sphericalToCartesian(yaw: number, pitch: number, radius: number): THREE.Vector3 {
const phi = (90 - pitch) * (Math.PI / 180);
const theta = yaw * (Math.PI / 180);
return new THREE.Vector3(
radius * Math.sin(phi) * Math.cos(theta),
radius * Math.cos(phi),
radius * Math.sin(phi) * Math.sin(theta)
);
}
}
Sunucu Tarafı Görüntü İşleme Hattı
360° görüntülerin kalitesi deneyimin temelidir. Bir Node.js işlem hattı uygulayalım ki gibi kameralardan gelen ham görüntüleri otomatik olarak optimize eder Ricoh Teta Z1 veya Insta360 X4Uyarlanabilir akış için birden fazla çözünürlük üretiyor.
import sharp from 'sharp';
import path from 'path';
import fs from 'fs/promises';
interface ProcessingConfig {
inputPath: string;
outputDir: string;
qualities: Array<{ width: number; suffix: string; quality: number }>;
}
export async function processPanoramaImage(config: ProcessingConfig): Promise<void> {
const { inputPath, outputDir, qualities } = config;
await fs.mkdir(outputDir, { recursive: true });
const baseName = path.basename(inputPath, path.extname(inputPath));
// Genera versioni multiple per progressive loading
for (const quality of qualities) {
const outputPath = path.join(outputDir, `${baseName}_${quality.suffix}.webp`);
await sharp(inputPath)
.resize(quality.width, quality.width / 2, {
fit: 'fill',
kernel: 'lanczos3',
})
.webp({
quality: quality.quality,
effort: 6, // Bilanciamento velocità/compressione
lossless: false,
})
.toFile(outputPath);
console.log(`Generated: ${outputPath} (${quality.width}x${quality.width / 2})`);
}
// Genera thumbnail per la mappa del piano
await sharp(inputPath)
.resize(400, 200, { fit: 'fill' })
.webp({ quality: 70 })
.toFile(path.join(outputDir, `${baseName}_thumb.webp`));
}
// Configurazione per produzione
const productionConfig: ProcessingConfig = {
inputPath: './raw/living-room.jpg',
outputDir: './processed/living-room',
qualities: [
{ width: 1024, suffix: 'low', quality: 65 }, // ~200KB - caricamento iniziale
{ width: 4096, suffix: 'med', quality: 80 }, // ~1.5MB - qualità standard
{ width: 8192, suffix: 'high', quality: 90 }, // ~5MB - massima qualità
],
};
VR/AR için WebXR entegrasyonu
Entegrasyon WebXR API'si uyumlu kulaklıklara sahip kullanıcılara izin verir (Meta Quest, Kendinizi tamamen mülkün içine kaptırmak için tarayıcı aracılığıyla Apple Vision Pro, HTC Vive). Ekleme ve Yerel oluşturucu desteği sayesinde Three.js ile şaşırtıcı derecede basit.
// Abilitazione WebXR nel renderer Three.js
export function enableWebXR(
renderer: THREE.WebGLRenderer,
container: HTMLElement
): void {
renderer.xr.enabled = true;
// Verifica supporto WebXR nel browser
if ('xr' in navigator) {
navigator.xr?.isSessionSupported('immersive-vr').then(supported => {
if (supported) {
// Mostra pulsante "Entra in VR"
const vrButton = createVRButton(renderer);
container.appendChild(vrButton);
}
});
}
}
function createVRButton(renderer: THREE.WebGLRenderer): HTMLButtonElement {
const button = document.createElement('button');
button.textContent = 'Visita in VR';
button.style.cssText = `
position: absolute;
bottom: 20px;
right: 20px;
padding: 12px 24px;
background: #0066cc;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
`;
button.addEventListener('click', async () => {
try {
const session = await navigator.xr!.requestSession('immersive-vr', {
optionalFeatures: ['local-floor', 'bounded-floor'],
});
await renderer.xr.setSession(session);
button.textContent = 'Uscita VR';
} catch (err) {
console.error('Errore avvio sessione VR:', err);
}
});
return button;
}
// Nel loop di animazione, gestisci sia VR che non-VR
function animate(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.PerspectiveCamera): void {
renderer.setAnimationLoop(() => {
// In VR: il renderer gestisce automaticamente i due occhi
// Fuori VR: rendering standard
renderer.render(scene, camera);
});
}
API'nin Yerleştirilmesi ve Listeleme Platformlarıyla Entegrasyon
Benimsetmeyi en üst düzeye çıkarmak için izleyicinin herhangi bir platforma kolayca entegre olması gerekir basit bir HTML etiketi veya iframe aracılığıyla mevcut. Custom Elements API ile standart bir Web Bileşeni oluşturalım.
// Web Component per embedding universale
class RealEstateTourElement extends HTMLElement {
private viewer: PanoramaViewer | null = null;
static get observedAttributes(): string[] {
return ['tour-id', 'api-key', 'start-room'];
}
connectedCallback(): void {
this.render();
this.initTour();
}
disconnectedCallback(): void {
this.viewer?.dispose();
}
private render(): void {
this.innerHTML = `
<div style="width:100%;height:100%;position:relative;">
<div id="tour-container" style="width:100%;height:100%;"></div>
<div id="tour-overlay" style="position:absolute;top:0;left:0;width:100%;height:100%;
background:black;opacity:0;pointer-events:none;"></div>
<div id="tour-controls" style="position:absolute;bottom:16px;left:16px;"></div>
</div>
`;
}
private async initTour(): Promise<void> {
const tourId = this.getAttribute('tour-id');
const apiKey = this.getAttribute('api-key');
if (!tourId || !apiKey) return;
const response = await fetch(
`https://api.realestate-tours.com/v1/tours/${tourId}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const tour: VirtualTour = await response.json();
const container = this.querySelector('#tour-container') as HTMLElement;
this.viewer = new PanoramaViewer(container);
const startRoom = tour.rooms.find(r => r.id === tour.startRoomId)!;
await this.viewer.loadRoom(startRoom);
}
}
// Registrazione come Custom Element
customElements.define('realestate-tour', RealEstateTourElement);
// Utilizzo in qualsiasi HTML:
// <realestate-tour tour-id="prop-12345" api-key="your-key" style="height:500px"></realestate-tour>
Performans Optimizasyonu
Performans kritik öneme sahiptir: Yüklenmesi 3 saniyeden uzun süren bir tur, kullanıcıların %53'ünü kaybeder (Google tarafından verilmiştir). İşte temel stratejiler.
Optimizasyon Stratejileri
- Uyarlanabilir akış dokusu: önce düşük çözünürlüğü yükleyin (200KB), ardından yüksek çözünürlüğü (5MB) değiştirin
- Akıllı önyükleme: boşta kalma süresi boyunca mevcut odadan ulaşılabilen odaları önceden yükler
- Geri dönüşlü WebP: WebP, aynı kalitedeki JPEG'e kıyasla %30-35 oranında azaltır
- Kenar önbelleğe alma özellikli CDN: görüntüleri coğrafi olarak dağıtın (Cloudflare Images, AWS CloudFront)
- LOD (Ayrıntı Düzeyi): DevicePixelRatio'ya göre mobil cihazlarda daha düşük çözünürlük
- GPU örneklemesi: birden fazla etkin nokta için ayrı Sprite'lar yerine InstancedMesh'i kullanın
- requestIdleCallback: önyüklemeyi yalnızca tarayıcı boştayken gerçekleştir
// Selezione risoluzione adattiva basata su dispositivo
function getOptimalResolution(
devicePixelRatio: number,
connectionType: string
): 'low' | 'med' | 'high' {
// Navigator Connection API (dove disponibile)
const connection = (navigator as any).connection;
const effectiveType = connection?.effectiveType ?? '4g';
if (effectiveType === '2g' || effectiveType === 'slow-2g') {
return 'low';
}
if (devicePixelRatio <= 1 || effectiveType === '3g') {
return 'med';
}
return 'high';
}
// Utilizzo
const resolution = getOptimalResolution(
window.devicePixelRatio,
''
);
const panoramaUrl = room.panoramaUrl.replace('.webp', `_${resolution}.webp`);
İş Metrikleri ve Analitikleri
Sanal turların gerçek değeri somut verilerle ölçülür. Bir analitik sistemi entegre ediyoruz Gayrimenkul satış ekipleri için temel ölçümleri takip eden, tura özel.
| Metrik | Tanım | Karşılaştırmalar |
|---|---|---|
| Tur Tamamlanma Oranı | En az 3 odayı ziyaret eden kullanıcıların yüzdesi | >%45 |
| Ortalama Oturum Süresi | Ortalama sanal ziyaret süresi | 4-8 dakika |
| Hotspot Etkileşim Oranı | Oturum başına tıklanan etkin nokta yüzdesi | >%30 |
| Potansiyel Müşteri Dönüşümü | İletişimle sonuçlanan oturumların yüzdesi | %8-15 |
| Fiziki Ziyaretlerin Azaltılması | Niteliksiz fiziksel ziyaretlerin azaltılması | %50-60 |
Uyarı: Telif Hakkı Görselleri 360
Panoramik görseller telif haklarına tabidir. Sözleşmeye dayalı anlaşmalarınız olduğundan emin olun materyali üreten fotoğrafçılar/ajanslarla net bir şekilde anlaşın. Kullanım şartlarına dahildir görüntülerin indirilmesi ve yeniden dağıtılmasıyla ilgili platformda açık sınırlamalar. Teknik olarak düşük çözünürlüklü filigranlar uygular ve WebGL tuvaline sağ tıklamayı devre dışı bırakır.
Sonuçlar ve Sonraki Adımlar
Standart web teknolojilerini temel alan eksiksiz bir sanal tur sistemi oluşturduk: WebGL/Three.js işleme için, evrensel yerleştirme için Özel Öğeler, optimizasyon için Sharp ardışık düzeni görüntüler. Bu yaklaşım, pahalı SaaS satıcılarına olan bağımlılığı ortadan kaldırır ve tam kullanıcı deneyiminin kişiselleştirilmesi.
Bir sonraki doğal adım, tarafından oluşturulan fotogerçekçi 3D modellerle entegrasyondur. Gauss Sıçraması o NeRF (Nöral Parlaklık Alanları), Basitten tam 3D rekonstrüksiyonlarla 2025-2026'da gayrimenkul görselleştirmesinde devrim yaratacak akıllı telefon ile video.







