Texture
Perlin noise will provide the random values needed for the effect. We load a Perlin noise texture and pass it into the fragment shader.
const textureLoader = new THREE . TextureLoader ( ) ; const texture = textureLoader . load ( config . material . textureURL ) ; const material = new THREE . ShaderMaterial ( { uniforms : { uTexture : new THREE . Uniform ( texture ) , } , fragmentShader : ` uniform sampler2D uTexture; ... ` , ... } ) ;
Fragment UVs
To sample the texture, we need the UV coords of each fragment. We can pass the coords from the vertex to the fragment shader using a varying. To confirm it's working, we use the UVs as the fragment's red and green value — resulting in a green-red gradient. To see the effect, set state: uv in the controls.
const material = new THREE . ShaderMaterial ( { vertexShader : ` varying vec2 vUv; void main() { // Final position gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); // Varyings vUv = uv; } ` , fragmentShader : ` varying vec2 vUv; void main() { vec2 textureUv = vUv; gl_FragColor = vec4(vUv, 0.0, 1.0); ... } ` , } ) ;
Map texture to geometry
To map the texture onto the material, we use texture(uTexture, textureUv).r; . It reads the texture's color value at the given coordinates using the same coordinate space as the fragment shader:
Passing the current fragment's UV coordinates to texture() lets us map the texture onto the material pixel by pixel. The function returns a normalized RGBA value. Since the texture is grayscale, all three color channels are equal, so we only need the red channel ( .r ). We then assign it to the fragment's color:
fragmentShader : ` uniform sampler2D uTexture; varying vec2 vUv; void main() { vec2 textureUv = vUv; // Texture float textureImpl = texture(uTexture, textureUv).r; gl_FragColor = vec4(textureImpl, textureImpl, textureImpl, 1.0); ... `
... continue reading