Newbie: How to pass 2 numbers down to fragementShader

I decided to try shaders and learn with Youtube videos where images are represented / mapped to via particles. My image has a 1920 by 1080 resolution, 2 values which I would like to use in the fragment shader. From what I (think to have) learned to far is:

  1. in the THREE.ShaderMaterial define the values as a uniform:

     // app.js
     this.material = new THREE.ShaderMaterial({
         uniforms: {
             t1: { type: "t", value: this.textures[0] },
             aTotalParticles: { type: "f", value: this.totalParticles },
             aParticlesX: { type: "f", value: this.particlesX },
             aParticlesY: { type: "f", value: this.particlesY },
         },
         fragmentShader: fragment,
         vertexShader: vertex
     });
    
     this.geometry = new THREE.BufferGeometry();
     this.positions = new THREE.BufferAttribute(new Float32Array(this.totalParticles * 3), 3)
     this.coordinates = new THREE.BufferAttribute(new Float32Array(this.totalParticles * 3), 3)
    
     let index = 0;
     for (let i = 0; i < this.particlesX; i++) {
         for (let j = 0; j < this.particlesY; j++) {
           ...
         }            
     }
    
     this.geometry.setAttribute( 'position', this.positions );
     this.geometry.setAttribute( 'aCoordinates', this.coordinates );
    
  2. in the VertexShader.glsl import those values as attributes and assign them to varyings:

     // vertextShader.glsl
     attribute vec2 aCoordinates;
     attribute float aTotalParticles;
     attribute float aParticlesX;
     attribute float aParticlesY;
    
     varying vec2 vUv;
     varying vec2 vCoordinates;
     varying float vTotalParticles;
     varying float vParticlesX;
     varying float vParticlesY;
     
     void main()
     {
         vUv = uv;
         vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
     
         gl_PointSize = 2000. * (1. / - mvPosition.z);
         gl_Position = projectionMatrix * mvPosition;
     
         vCoordinates = aCoordinates.xy;
         vTotalParticles = aTotalParticles;
         vTotalParticles = aTotalParticles;
         vParticlesX = aParticlesX;
         vParticlesY = aParticlesY;
     }
    
  3. and eventually in the fragmentShader import those values the varyings again (same name):

     // fragmentShader.glsl
     varying vec2 vCoordinates;
     varying float vTotalParticles;
     varying float vParticlesX;
     varying float vParticlesY;
     uniform sampler2D t1;
     
     void main()
     {
         vec2 myUV = vec2(vCoordinates.x / vParticlesX, vCoordinates.y / vParticlesY);
         vec4 image  = texture2D(t1, myUV);
         gl_FragColor = image;
     }
    

However, the fragmentShader calculates only one color that all particles receive. If I use floats directly

    vec2 myUV = vec2(vCoordinates.x / 1920., vCoordinates.y / 1080.);

the output is what is expected.

What am I doing wrong in my script?

1 Like
 attribute float aParticlesX;
 attribute float aParticlesY;

should be

 uniform float aParticlesX;
 uniform float aParticlesY;

and you can reference the uniforms directly in the fragment shader… you don’t have to pass them as varyings since they aren’t going to change (“vary”) across the triangle… since they are coming from a uniform. (which is applied (“uniformly”) to the whole drawcall)

2 Likes

Wow that was quick!!! And it works! That’s awesome!!!
Thank you VERY much, @manthrax !! Very much appreciated!

I think I will need some time to find out why these can be uniform, while other values have to be attributes…

1 Like

Attributes can be different for each vertex… hence why its an array of values…

Uniforms apply to the whole draw call… so they are a single value… or a single vec3/4 or a single mat4

1 Like