How and when to declare shader variable, when not?

As a newbie to shaders I try find my way though this thick and badly signposted jungle. :slight_smile:

What I stumple upon time and again is why you can find so many shaders where not all variables were declared though they are no built-in variables of ThreeJS (three.js docs), see for example

vec4 mvPosition

at three.js examples

Why “mvPosition” is not defined outside “void main(){}” like:

varying vec4 mvPosition

… and then used inside just like (the other variables):

mvPosition

Does the reason for this relates with the fact that this variable is only used by the vertex shader?
So far, I could not find a clear 101 for shaders.

Any hints are welcome.

mvPosition is the vertex position in eye/view/camera space. This value is only necessary in the vertex shader. It many simple shaders you don’t see this intermediate value because gl_Position (the vertex position in clip space) is computed like so:

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

However, in the example mvPosition is also used to compute the point size.

Many thanks @Mugen87 for explanation.
Yes, I have understood what is for.
What I do not understand is why in this case it is not needed to declare type of “mvPosition” before "void … "

The variable mvPosition is only used in main() so I don’t think it would make sense to declare it outside of the function. Think of it like a local variable.

Yes.

varying variables are variables you setup in the vertex shader and that you want to use in the fragment shader: the GPU will interpolate those variables and will provide them in the fragment shader (provided you also declare them as varying in the fragment shader). varying is the way by which a vertex shader can pass variables to the fragment shader.

In your example, mvPosition is only used locally as a helper variable for some computation and need not be used in the fragment shader, so is not declared varying.

The stuff you declare outside of main:

  • attributes (vertex only, input)
  • uniforms (input)
  • varyings (pass values from vertex shader to fragment shader)
  • const values (for example the number PI)
  • functions

If there is no varying, it means the fragment shader is not using the vertex variable.

Think of it like this - you can have a million vertices in a point cloud, and you can have a million pixels on a screen.
The main() function will run individually on each vertex and each fragment. The input is the same though for each vertex and each fragment, same goes for all the listed types - const, function varying. mvPosition has a meaning, it’s model view position, but each vertex is going to compute it differently.
You may have only 3 vertices, representing a triangle, hell, even just one with gl_PointSize, but if you’re on a 1920x1200 screen you’re going to have much more pixels. So when your 6 vertices compute mvPosition the fragment shader is going to interpolate this value.

The millioin fragments you have on your screen, are all going to have a different value from the mvPosition varying, but it still originates from the same operation, only the 3 total vertices in the vertex shader.

Thank you very much, @Mugen87 @pailhead @Popov72!

Awesome, I had been glad if I would have found these hints in any shader 101.

Your hints help to get a better picture.
(Unfortunately, it is impossible to give 3 “solution” labels here. Imagine they have).