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.