threejs(13)-shader set point material

Shader material built-in variables

The built-in variables of the three.js shader are

  1. gl_PointSize: In point rendering mode, controls the rendering pixel size of the square point area (note that this is the pixel size, not the three.js unit, so when the camera is moved, the size of the point seen on the screen remains unchanged)
  2. gl_Position: Controls the position of selected vertices
  3. gl_FragColor: RGB color value of the fragment
  4. gl_FragCoord: The coordinates of the fragment, also in pixels
  5. gl_PointCoord: In point rendering mode, corresponds to square pixel coordinates

They either appear in the shader individually or in groups and are the soul of the shader. Let’s talk about their meaning and usage respectively.

  1. gl_PointSize

The gl_PointSize built-in variable is a float type. In point rendering mode, since the vertex is a point, theoretically we cannot see it, so it is represented as a square facing the camera. The built-in variable gl_PointSize is mainly used to set the pixel size of the square surface rendered by the vertices (the default value is 0).

void main() {<!-- --> gl_PointSize = 10.0; }
  1. gl_Position

The gl_Position built-in variable is a vec4 type, which represents the vertex position coordinates that are ultimately passed into the fragment shader to be used for fragmentation. vec4(x,y,z,1.0), the first three parameters represent the xyz coordinate value of the vertex, and the fourth parameter is a floating point number 1.0.

void main() {<!-- --> gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }
  1. gl_FragColor

The gl_FragColor built-in variable is of type vec4, which is mainly used to set the color of the fragment pixel. Its first three parameters represent the fragment pixel color value RGB, and the fourth parameter
Is the fragment pixel transparency A, 1.0 means opaque, 0.0 means completely transparent.

void main() {<!-- --> gl_FragColor = vec4(1.0,0.0,0.0,1.0); }
  1. gl_FragCoord

The gl_FragCoord built-in variable is of type vec2, which represents the coordinates of all fragments or pixels rendered by WebGL on the canvas. The origin of the coordinates is the upper left corner of the canvas. The x-axis is horizontally to the right, and the y is vertically downward. The unit of gl_FragCoord coordinates is a pixel, and the value of gl_FragCoord is vec2(x,y). The vertical and horizontal coordinates of the fragment coordinates can be accessed respectively through gl_FragCoord.x and gl_FragCoord.y. I borrowed a picture here

Let’s give an example below

fragmentShader: `
    void main() {<!-- -->
        if(gl_FragCoord.x < 600.0) {<!-- -->
            gl_FragColor = vec4(1.0,0.0,0.0,1.0);
        } else {<!-- -->
            gl_FragColor = vec4(1.0,1.0,0.0,1.0);
        }
    }
`


Here, 600 pixels is used as the boundary. The part where the x value is less than 600 pixels, the material is rendered red, and the part greater than 600 pixels is rendered yellow.

  1. gl_PointCoord

The gl_PointCoord built-in variable is also of vec2 type, which also represents the coordinates of the pixel, but unlike gl_FragCoord, gl_FragCoord calculates the x value from [0, width] and the y value from [0, height] based on the entire canvas. gl_PointCoord is effective in point rendering mode, and its range corresponds to the small square surface, also from the upper left corner [0,0] to the lower right corner [1,1].

  1. Built-in variable exercises

We have roughly talked about the five built-in variables. Let’s use a small case to try out the other four except gl_FragCoord. First picture,

var planeGeom = new THREE.PlaneGeometry(1000, 1000, 100, 100);
uniforms = {<!-- -->
    time: {<!-- -->
        value: 0
    }
}
var planeMate = new THREE.ShaderMaterial({<!-- -->
    transparent: true,
    side: THREE.DoubleSide,
    uniforms: uniforms,
    vertexShader: `
                uniform float time;
        void main() {<!-- -->
            float y = sin(position.x / 50.0 + time) * 10.0 + sin(position.y / 50.0 + time) * 10.0;
            vec3 newPosition = vec3(position.x, position.y, y * 2.0);
            gl_PointSize = (y + 20.0) / 4.0;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
        }
    `,
    fragmentShader: `
        void main() {<!-- -->
            float r = distance(gl_PointCoord, vec2(0.5, 0.5));
            if(r < 0.5) {<!-- -->
                gl_FragColor = vec4(0.0,1.0,1.0,1.0);
            }
        }
    `
})
var planeMesh = new THREE.Points(planeGeom, planeMate);
planeMesh.rotation.x = - Math.PI / 2;
scene.add(planeMesh);

