Shader material built-in variables
The built-in variables of the three.js shader are
- 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)
- gl_Position: Controls the position of selected vertices
- gl_FragColor: RGB color value of the fragment
- gl_FragCoord: The coordinates of the fragment, also in pixels
- 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.
- 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; }
- 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 ); }
- 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); }
- 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.
- 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].
- 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; }