1+ < html lang ="en ">
2+ < head >
3+ < title > three.js webgpu - centroid sampling</ title >
4+ < meta charset ="utf-8 ">
5+ < meta name ="viewport " content ="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0 ">
6+ < link type ="text/css " rel ="stylesheet " href ="main.css ">
7+ </ head >
8+ < style >
9+ body {
10+ margin : 0 ;
11+ overflow : hidden;
12+ width : 100vw ;
13+ height : 100vh ;
14+ }
15+
16+ # demo {
17+ display : flex;
18+ flex-direction : row;
19+ align-items : center;
20+ }
21+
22+ .renderer-wrapper {
23+ display : flex;
24+ flex-direction : column;
25+ align-items : center;
26+ }
27+
28+ # antialising-disabled {
29+ border-right : 1px solid black;
30+ }
31+
32+ canvas {
33+ width : 100% ;
34+ height : 100% ;
35+ }
36+ </ style >
37+ < body >
38+ < div id ="demo ">
39+ < div id ="antialising-disabled " class ="renderer-wrapper ">
40+ < div > antialising disabled</ div >
41+ </ div >
42+ < div id ="antialising-enabled " class ="renderer-wrapper ">
43+ < div > antialising enabled</ div >
44+ </ div >
45+ </ div >
46+
47+ < script type ="importmap ">
48+ {
49+ "imports" : {
50+ "three" : "../build/three.webgpu.js" ,
51+ "three/webgpu" : "../build/three.webgpu.js" ,
52+ "three/tsl" : "../build/three.tsl.js" ,
53+ "three/addons/" : "./jsm/"
54+ }
55+ }
56+ </ script >
57+
58+ < script type ="module ">
59+
60+ import * as THREE from 'three' ;
61+ import { varying , uv , texture , Fn } from 'three/tsl' ;
62+
63+ import { GUI } from 'three/addons/libs/lil-gui.module.min.js' ;
64+
65+ let rendererAntialiasingEnabled ;
66+ let rendererAntialiasingDisabled ;
67+ let camera ;
68+ let scene ;
69+ let gui ;
70+
71+ const effectController = {
72+ sampling : 'normal'
73+ } ;
74+
75+ const atlasCanvas = document . createElement ( 'canvas' ) ;
76+ atlasCanvas . width = 16 ;
77+ atlasCanvas . height = 16 ;
78+
79+ const ctx = atlasCanvas . getContext ( '2d' ) ;
80+ ctx . fillStyle = 'red' ;
81+ ctx . fillRect ( 0 , 0 , 8 , 8 ) ;
82+
83+ const redUVs = [ 0 , 1 , 0.5 , 1 , 0.5 , 0.5 , 0 , 0.5 ] ;
84+ ctx . fillStyle = 'green' ;
85+ ctx . fillRect ( 8 , 0 , 8 , 8 ) ;
86+
87+ const greenUVs = [ 1 , 1 , 0.5 , 1 , 0.5 , 0.5 , 1 , 0.5 ] ;
88+
89+ ctx . fillStyle = 'blue' ;
90+ ctx . fillRect ( 0 , 8 , 8 , 8 ) ;
91+
92+ const blueUVs = [ 0 , 0 , 0.5 , 0 , 0.5 , 0.5 , 0 , 0.5 ] ;
93+
94+ ctx . fillStyle = 'yellow' ;
95+ ctx . fillRect ( 8 , 8 , 8 , 8 ) ;
96+
97+ const yellowUVs = [ 1 , 0 , 0.5 , 0 , 0.5 , 0.5 , 1 , 0.5 ] ;
98+
99+ const faces = [ redUVs , greenUVs , blueUVs , yellowUVs ] ;
100+
101+ const canvasTexture = new THREE . CanvasTexture ( atlasCanvas ) ;
102+ canvasTexture . colorSpace = THREE . SRGBColorSpace ;
103+ canvasTexture . mapping = THREE . UVMapping ;
104+ canvasTexture . wrapS = THREE . RepeatWrapping ;
105+ canvasTexture . wrapT = THREE . RepeatWrapping ;
106+ canvasTexture . magFilter = THREE . NearestFilter ;
107+ canvasTexture . minFilter = THREE . NearestFilter ;
108+ canvasTexture . format = THREE . RGBAFormat ;
109+ canvasTexture . type = THREE . UnsignedByteType ;
110+
111+ const forceWebGL = false ;
112+
113+ init ( ) ;
114+
115+ function init ( ) {
116+
117+ camera = new THREE . PerspectiveCamera ( ) ;
118+ camera . fov = 60 ;
119+ camera . near = 1 ;
120+ camera . far = 2100 ;
121+ camera . position . z = 50 ;
122+
123+ scene = new THREE . Scene ( ) ;
124+
125+ const makeFaceGeometry = ( uvs ) => {
126+
127+ const geometry = new THREE . BufferGeometry ( ) ;
128+ const positions = [ - 1 , - 1 , 0 , 1 , - 1 , 0 , 1 , 1 , 0 , - 1 , 1 , 0 ] ;
129+ geometry . setAttribute (
130+ 'position' ,
131+ new THREE . BufferAttribute ( new Float32Array ( positions ) , 3 )
132+ ) ;
133+
134+ const indices = [ 0 , 1 , 2 , 2 , 3 , 0 ] ;
135+ geometry . setIndex ( indices ) ;
136+
137+ geometry . setAttribute (
138+ 'uv' ,
139+ new THREE . BufferAttribute ( new Float32Array ( uvs ) , 2 )
140+ ) ;
141+
142+ return geometry ;
143+
144+ } ;
145+
146+ const material = new THREE . MeshBasicNodeMaterial ( ) ;
147+ const testUV = varying ( uv ( ) , 'testUV' ) ;
148+
149+ const createShader = ( type , sampling ) => {
150+
151+ return Fn ( ( ) => {
152+
153+ testUV . setInterpolation ( type , sampling ) ;
154+
155+ return texture ( canvasTexture , testUV ) . rgb ;
156+
157+ } ) ;
158+
159+ } ;
160+
161+ const withFlatFirstShader = createShader ( 'flat' , 'first' ) ;
162+ const withFlatEitherShader = createShader ( 'flat' , 'either' ) ;
163+
164+ const withSampleShader = Fn ( ( ) => {
165+
166+ testUV . setInterpolation ( THREE . InterpolationSamplingType . PERSPECTIVE , THREE . InterpolationSamplingMode . SAMPLE ) ;
167+
168+ return texture ( canvasTexture , testUV ) . rgb ;
169+
170+ } ) ;
171+
172+ const withInterpolationShader = Fn ( ( ) => {
173+
174+ testUV . setInterpolation ( THREE . InterpolationSamplingType . PERSPECTIVE , THREE . InterpolationSamplingMode . CENTROID ) ;
175+
176+ return texture ( canvasTexture , testUV ) . rgb ;
177+
178+ } ) ;
179+
180+ const withoutInterpolationShader = Fn ( ( ) => {
181+
182+ return texture ( canvasTexture , uv ( ) ) . rgb ;
183+
184+ } ) ;
185+
186+ material . colorNode = withoutInterpolationShader ( ) ;
187+
188+ const faceMeshes = [ ] ;
189+
190+ for ( let x = - 5 ; x < 5 ; x ++ ) {
191+
192+ for ( let y = - 5 ; y < 5 ; y ++ ) {
193+
194+ const face = faces [ Math . floor ( Math . random ( ) * faces . length ) ] ;
195+ const geometry = makeFaceGeometry ( face ) ;
196+ const mesh = new THREE . Mesh ( geometry , material ) ;
197+ mesh . position . set ( x * 2 , y * 2 , 0 ) ;
198+ faceMeshes . push ( mesh ) ;
199+ scene . add ( mesh ) ;
200+
201+ }
202+
203+ }
204+
205+ // Create Standard Renderer
206+ rendererAntialiasingDisabled = new THREE . WebGPURenderer ( {
207+ antialias : false ,
208+ forceWebGL : forceWebGL
209+ } ) ;
210+
211+ rendererAntialiasingDisabled . setPixelRatio ( window . devicePixelRatio ) ;
212+ rendererAntialiasingDisabled . setSize ( window . innerWidth / 2 , window . innerHeight ) ;
213+ rendererAntialiasingDisabled . setAnimationLoop ( animateStandard ) ;
214+
215+ // Create antialiased renderer
216+ rendererAntialiasingEnabled = new THREE . WebGPURenderer ( {
217+ antialias : true ,
218+ forceWebGL : forceWebGL
219+ } ) ;
220+
221+ document . body . querySelector ( '#antialising-enabled' ) . appendChild ( rendererAntialiasingEnabled . domElement ) ;
222+ rendererAntialiasingEnabled . setPixelRatio ( window . devicePixelRatio ) ;
223+ rendererAntialiasingEnabled . setSize ( window . innerWidth / 2 , window . innerHeight ) ;
224+ rendererAntialiasingEnabled . setAnimationLoop ( animateAliased ) ;
225+
226+ document . body . querySelector ( '#antialising-disabled' ) . appendChild ( rendererAntialiasingDisabled . domElement ) ;
227+ document . body . querySelector ( '#antialising-disabled' ) . appendChild ( rendererAntialiasingDisabled . domElement ) ;
228+
229+ onWindowResize ( ) ;
230+
231+ window . addEventListener ( 'resize' , onWindowResize ) ;
232+
233+ gui = new GUI ( ) ;
234+ gui . add ( effectController , 'sampling' , [
235+ THREE . InterpolationSamplingMode . NORMAL ,
236+ THREE . InterpolationSamplingMode . CENTROID ,
237+ THREE . InterpolationSamplingMode . SAMPLE ,
238+ THREE . InterpolationSamplingMode . FLAT_FIRST ,
239+ THREE . InterpolationSamplingMode . FLAT_EITHER
240+ ] ) . onChange ( ( ) => {
241+
242+ const interpolationShaderLib = {
243+ [ THREE . InterpolationSamplingMode . NORMAL ] : withoutInterpolationShader ,
244+ [ THREE . InterpolationSamplingMode . CENTROID ] : withInterpolationShader ,
245+ [ THREE . InterpolationSamplingMode . SAMPLE ] : withSampleShader ,
246+ [ THREE . InterpolationSamplingMode . FLAT_FIRST ] : withFlatFirstShader ,
247+ [ THREE . InterpolationSamplingMode . FLAT_EITHER ] : withFlatEitherShader
248+ } ;
249+
250+ const shader = interpolationShaderLib [ effectController . sampling ] ;
251+
252+ for ( let i = 0 ; i < faceMeshes . length ; i ++ ) {
253+
254+ faceMeshes [ i ] . material . colorNode = shader ( ) ;
255+ faceMeshes [ i ] . material . needsUpdate = true ;
256+
257+ }
258+
259+
260+ } ) ;
261+
262+ }
263+
264+ function onWindowResize ( ) {
265+
266+ const halfWidth = window . innerWidth / 2 ;
267+ rendererAntialiasingDisabled . setSize ( halfWidth , window . innerHeight ) ;
268+ rendererAntialiasingEnabled . setSize ( halfWidth , window . innerHeight ) ;
269+ const aspect = ( halfWidth ) / window . innerHeight ;
270+
271+ camera . aspect = aspect ;
272+ camera . updateProjectionMatrix ( ) ;
273+
274+ }
275+
276+ function animateStandard ( ) {
277+
278+ rendererAntialiasingDisabled . render ( scene , camera ) ;
279+
280+ }
281+
282+ function animateAliased ( ) {
283+
284+ rendererAntialiasingEnabled . render ( scene , camera ) ;
285+
286+ }
287+
288+ </ script >
289+ </ body >
290+ </ html >
0 commit comments