How to design Parallax Bird From Codepen?

Share Your Love

In this post today I am covered another great design like Parallax Bird from Codepen, go through the post and see how it looks?

The “Animal Kingdom#CodePenChallenge got off to a soaring start with the week one prompt: birds! Check out our Challenge Collection, including ycw’s “parallax bird”.

In HTML:

//No Use Of HTML

In CSS:

canvas {
  display: block;
  width: 100vw;
  height: 100vh;
  cursor: grab;
}

In Javascript:

import * as THREE from "//cdn.skypack.dev/[email protected]";
import { OrbitControls } from "//cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls";

// ----
// canvas and context
// ----

const canvas = document.createElement('canvas');
document.body.append(canvas);

const gl = canvas.getContext('webgl2');
if (!gl) throw alert('webgl2 is required, bye');

// ----
// webgl program
// ----

const vsrc = `#version 300 es 
layout(location=0)in vec3 aPosition;
layout(location=1)in vec2 aTexcoord;
layout(location=2)in vec3 aTangent;
layout(location=3)in vec3 aNormal;
uniform mat4 uProj;
uniform mat4 uView;
uniform mat4 uModel;
uniform vec3 uViewPos;
out vec2 vTexcoord;
out vec3 vFragPos;
out vec3 vTsViewPos;
out vec3 vTsFragPos;
void main() {
  gl_Position = uProj * uView * uModel * vec4(aPosition, 1.);
  vFragPos = vec3(uModel * vec4(aPosition, 1.));
  vTexcoord = aTexcoord;
  // TBN 
  vec3 T = normalize(aTangent - dot(aNormal, aTangent) * aNormal);
  vec3 B = normalize(cross(aNormal, T)); // right-handed tangent space
  mat3 TBNinv = transpose(mat3(T, B, aNormal));
  // into TBN space 
  vTsViewPos = TBNinv * uViewPos;
  vTsFragPos = TBNinv * vFragPos;
}
`;

const fsrc = `#version 300 es
precision highp float;
out vec4 fColor;
in vec3 vFragPos;
in vec2 vTexcoord;
in vec3 vTsViewPos;
in vec3 vTsFragPos;
uniform sampler2D uHeightMap;

float depth(in vec2 st) {
  return 1. - texture(uHeightMap, st).r;
}

#define scale (.5)
#define minLayer (10.) // 0d
#define maxLayer (50.) // 90d 
vec2 f(vec2 texcoord, vec3 tsEyeToFrag) {
  float n = mix(maxLayer, minLayer, abs(dot(vec3(0.,0.,1.), tsEyeToFrag))); // n layers
  vec2 offsetPerLayer = tsEyeToFrag.xy * scale / n;
  float depthPerLayer = 1. / n;
  vec2 uv0 = texcoord;  // prev uv
  vec2 uv1 = texcoord;  // curr uv
  float d = 2.;
  float D = 0.;
  while (d > D) {
    uv0 = uv1;
    uv1 += offsetPerLayer;
    d = depth(uv1);
    D += depthPerLayer;
  }

  float d1 = abs(D-d); // dist from curr depth value to curr layer depth
  float d0 = abs((D-depthPerLayer) - depth(uv0)); // ditto, for prev  
  return mix(uv0, uv1, d0 / (d0 + d1)); // lerp
}

void main() {
  vec3 eye2frag = normalize(vTsFragPos - vTsViewPos);
  vec2 texcoord = f(vTexcoord, eye2frag);
  if(texcoord.x > 1. || texcoord.y > 1. || texcoord.x < 0. || texcoord.y < 0.)
    discard;
  fColor = texture(uHeightMap, texcoord);
}
`;

const prog = (() => {

  const vs = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(vs, vsrc);
  gl.compileShader(vs);
  if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
    throw new Error('VS: ' + gl.getShaderInfoLog(vs));
  }

  const fs = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(fs, fsrc);
  gl.compileShader(fs);
  if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
    throw new Error('FS: ' + gl.getShaderInfoLog(fs));
  }

  const prog = gl.createProgram();
  gl.attachShader(prog, vs);
  gl.attachShader(prog, fs);
  gl.linkProgram(prog);
  if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
    throw new Error('Prog: ' + gl.getProgramInfoLog(prog));
  }

  gl.deleteShader(vs);
  gl.detachShader(prog, vs);
  gl.deleteShader(fs);
  gl.detachShader(prog, fs);

  return prog;
})();

// ----
// locations
// ----

