How do I change vertex positions with the positionNode using wgslFn? I added a codePen example

import {ShaderNode, MeshBasicNodeMaterial, texture, color, vec2, vec3, vec4, wgslFn, tslFn, uv, attribute, uniform, storage, positionWorld, normalLocal, normalWorld } from './three-defs.js';

I’m trying to change vertex positions with wgslFn (WebGPU-shading-language-function). With tslFn I manage that but with wgsl I have a situation that I don’t understand yet. Here is my test code.

const textureLoader = new THREE.TextureLoader();
const vertexTexture = textureLoader.load( '../resources/textures/displacement.jpg' );				

this.dt = 0.01;

const WGSLPosition = wgslFn(`
	fn WGSLPosition( 
		vTex: texture_2d<f32>, 
		vTex_sampler: sampler,
		uv: vec2<f32>,
		time: f32,
		position: vec3<f32>
	) -> vec4<f32> {
		return vec4<f32>( position * 1.5, 1.0 );				
	}
`);		
		
this.wgslShaderParams = {
	vTex: texture( vertexTexture ),
	vTex_sampler: texture( vertexTexture ),
	uv: uv(),
	time: uniform(0),
	position: attribute('position'),
}

const material = new MeshBasicNodeMaterial();
material.colorNode = WGSLPosition(this.wgslShaderParams);
//When I include material.positionNode I see nothing but the code is running
//material.positionNode = WGSLPosition(this.wgslShaderParams);  
			
const geometry = new THREE.BoxGeometry();
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);

With material.positionNode commented out, I see this:

The camera is at (0, 0, 5) the cube has an edge length of 1. So I see exactly what I want to see. The wgslFn works and I see the vertex positions with the colorNode in color depending on their positions. So reading the attributes works.
If I now set the wgslFn to the positionNode, I expect to see the same thing with a cube that is 1.5 times larger. But as soon as I include the material.positionNode I see nothing but the code is running.

The example is very simple but I can’t figure out where my thinking is wrong.

Does anyone recognize my thinking error?

1 Like

This is all so new, are you familiar with Tour of WGSL

WGSL tutorials are good but WGSL is not the problem. A description of how I use wgslFn with the positionNode would help, but I can’t find that in any WGSL description, since the wgslFn is a 3js specific function.
I understood the webgpu materials example from 3js. I think that’s very good, because I learned a lot about the material node system from there. I understand the tslFn and wgslFn examples contained therein.
In the materials example, however, there are no examples with tslFn and wgslFn in connection with the positionNode for displacement applications. So at the moment I can only try or hope that someone knows something.

2 Likes

I have a webgpu codePen example here:

const textureLoader = new THREE.TextureLoader();
const vertexTexture = textureLoader.load( '../resources/textures/displacement.jpg' );


const WGSLPosition = wgslFn(`
	fn WGSLPosition(
		displacementTexture: texture_2d<f32>,
		displacementSampler: sampler,
		time: f32,
		position: vec3<f32>,
	) -> vec3<f32> {

		return vec3<f32>(position *2);						
	}
`);

this.wgslShaderParams = {
	displacementTexture: texture(vertexTexture),
	displacementSampler: texture(vertexTexture),
	time: uniform(0),
	position: attribute('position'),
}

const material = new MeshBasicNodeMaterial();
material.colorNode = WGSLPosition(this.wgslShaderParams);
material.positionNode = WGSLPosition(this.wgslShaderParams);

const geometry = new THREE.BoxGeometry();
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);

In this case, the cube disappears. Only when I comment out the positionNode line do I see the cube again. Since I have also assigned the wgsl function to the colorNode to check that the wgsl function works.

Without

material.positionNode = WGSLPosition(this.wgslShaderParams);

1 Like

Errormessage:

Tint WGSL reader failure: :49:45 error: unresolved identifier ‘nodeUniform0_sampler’
nodeVarying0 = WGSLPosition( nodeUniform0, nodeUniform0_sampler, NodeUniforms.nodeUniform1, position ).xyz;

In the error message I see that the variables are considered as varyings. I imagine the way the node system works like this:

If this wgsl code is addressed to the colorNode, then it is automatically interpreted as a fragmentShader and all variables are seen as uniforms. In my opinion the textureSampler in wgsl is still the same as in webgl. By that I mean textureSamplers have always been uniform variables. That’s the reason why it works with the colorNode.

If the wgsl code is now addressed to the positionNode, then it must be interpreted as a vertexShader. And according to the error message, all variables are treated as varyings in order to be able to transfer them to the fragmentShader after the vertexShader is done. This works like in webgl with all scalar variables, vectors and attributes. For this purpose, varyings are declared in webgl and in the main function in the shader you simply assign the attribute variable to the varying variable. Attribute variables like position, normal, uv, are all vectors.

`
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
attribute int custom;

varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
varying int vCustom;

main(){

   vPosition = position;
   vNormal = normal;
   vUv = uv;
   vCustom = custom;

}
`

But I have never experienced in webgl that one try to assign a textureSampler to a varying variable in the vertexShader. Textures are always read in via uniforms in the fragmentShader and/or in the vertexShader itself. Now, when the wgsl code, considered by the positionNode as a vertexShader and tries to pass the textureSampler to the fragmentShader via varying, it has to crash.

If my theory about the error is correct then it is currently not possible to use textures in wgslFn in connection with the positionNode. The node system therefore needs a way of distinguishing between variables that are only to be used in the vertexShader itself and those that are to be sent to the fragmentShader via varyings. A simple way I imagine it is like this:

const WGSLPosition = wgslFn(`
   fn WGSLPosition(
      displacementTexture: texture_2d<f32>,
      displacementSampler: sampler,
      times: f32,
      location: vec3<f32>,
   ) -> vec4<f32> {

      return vec4<f32>(position *2, 1.0);
   }
`);


this.wgslShaderParams = {
   displacementTexture: texture(vertexTexture),
   displacementSampler: texture(vertexTexture),
   time: uniform(0),
   position: attribute('position'),
   vPosition: varying(position),
}

Thus, the node system would always know exactly how to handle a variable.
But that’s just a spontaneous thought. I’m not a 3js developer and I don’t want to presume to say what would be best.

I just hope to be able to use textures soon in connection with the positionNode because I also need the coordinates of the neighboring vertices for displacement calculations and I only have access to them with a dataTexture in the shader.

The 3js team is doing a great job and I hope all of this here can be helpfull

1 Like