Why can't I define constants right at the beginning in wgslFn?

I have a question out of curiosity. Why can’t I define global constants right at the beginning in wgslFn?

After the main function it works. But if subfunctions and main functions depend on constants I have to define them twice. This doesn’t hurt me now, it works but it doesn’t seem right to me.

@Sunag: if you are also here in the forum, thanks for the textureStore function. This now completely replaces the renderTargets.
The compute texture example is perfect. I understood it straight away.

export const initialSpectrumWGSL = wgslFn(`

	fn computeWGSL(
		storageTex: texture_storage_2d<rgba8unorm, write>,
		index: u32,
		resolution: f32,
		time: f32,
		L: f32,
		wind: vec2<f32>
	) -> void {

		let posX = index % u32(resolution);
		let posY = index / u32(resolution);
		let indexUV = vec2u( posX, posY );
		let uv = vec2f( f32( posX ) / resolution, f32( posY ) / resolution );

                  

		const PI: f32 = 3.141592653;
		const g: f32 = 9.81;
		const KM: f32 = 370;
		const CM: f32 = 0.23;

                        

		let K: vec2<f32> = 2 * PI * (uv - vec2<f32>(0.5)) / L;
		let k: f32 = length(K);
		let l_wind: f32 = length(wind);
		let Omega: f32 = 0.84;
		let kp: f32 = g * square(Omega/l_wind);
		let c: f32 = omega(k)/k;
		let cp: f32 = omega(kp)/kp;
		let Lpm: f32 = exp(-1.25 * square(kp/k));
		let gamma: f32 = 1.7;
		let sigma: f32 = 0.08 * (1 + 4*pow(Omega, -3));
		let Gamma: f32 = exp(-square(sqrt(k/kp) - 1)/2 * square(sigma));
		let Jp: f32 = pow(gamma, Gamma);
		let Fp: f32 = Lpm * Jp * exp(-Omega/sqrt(10) * (sqrt(k/kp) - 1));
		let alphap: f32 = 0.006*sqrt(Omega);
		let Bl: f32 = 0.5*alphap*cp/c*Fp;
		let z0: f32 = 0.000037 * square(l_wind)/g*pow(l_wind/cp, 0.9);
		let uStar: f32 = 0.41*l_wind/log(10.0/z0);
		let alpham: f32 = alphaM(uStar);
		let Fm: f32 = exp(-0.25*square(k/KM-1.0));
		let Bh: f32 = 0.5*alpham*CM/c*Fm*Lpm;
		let a0: f32 = log(2.0)/4.0;
		let am: f32 = 0.13*uStar/CM;
		let Delta: f32 = tanH(a0+4.0*pow(c/cp, 2.5)+am*pow(CM/c, 2.5));
		let cosPhi: f32 = dot(normalize(wind), normalize(K));
		let S: f32 = (1.0/(2.0*PI))*pow(k,-4.0)*(Bl+Bh)*(1.0+Delta*(2.0*cosPhi*cosPhi-1.0));
		let dk: f32 = 2.0*PI/L;
		let h: f32 = spectrum( S, K, dk );

		textureStore( storageTex, indexUV, vec4f( h, 0, 0, 0 ) );
	}

             
	const PI: f32 = 3.141592653;
	const g: f32 = 9.81;
	const KM: f32 = 370;
	const CM: f32 = 0.23;


	fn square( x: f32 ) -> f32 {
		return x * x;
	}

	fn omega( k: f32 ) -> f32 {
		return sqrt(g * k * (1 + square(k / KM)));
	}

	fn tanH( x: f32 ) -> f32 {
		return (1 - exp(-2 * x)) / (1 + exp(-2 * x));
	}

	fn alphaM( uStar: f32 ) -> f32 {
		if(uStar < CM){
			return 0.01 * (1.0+log(uStar/CM));
		}
		else{
			return 0.01 * (1.0+3.0*log(uStar/CM));
		}
	}

	fn spectrum( S: f32, K: vec2<f32>, dk: f32 ) -> f32 {
		if(K.x == 0 && K.y == 0){
			return 0;
		}
		else{
			return sqrt(S/2.0)*dk;
		}
	}
`);
2 Likes

Hi, hoisting are enabled in WGSL so it wouldn’t be a problem if you put your constants below the initial (main) function, e.g:

fn myFunc() -> f32 {

	return myConst;

}

const myConst : f32 = 1.0;

You can also use snippet, which is code that is not pre-processed and will be embedded in the final code of your shader, e.g:

// import { wgslFn, code } from 'three/nodes';
// ...

const snippet = code( `
const myConst : f32 = 1.0;
` );

// wgslFn( code, [ ...includes ] )
const myFunc = wgslFn( `
fn myFunc() -> f32 {
	return myConst;
}
`, [ snippet ] );

This is useful when using the same code (snippet) in more than one function.

3 Likes