|
32 | 32 |
|
33 | 33 | import * as THREE from 'three/webgpu';
|
34 | 34 |
|
| 35 | + import { Fn, length, fract, vec4, positionWorld, smoothstep, max, abs, float, cameraPosition, clamp } from 'three/tsl'; |
| 36 | + |
35 | 37 | import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
36 | 38 |
|
37 | 39 | import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
|
|
90 | 92 | 'sheen_test.mtlx',
|
91 | 93 | ];
|
92 | 94 |
|
93 |
| - let camera, scene, renderer, prefab; |
| 95 | + let camera, scene, renderer; |
| 96 | + let controls, prefab; |
94 | 97 | const models = [];
|
95 | 98 |
|
96 | 99 | init();
|
|
100 | 103 | const container = document.createElement( 'div' );
|
101 | 104 | document.body.appendChild( container );
|
102 | 105 |
|
103 |
| - camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 50 ); |
104 |
| - camera.position.set( 0, 3, 20 ); |
105 |
| - |
106 |
| - scene = new THREE.Scene(); |
107 |
| - |
108 |
| - renderer = new THREE.WebGPURenderer( { antialias: true, alpha: true } ); |
| 106 | + renderer = new THREE.WebGPURenderer( { antialias: true } ); |
109 | 107 | renderer.setPixelRatio( window.devicePixelRatio );
|
110 | 108 | renderer.setSize( window.innerWidth, window.innerHeight );
|
111 | 109 | renderer.toneMapping = THREE.LinearToneMapping;
|
112 | 110 | renderer.toneMappingExposure = .5;
|
113 | 111 | renderer.setAnimationLoop( render );
|
114 | 112 | container.appendChild( renderer.domElement );
|
115 | 113 |
|
| 114 | + camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 200 ); |
| 115 | + camera.position.set( 10, 10, 20 ); |
| 116 | + |
| 117 | + scene = new THREE.Scene(); |
| 118 | + scene.background = new THREE.Color( 0xffffff ); |
| 119 | + |
| 120 | + // Ground plane |
| 121 | + |
| 122 | + const material = new THREE.MeshStandardNodeMaterial(); |
| 123 | + |
| 124 | + const gridXZ = Fn( ( [ gridSize = float( 1.0 ), dotWidth = float( 0.1 ), lineWidth = float( 0.02 ) ] ) => { |
| 125 | + |
| 126 | + const worldPos = positionWorld; |
| 127 | + const grid = fract( worldPos.xz.div( gridSize ) ); |
| 128 | + |
| 129 | + // Distance-based antialiasing |
| 130 | + const distToCamera = length( worldPos.sub( cameraPosition ) ); |
| 131 | + const smoothing = clamp( distToCamera.div( 100.0 ), 0.01, 0.02 ); |
| 132 | + |
| 133 | + // Create dots at cell centers |
| 134 | + const dotDist = length( grid.sub( 0.5 ) ); |
| 135 | + const dots = smoothstep( dotWidth.add( smoothing ), dotWidth.sub( smoothing ), dotDist ); |
| 136 | + |
| 137 | + // Create grid lines |
| 138 | + const lineX = smoothstep( lineWidth.add( smoothing ), lineWidth.sub( smoothing ), abs( grid.x.sub( 0.5 ) ) ); |
| 139 | + const lineZ = smoothstep( lineWidth.add( smoothing ), lineWidth.sub( smoothing ), abs( grid.y.sub( 0.5 ) ) ); |
| 140 | + const lines = max( lineX, lineZ ); |
| 141 | + |
| 142 | + return max( dots, lines ); |
| 143 | + |
| 144 | + } ); |
| 145 | + |
| 146 | + const radialGradient = Fn( ( [ radius = float( 10.0 ), falloff = float( 1.0 ) ] ) => { |
| 147 | + |
| 148 | + return smoothstep( radius, radius.sub( falloff ), length( positionWorld ) ); |
| 149 | + |
| 150 | + } ); |
| 151 | + |
| 152 | + // Create grid pattern |
| 153 | + const gridPattern = gridXZ( 1.0, 0.04, 0.01 ); |
| 154 | + const baseColor = vec4( 1.0, 1.0, 1.0, 0.0 ); |
| 155 | + const gridColor = vec4( 0.2, 0.2, 0.2, 1.0 ); |
| 156 | + |
| 157 | + // Mix base color with grid lines |
| 158 | + material.colorNode = gridPattern.mix( baseColor, gridColor ).mul( radialGradient( 30.0, 20.0 ) ); |
| 159 | + material.transparent = true; |
| 160 | + |
| 161 | + const plane = new THREE.Mesh( new THREE.CircleGeometry( 50 ), material ); |
| 162 | + plane.rotation.x = - Math.PI / 2; |
| 163 | + plane.renderOrder = - 1; |
| 164 | + scene.add( plane ); |
| 165 | + |
116 | 166 | //
|
117 | 167 |
|
118 |
| - const controls = new OrbitControls( camera, renderer.domElement ); |
| 168 | + controls = new OrbitControls( camera ); |
| 169 | + controls.connect( renderer.domElement ); |
| 170 | + controls.enableDamping = true; |
119 | 171 | controls.minDistance = 2;
|
120 | 172 | controls.maxDistance = 40;
|
121 | 173 |
|
|
127 | 179 |
|
128 | 180 | texture.mapping = THREE.EquirectangularReflectionMapping;
|
129 | 181 |
|
130 |
| - scene.background = texture; |
131 | 182 | scene.environment = texture;
|
132 | 183 |
|
133 | 184 | prefab = ( await new GLTFLoader().loadAsync( './models/gltf/ShaderBall.glb' ) ).scene;
|
|
154 | 205 |
|
155 | 206 | function updateModelsAlign() {
|
156 | 207 |
|
157 |
| - const COLUMN_COUNT = 8; |
| 208 | + const COLUMN_COUNT = 6; |
158 | 209 | const DIST_X = 3;
|
159 |
| - const DIST_Y = 4; |
| 210 | + const DIST_Z = 3; |
160 | 211 |
|
161 | 212 | const lineCount = Math.floor( models.length / COLUMN_COUNT ) - 1.5;
|
162 | 213 |
|
163 | 214 | const offsetX = ( DIST_X * ( COLUMN_COUNT - 1 ) ) * - .5;
|
164 |
| - const offsetY = ( DIST_Y * lineCount ) * .5; |
| 215 | + const offsetZ = ( DIST_Z * lineCount ) * .5; |
165 | 216 |
|
166 | 217 | for ( let i = 0; i < models.length; i ++ ) {
|
167 | 218 |
|
168 | 219 | const model = models[ i ];
|
169 | 220 |
|
170 | 221 | model.position.x = ( ( i % COLUMN_COUNT ) * DIST_X ) + offsetX;
|
171 |
| - model.position.y = ( Math.floor( i / COLUMN_COUNT ) * - DIST_Y ) + offsetY; |
| 222 | + model.position.z = ( Math.floor( i / COLUMN_COUNT ) * - DIST_Z ) + offsetZ; |
172 | 223 |
|
173 | 224 | }
|
174 | 225 |
|
|
197 | 248 | const previewMesh = model.getObjectByName( 'Preview_Mesh' );
|
198 | 249 | previewMesh.material = material;
|
199 | 250 |
|
| 251 | + if ( material.transparent ) { |
| 252 | + |
| 253 | + calibrationMesh.renderOrder = 1; |
| 254 | + previewMesh.renderOrder = 2; |
| 255 | + |
| 256 | + } |
| 257 | + |
200 | 258 | }
|
201 | 259 |
|
202 | 260 | function addGUI() {
|
|
247 | 305 |
|
248 | 306 | function render() {
|
249 | 307 |
|
| 308 | + controls.update(); |
250 | 309 | renderer.render( scene, camera );
|
251 | 310 |
|
252 | 311 | }
|
|
0 commit comments