const loc = {
  aPosition: 0,
  aTexcoord: 1,
  aTangent: 2,
  aNormal: 3,
  uProj: gl.getUniformLocation(prog, 'uProj'),
  uView: gl.getUniformLocation(prog, 'uView'),
  uModel: gl.getUniformLocation(prog, 'uModel'),
  uViewPos: gl.getUniformLocation(prog, 'uViewPos'),
  uHeightMap: gl.getUniformLocation(prog, 'uHeightMap')
};

// ----
// attributes
// ----

const srcData = (() => {
  const _geom = new THREE.PlaneBufferGeometry();
  _geom.computeTangents();
  const geom = _geom.toNonIndexed();
  const aPosition = geom.attributes.position.array;
  const aTexcoord = geom.attributes.uv.array;
  const aTangent = geom.attributes.tangent.array; // vec4 ??????
  const aNormal = geom.attributes.normal.array;
  geom.dispose();
  return { aPosition, aTexcoord, aTangent, aNormal };
})();

const { vao, count } = (() => {
  const vao = gl.createVertexArray();
  gl.bindVertexArray(vao);

  const aPosition = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, aPosition);
  gl.bufferData(gl.ARRAY_BUFFER, srcData.aPosition, gl.STATIC_DRAW);
  gl.vertexAttribPointer(loc.aPosition, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(loc.aPosition);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  const aTexcoord = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, aTexcoord);
  gl.bufferData(gl.ARRAY_BUFFER, srcData.aTexcoord, gl.STATIC_DRAW);
  gl.vertexAttribPointer(loc.aTexcoord, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(loc.aTexcoord);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  const aTangent = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, aTangent);
  gl.bufferData(gl.ARRAY_BUFFER, srcData.aTangent, gl.STATIC_DRAW);
  gl.vertexAttribPointer(loc.aTangent, 3, gl.FLOAT, false, 4*4, 0);
  gl.enableVertexAttribArray(loc.aTangent);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  const aNormal = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, aNormal);
  gl.bufferData(gl.ARRAY_BUFFER, srcData.aNormal, gl.STATIC_DRAW);
  gl.vertexAttribPointer(loc.aNormal, 3, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(loc.aNormal);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  gl.bindVertexArray(null);
  return { vao, count: srcData.aPosition.length / 3 };
})();


const mesh = new THREE.Group();

// ----
// textures
// ----

(() => {
  const tex = gl.createTexture();
  const img = new Image();
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
  img.addEventListener('load', () => {
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, gl.RED, gl.UNSIGNED_BYTE, img);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT);
    gl.generateMipmap(gl.TEXTURE_2D);
  });
  img.addEventListener('error', e => {
    alert('failed to load img from unsplash, bye');
  });
  img.crossOrigin = '';
  img.src = 'https://images.unsplash.com/photo-1565569995015-414d4ef36690?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MTQ4fHxoZWFkJTIwYmlyZHxlbnwwfHwwfHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=60';
})();

// ----
// render
// ----

const camera = new THREE.PerspectiveCamera(50, 1, 0.1, 10);
camera.position.set(0, 0, 1);
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
const viewPos = new THREE.Vector3();

const render = () => {
  controls.update();
  
  gl.useProgram(prog);
  gl.uniform1i(loc.uHeightMap, 0);
  gl.uniformMatrix4fv(loc.uProj, false, camera.projectionMatrix.elements);
  gl.uniformMatrix4fv(loc.uView, false, camera.matrixWorldInverse.elements);
  gl.uniformMatrix4fv(loc.uModel, false, mesh.matrixWorld.elements);
  gl.uniform3fv(loc.uViewPos, camera.getWorldPosition(viewPos).toArray());
  
  gl.bindVertexArray(vao);
  gl.drawArrays(gl.TRIANGLES, 0, count);
  gl.bindVertexArray(null);
};

requestAnimationFrame(function f() {
  requestAnimationFrame(f);
  render();
});

// ----
// on resize view
// ----

window.addEventListener('resize', () => {
  canvas.width = innerWidth * devicePixelRatio | 0;
  canvas.height = innerHeight * devicePixelRatio | 0;
  gl.viewport(0, 0, canvas.width, canvas.height);
  camera.aspect = canvas.width / canvas.height;
  camera.updateProjectionMatrix();
});
dispatchEvent(new Event('resize'));

In Codepen:

See the Pen parallax bird by ycw (@ycw) on CodePen.

Please share and comment on this post and wants to improve add content on it WhatsApp us.

Share Your Love
Avatar photo
Lingaraj Senapati

Hey There! I am Lingaraj Senapati, the Founder of lingarajtechhub.com My skills are Freelance, Web Developer & Designer, Corporate Trainer, Digital Marketer & Youtuber.

Articles: 429

Newsletter Updates

Enter your email address below to subscribe to our newsletter