|
@@ -0,0 +1,226 @@
|
|
|
|
+<html lang="en">
|
|
|
|
+ <head>
|
|
|
|
+ <title>three.js - webgpu - tsl editor</title>
|
|
|
|
+ <meta charset="utf-8">
|
|
|
|
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
|
+ <link type="text/css" rel="stylesheet" href="main.css">
|
|
|
|
+ </head>
|
|
|
|
+ <body>
|
|
|
|
+
|
|
|
|
+ <style>
|
|
|
|
+ #source {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 50%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ }
|
|
|
|
+ #result {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ right: 0;
|
|
|
|
+ width: 50%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ }
|
|
|
|
+ #renderer {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ right: 50%;
|
|
|
|
+ width: 200px;
|
|
|
|
+ height: 200px;
|
|
|
|
+ z-index: 100;
|
|
|
|
+ pointer-events: none;
|
|
|
|
+ }
|
|
|
|
+ </style>
|
|
|
|
+
|
|
|
|
+ <div id="source"></div>
|
|
|
|
+ <div id="result"></div>
|
|
|
|
+ <div id="renderer"></div>
|
|
|
|
+
|
|
|
|
+ <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
|
|
|
+
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs/loader.min.js"></script>
|
|
|
|
+
|
|
|
|
+ <script type="importmap">
|
|
|
|
+ {
|
|
|
|
+ "imports": {
|
|
|
|
+ "three": "../build/three.module.js",
|
|
|
|
+ "three/addons/": "./jsm/",
|
|
|
|
+ "three/nodes": "./jsm/nodes/Nodes.js"
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ </script>
|
|
|
|
+
|
|
|
|
+ <script type="module">
|
|
|
|
+
|
|
|
|
+ import * as THREE from 'three';
|
|
|
|
+ import * as Nodes from 'three/nodes';
|
|
|
|
+
|
|
|
|
+ import WebGPU from 'three/addons/capabilities/WebGPU.js';
|
|
|
|
+ import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
|
|
|
|
+ import WGSLNodeBuilder from 'three/addons/renderers/webgpu/nodes/WGSLNodeBuilder.js';
|
|
|
|
+
|
|
|
|
+ import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
|
|
|
+
|
|
|
|
+ init();
|
|
|
|
+
|
|
|
|
+ function init() {
|
|
|
|
+
|
|
|
|
+ if ( WebGPU.isAvailable() === false ) {
|
|
|
|
+
|
|
|
|
+ document.body.appendChild( WebGPU.getErrorMessage() );
|
|
|
|
+
|
|
|
|
+ throw new Error( 'No WebGPU support' );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // add the depedencies
|
|
|
|
+
|
|
|
|
+ const width = 200;
|
|
|
|
+ const height = 200;
|
|
|
|
+
|
|
|
|
+ const camera = new THREE.PerspectiveCamera( 70, width / height, 0.1, 10 );
|
|
|
|
+ camera.position.z = .72;
|
|
|
|
+ camera.lookAt( 0, 0, 0 );
|
|
|
|
+
|
|
|
|
+ const scene = new THREE.Scene();
|
|
|
|
+ scene.background = new THREE.Color( 0x222222 );
|
|
|
|
+
|
|
|
|
+ const rendererDOM = document.getElementById( 'renderer' );
|
|
|
|
+
|
|
|
|
+ const renderer = new WebGPURenderer();
|
|
|
|
+ renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
|
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
|
+ renderer.setSize( 200, 200 );
|
|
|
|
+ rendererDOM.appendChild( renderer.domElement );
|
|
|
|
+
|
|
|
|
+ const material = new Nodes.NodeMaterial();
|
|
|
|
+ material.outputNode = Nodes.vec4( 0, 0, 0, 1 );
|
|
|
|
+
|
|
|
|
+ const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material );
|
|
|
|
+ scene.add( mesh );
|
|
|
|
+
|
|
|
|
+ renderer.setAnimationLoop( () => {
|
|
|
|
+
|
|
|
|
+ renderer.render( scene, camera );
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ // editor
|
|
|
|
+
|
|
|
|
+ window.require.config( { paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@latest/min/vs' } } );
|
|
|
|
+
|
|
|
|
+ require( [ 'vs/editor/editor.main' ], () => {
|
|
|
|
+
|
|
|
|
+ const options = { shader: 'fragment', outputColorSpace: THREE.LinearSRGBColorSpace };
|
|
|
|
+
|
|
|
|
+ let timeout = null;
|
|
|
|
+ let nodeBuilder = null;
|
|
|
|
+
|
|
|
|
+ const editorDOM = document.getElementById( 'source' );
|
|
|
|
+ const resultDOM = document.getElementById( 'result' );
|
|
|
|
+
|
|
|
|
+ const tslCode = `// Example: Desaturate texture
|
|
|
|
+
|
|
|
|
+const { texture, uniform, vec4 } = TSL;
|
|
|
|
+
|
|
|
|
+//const samplerTexture = new THREE.Texture();
|
|
|
|
+const samplerTexture = new THREE.TextureLoader().load( './textures/uv_grid_opengl.jpg' );
|
|
|
|
+
|
|
|
|
+// label is optional
|
|
|
|
+const myMap = texture( samplerTexture ).rgb.label( 'myTexture' );
|
|
|
|
+const myColor = uniform( new THREE.Color( 0x0066ff ) ).label( 'myColor' );
|
|
|
|
+const opacity = .7;
|
|
|
|
+
|
|
|
|
+const desaturatedMap = myMap.rgb.saturation( 0 ); // try add .temp( 'myVar' ) after saturation()
|
|
|
|
+
|
|
|
|
+const finalColor = desaturatedMap.add( myColor );
|
|
|
|
+
|
|
|
|
+output = vec4( finalColor, opacity );
|
|
|
|
+`;
|
|
|
|
+
|
|
|
|
+ const editor = window.monaco.editor.create( editorDOM, {
|
|
|
|
+ value: tslCode,
|
|
|
|
+ language: 'javascript',
|
|
|
|
+ theme: 'vs-dark',
|
|
|
|
+ automaticLayout: true
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ const result = window.monaco.editor.create( resultDOM, {
|
|
|
|
+ value: '',
|
|
|
|
+ language: 'wgsl',
|
|
|
|
+ theme: 'vs-dark',
|
|
|
|
+ automaticLayout: true,
|
|
|
|
+ readOnly: true
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ const showCode = () => {
|
|
|
|
+
|
|
|
|
+ result.setValue( nodeBuilder[ options.shader + 'Shader' ] );
|
|
|
|
+ result.revealLine( 1 );
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const build = () => {
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+
|
|
|
|
+ const tslCode = `let output = null;\n${ editor.getValue() }\nreturn { output };`;
|
|
|
|
+ const nodes = new Function( 'THREE', 'TSL', tslCode )( THREE, Nodes );
|
|
|
|
+
|
|
|
|
+ mesh.material.outputNode = nodes.output;
|
|
|
|
+ mesh.material.needsUpdate = true;
|
|
|
|
+
|
|
|
|
+ nodeBuilder = new WGSLNodeBuilder( mesh, renderer );
|
|
|
|
+ nodeBuilder.build();
|
|
|
|
+
|
|
|
|
+ showCode();
|
|
|
|
+
|
|
|
|
+ // extra debug info
|
|
|
|
+
|
|
|
|
+ /*const style = 'background-color: #333; color: white; font-style: italic; border: 2px solid #777; font-size: 22px;';
|
|
|
|
+
|
|
|
|
+ console.log( '%c [ WGSL ] Vertex Shader ', style );
|
|
|
|
+ console.log( nodeBuilder.vertexShader );
|
|
|
|
+ console.log( '%c [ WGSL ] Fragment Shader ', style );
|
|
|
|
+ console.log( nodeBuilder.fragmentShader );*/
|
|
|
|
+
|
|
|
|
+ } catch ( e ) {
|
|
|
|
+
|
|
|
|
+ result.setValue( 'Error: ' + e.message );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ build();
|
|
|
|
+
|
|
|
|
+ editor.getModel().onDidChangeContent( () => {
|
|
|
|
+
|
|
|
|
+ if ( timeout ) clearTimeout( timeout );
|
|
|
|
+
|
|
|
|
+ timeout = setTimeout( build, 1000 );
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ // gui
|
|
|
|
+
|
|
|
|
+ const gui = new GUI();
|
|
|
|
+
|
|
|
|
+ gui.add( options, 'shader', [ 'vertex', 'fragment' ] ).onChange( showCode );
|
|
|
|
+
|
|
|
|
+ gui.add( options, 'outputColorSpace', [ THREE.LinearSRGBColorSpace, THREE.SRGBColorSpace ] ).onChange( ( value ) => {
|
|
|
|
+
|
|
|
|
+ renderer.outputColorSpace = value;
|
|
|
|
+
|
|
|
|
+ build();
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ } );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ </script>
|
|
|
|
+ </body>
|
|
|
|
+</html>
|