mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.7k stars 35.37k forks source link

Noise algorithm in WebGPURenderer - performance concerns #29830

Closed Makio64 closed 2 days ago

Makio64 commented 6 days ago

Description

I like to use different kind of noise in my projects and i optimized them a lot for this purpose like ashima snoise : https://www.shadertoy.com/view/4sdGD8

When I translate it to tsl using the tsl-translator ( https://threejs.org/examples/?q=tsl%20to#webgpu_tsl_editor ) it give me something like :

// Three.js Transpiler r170

import { vec3, mod, Fn, vec2, floor, step, float, sub, int, dot, max, fract, abs, pow, mul } from 'three/tsl';

export const permute = /*#__PURE__*/ Fn( ( [ x_immutable ] ) => {

    const x = vec3( x_immutable ).toVar();

    return mod( x.mul( 34. ).add( 1. ).mul( x ), 289. );

} ).setLayout( {
    name: 'permute',
    type: 'vec3',
    inputs: [
        { name: 'x', type: 'vec3', qualifier: 'in' }
    ]
} );

export const snoise = /*#__PURE__*/ Fn( ( [ v_immutable ] ) => {

    const v = vec2( v_immutable ).toVar();
    const i = vec2( floor( v.x.add( v.y ).mul( .36602540378443 ).add( v ) ) ).toVar(), x0 = vec2( i.x.add( i.y ).mul( .211324865405187 ).add( v.sub( i ) ) ).toVar();
    const s = float( step( x0.x, x0.y ) ).toVar();
    const j = vec2( sub( 1.0, s ), s ).toVar(), x1 = vec2( x0.sub( j ).add( .211324865405187 ) ).toVar(), x3 = vec2( x0.sub( .577350269189626 ) ).toVar();
    i.assign( mod( i, 289. ) );
    const p = vec3( permute( permute( i.y.add( vec3( int( 0 ), j.y, int( 1 ) ) ) ).add( i.x ).add( vec3( int( 0 ), j.x, int( 1 ) ) ) ) ).toVar(), m = vec3( max( .5.sub( vec3( dot( x0, x0 ), dot( x1, x1 ), dot( x3, x3 ) ) ), 0. ) ).toVar(), x = vec3( fract( p.mul( .024390243902439 ) ).mul( 2. ).sub( 1. ) ).toVar(), h = vec3( abs( x ).sub( .5 ) ).toVar(), a0 = vec3( x.sub( floor( x.add( .5 ) ) ) ).toVar();

    return .5.add( mul( 65., dot( pow( m, vec3( 4. ) ).mul( - 0.85373472095314.mul( a0.mul( a0 ).add( h.mul( h ) ) ).add( 1.79284291400159 ) ), a0.mul( vec3( x0.x, x1.x, x3.x ) ).add( h.mul( vec3( x0.y, x1.y, x3.y ) ) ) ) ) );

} ).setLayout( {
    name: 'snoise',
    type: 'float',
    inputs: [
        { name: 'v', type: 'vec2', qualifier: 'in' }
    ]
} );

Solution

I was wondering if this seems ok to you in terms of performance ?

Also I was wondering if Three have a current system to check the output of the shaders ?

Alternatives

RawShaderNodeMaterial( gslVertexShader, gslFragmentShader, wgslVertexShader, wgslFragmentShader )

Additional context

No response

Mugen87 commented 6 days ago

Also I was wondering if Three have a current system to check the output of the shaders ?

For a given mesh, you can query vertex and fragment shader like so:

console.log( await renderer.debug.getShaderAsync( scene, camera, mesh ) );

getShaderAsync() gives you an object holding the vertex and fragment shader string for the current configured backend. If you want to force GLSL output, you have to create the renderer via forceWebGL: true. Or use a browser that does not support WebGPU yet.

You might want to use the dev branch since there is a minor bug in r170 that breaks the method, see #29832.

Mugen87 commented 5 days ago

I was wondering if this seems ok to you in terms of performance ?

I don't see an obvious flaw.