Case-Nebula


src/main/main.js

import * as THREE from "three";

import {<!-- --> OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import fragmentShader from "../shader/basic/fragmentShader.glsl";
import vertexShader from "../shader/basic/vertexShader.glsl";
// Goal: Create a rotating galaxy
//Initialize scene
const scene = new THREE.Scene();

//Create a perspective camera
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerHeight / window.innerHeight,
  0.1,
  1000
);
//Set camera position
// object3d has position, and the attribute is a 3-dimensional vector
camera.aspect = window.innerWidth / window.innerHeight;
//Update the camera's projection matrix
camera.updateProjectionMatrix();
camera.position.set(0, 0, 5);
scene.add(camera);

//Add auxiliary axis to help us view the 3D coordinate axis
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);

//Import texture
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('textures/particles/10.png');
const texture1 = textureLoader.load('textures/particles/9.png');
const texture2 = textureLoader.load('textures/particles/11.png');

let geometry=null;
let points=null;

//Set the parameters of the galaxy
const params = {<!-- -->
  count: 1000,
  size: 0.1,
  radius: 5,
  branches: 4,
  spin: 0.5,
  color: "#ff6030",
  outColor: "#1b3984",
};

//GalaxyColor
let galaxyColor = new THREE.Color(params.color);
let outGalaxyColor = new THREE.Color(params.outColor);
let material;
const generateGalaxy = () => {<!-- -->
  // If these vertices already exist, release the memory first and then delete the vertex data
  if (points !== null) {<!-- -->
    geometry.dispose();
    material.dispose();
    scene.remove(points);
  }
  // Generate vertex geometry
  geometry = new THREE.BufferGeometry();
  // Randomly generate location
  const positions = new Float32Array(params.count * 3);
  const colors = new Float32Array(params.count * 3);

  const scales = new Float32Array(params.count);

  //Pattern attributes
  const imgIndex = new Float32Array(params.count)

  // Loop generation point
  for (let i = 0; i < params.count; i + + ) {<!-- -->
    const current = i * 3;

    // Calculate the angle of the branch = (calculate which branch the current point is on)*(2*Math.PI/how many branches)
    const branchAngel =
      (i % params.branches) * ((2 * Math.PI) / params.branches);

    const radius = Math.random() * params.radius;
    //The further away from the center of the circle, the greater the degree of rotation
    // const spinAngle = radius * params.spin;

    // Randomly set x/y/z offset values
    const randomX =
      Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;
    const randomY =
      Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;
    const randomZ =
      Math.pow(Math.random() * 2 - 1, 3) * 0.5 * (params.radius - radius) * 0.3;

    //Set the x value coordinate of the current point
    positions[current] = Math.cos(branchAngel) * radius + randomX;
    //Set the y value coordinate of the current point
    positions[current + 1] = randomY;
    //Set the z value coordinate of the current point
    positions[current + 2] = Math.sin(branchAngel) * radius + randomZ;

    const mixColor = galaxyColor.clone();
    mixColor.lerp(outGalaxyColor, radius / params.radius);

    //Set color
    colors[current] = mixColor.r;
    colors[current + 1] = mixColor.g;
    colors[current + 2] = mixColor.b;



    //The size of the vertices
    scales[current] = Math.random();

    // Set different patterns according to the index value;
    imgIndex[current] = i%3;
  }
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
  geometry.setAttribute("aScale", new THREE.BufferAttribute(scales, 1));
  geometry.setAttribute("imgIndex", new THREE.BufferAttribute(imgIndex, 1));
  //Set the point shader material
  material = new THREE.ShaderMaterial({<!-- -->
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    
    transparent: true,
    vertexColors: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
    uniforms: {<!-- -->
      uTime: {<!-- -->
        value: 0,
      },
      uTexture:{<!-- -->
        value:texture
      },
      uTexture1:{<!-- -->
        value:texture1
      },
      uTexture2:{<!-- -->
        value:texture2
      },
      uTime:{<!-- -->
        value:0
      },
      uColor:{<!-- -->
        value:galaxyColor
      }

    },
  });

  //generate point
  points = new THREE.Points(geometry, material);
  scene.add(points);
  console.log(points);
  // console.log(123);
};

