Tech News
← Back to articles

Cigarette smoke effect using shaders

read original related products more articles

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