Skip to content

Commit 32e12df

Browse files
cmhhelgesonsunag
andauthored
TSL: Introduce varying.setInterpolation() (#30582)
* init branch * add example and GLSL fallback * refine example * add screenshot * try new screenshot * try fix screenshot again * add centroid sampling to puppeteer exception list * rev * update example * updates --------- Co-authored-by: sunag <[email protected]>
1 parent 468f47a commit 32e12df

File tree

10 files changed

+444
-10
lines changed

10 files changed

+444
-10
lines changed

Diff for: examples/files.json

+1
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@
306306
"webgpu_camera",
307307
"webgpu_camera_array",
308308
"webgpu_camera_logarithmicdepthbuffer",
309+
"webgpu_centroid_sampling",
309310
"webgpu_clearcoat",
310311
"webgpu_clipping",
311312
"webgpu_compute_audio",

Diff for: examples/screenshots/webgpu_centroid_sampling.jpg

40.3 KB
Loading

Diff for: examples/webgpu_centroid_sampling.html

+290
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
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>

Diff for: src/constants.js

+46
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,32 @@ export const TimestampQuery = {
16091609
RENDER: 'render'
16101610
};
16111611

1612+
/**
1613+
* Represents mouse buttons and interaction types in context of controls.
1614+
*
1615+
* @type {ConstantsInterpolationSamplingType}
1616+
* @constant
1617+
*/
1618+
export const InterpolationSamplingType = {
1619+
PERSPECTIVE: 'perspective',
1620+
LINEAR: 'linear',
1621+
FLAT: 'flat'
1622+
};
1623+
1624+
/**
1625+
* Represents the different interpolation sampling modes.
1626+
*
1627+
* @type {ConstantsInterpolationSamplingMode}
1628+
* @constant
1629+
*/
1630+
export const InterpolationSamplingMode = {
1631+
NORMAL: 'normal',
1632+
CENTROID: 'centroid',
1633+
SAMPLE: 'sample',
1634+
FLAT_FIRST: 'flat first',
1635+
FLAT_EITHER: 'flat either'
1636+
};
1637+
16121638
/**
16131639
* This type represents mouse buttons and interaction types in context of controls.
16141640
*
@@ -1638,3 +1664,23 @@ export const TimestampQuery = {
16381664
* @property {string} COMPUTE - A `compute` timestamp query.
16391665
* @property {string} RENDER - A `render` timestamp query.
16401666
**/
1667+
1668+
/**
1669+
* Represents the different interpolation sampling types.
1670+
*
1671+
* @typedef {Object} ConstantsInterpolationSamplingType
1672+
* @property {string} PERSPECTIVE - Perspective-correct interpolation.
1673+
* @property {string} LINEAR - Linear interpolation.
1674+
* @property {string} FLAT - Flat interpolation.
1675+
*/
1676+
1677+
/**
1678+
* Represents the different interpolation sampling modes.
1679+
*
1680+
* @typedef {Object} ConstantsInterpolationSamplingMode
1681+
* @property {string} NORMAL - Normal sampling mode.
1682+
* @property {string} CENTROID - Centroid sampling mode.
1683+
* @property {string} SAMPLE - Sample-specific sampling mode.
1684+
* @property {string} FLAT_FIRST - Flat interpolation using the first vertex.
1685+
* @property {string} FLAT_EITHER - Flat interpolation using either vertex.
1686+
*/

Diff for: src/nodes/core/NodeBuilder.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1848,9 +1848,11 @@ class NodeBuilder {
18481848
* @param {(VaryingNode|PropertyNode)} node - The varying node.
18491849
* @param {?string} name - The varying's name.
18501850
* @param {string} [type=node.getNodeType( this )] - The varying's type.
1851+
* @param {?string} interpolationType - The interpolation type of the varying.
1852+
* @param {?string} interpolationSampling - The interpolation sampling type of the varying.
18511853
* @return {NodeVar} The node varying.
18521854
*/
1853-
getVaryingFromNode( node, name = null, type = node.getNodeType( this ) ) {
1855+
getVaryingFromNode( node, name = null, type = node.getNodeType( this ), interpolationType = null, interpolationSampling = null ) {
18541856

18551857
const nodeData = this.getDataFromNode( node, 'any' );
18561858

@@ -1863,7 +1865,7 @@ class NodeBuilder {
18631865

18641866
if ( name === null ) name = 'nodeVarying' + index;
18651867

1866-
nodeVarying = new NodeVarying( name, type );
1868+
nodeVarying = new NodeVarying( name, type, interpolationType, interpolationSampling );
18671869

18681870
varyings.push( nodeVarying );
18691871

0 commit comments

Comments
 (0)