generateGalaxy()



//Initialize renderer
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;

//Set the rendering size
renderer.setSize(window.innerWidth, window.innerHeight);

// Monitor screen size changes and set the rendering size
window.addEventListener("resize", () => {<!-- -->
  // console.log("resize");
  //Update camera
  camera.aspect = window.innerWidth / window.innerHeight;
  //Update the camera's projection matrix
  camera.updateProjectionMatrix();

  //Update renderer
  renderer.setSize(window.innerWidth, window.innerHeight);
  //Set the pixel ratio of the renderer
  renderer.setPixelRatio(window.devicePixelRatio);
});



//Add renderer to body
document.body.appendChild(renderer.domElement);

//Initialize controller
const controls = new OrbitControls(camera, renderer.domElement);
//Set controller damping
controls.enableDamping = true;
// //Set automatic rotation
// controls.autoRotate = true;

const clock = new THREE.Clock();

function animate(t) {<!-- -->
  // controls.update();
  const elapsedTime = clock.getElapsedTime();
  material.uniforms.uTime.value = elapsedTime;
  requestAnimationFrame(animate);
  // Use the renderer to render the camera to see the content of this scene rendered.
  renderer.render(scene, camera);
}

animate();

src/shader/basic/fragmentShader.glsl


varying vec2 vUv;

uniform sampler2D uTexture;
uniform sampler2D uTexture1;
uniform sampler2D uTexture2;
varying float vImgIndex;
varying vec3 vColor;
void main(){<!-- -->
    
    // gl_FragColor = vec4(gl_PointCoord,0.0,1.0);

    //Set gradient circle
    // float strength = distance(gl_PointCoord,vec2(0.5)); // Distance from point to center
    // strength*=2.0;
    // strength = 1.0-strength;
    // gl_FragColor = vec4(strength);

    // round point
    // float strength = 1.0-distance(gl_PointCoord,vec2(0.5));
    // strength = step(0.5,strength);
    // gl_FragColor = vec4(strength);

    //Set the pattern according to the texture
    // vec4 textureColor = texture2D(uTexture,gl_PointCoord);
    // gl_FragColor = vec4(textureColor.rgb,textureColor.r);
    vec4 textureColor;
    if(vImgIndex==0.0){<!-- -->
       textureColor = texture2D(uTexture,gl_PointCoord);
    }else if(vImgIndex==1.0){<!-- -->
       textureColor = texture2D(uTexture1,gl_PointCoord);
    }else{<!-- -->
       textureColor = texture2D(uTexture2,gl_PointCoord);
    }
    

    gl_FragColor = vec4(vColor,textureColor.r);
    

}

src/shader/basic/vertexShader.glsl

varying vec2 vUv;

attribute float imgIndex;
attribute float aScale;
varying float vImgIndex;

uniform float uTime;

varying vec3 vColor;
void main(){<!-- -->
    vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
    

    // Get the angle of the fixed point
    float angle = atan(modelPosition.x,modelPosition.z);
    // Get the distance from the vertex to the center
    float distanceToCenter = length(modelPosition.xz);
    // Set the rotation offset degree based on the distance from the vertex to the center
    float angleOffset = 1.0/distanceToCenter*uTime;
    //The current degree of rotation
    angle + =angleOffset;

    modelPosition.x = cos(angle)*distanceToCenter;
    modelPosition.z = sin(angle)*distanceToCenter;

    vec4 viewPosition = viewMatrix*modelPosition;
    gl_Position = projectionMatrix * viewPosition;

    //Set the point size
    // gl_PointSize = 100.0; // size of point
    //Determine whether to use the camera based on the z coordinate of the viewPosition
    gl_PointSize =200.0/-viewPosition.z*aScale; //The size of the point
    vUv = uv;
    vImgIndex=imgIndex;
    vColor = color;
}