A Pen by Christian Anderson on CodePen.
Created
October 1, 2021 17:16
-
-
Save techonomics69/dfbcda9e393d8877e4a0817bd7f79029 to your computer and use it in GitHub Desktop.
Portal scene - threejs-journey.xyz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<canvas class="webgl"></canvas> | |
<script id="firefliesVertexShader" type="x-shader/x-vertex"> | |
uniform float uPixelRatio; | |
uniform float uSize; | |
uniform float uTime; | |
attribute float aScale; | |
void main() { | |
vec4 modelPosition = modelMatrix * vec4(position, 1.0); | |
modelPosition.y += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2; | |
modelPosition.z += cos(uTime + modelPosition.x * 100.0) * aScale * 0.2; | |
modelPosition.x += cos(uTime + modelPosition.x * 100.0) * aScale * 0.2; | |
vec4 viewPosition = viewMatrix * modelPosition; | |
vec4 projectionPostion = projectionMatrix * viewPosition; | |
gl_Position = projectionPostion; | |
gl_PointSize = uSize * aScale * uPixelRatio; | |
gl_PointSize *= (1.0 / - viewPosition.z); | |
} | |
</script> | |
<script id="firefliesFragmentShader" type="x-shader/x-fragment"> | |
void main() { | |
float distanceToCenter = distance(gl_PointCoord, vec2(0.5)); | |
float strength = 0.05 / distanceToCenter - 0.1; | |
gl_FragColor = vec4(1.0, 1.0, 1.0, strength); | |
} | |
</script> | |
<script id="portalVertexShader" type="x-shader/x-vertex"> | |
varying vec2 vUv; | |
void main() { | |
vec4 modelPosition = modelMatrix * vec4(position, 1.0); | |
vec4 viewPosition = viewMatrix * modelPosition; | |
vec4 projectionPosition = projectionMatrix * viewPosition; | |
gl_Position = projectionPosition; | |
vUv = uv; | |
} | |
</script> | |
<script id="portalFragmentShader" type="x-shader/x-fragment"> | |
uniform float uTime; | |
uniform vec3 uColorStart; | |
uniform vec3 uColorEnd; | |
varying vec2 vUv; | |
// Classic Perlin 3D Noise | |
// by Stefan Gustavson | |
// | |
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);} | |
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} | |
vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);} | |
float cnoise(vec3 P){ | |
vec3 Pi0 = floor(P); // Integer part for indexing | |
vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 | |
Pi0 = mod(Pi0, 289.0); | |
Pi1 = mod(Pi1, 289.0); | |
vec3 Pf0 = fract(P); // Fractional part for interpolation | |
vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 | |
vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); | |
vec4 iy = vec4(Pi0.yy, Pi1.yy); | |
vec4 iz0 = Pi0.zzzz; | |
vec4 iz1 = Pi1.zzzz; | |
vec4 ixy = permute(permute(ix) + iy); | |
vec4 ixy0 = permute(ixy + iz0); | |
vec4 ixy1 = permute(ixy + iz1); | |
vec4 gx0 = ixy0 / 7.0; | |
vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5; | |
gx0 = fract(gx0); | |
vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); | |
vec4 sz0 = step(gz0, vec4(0.0)); | |
gx0 -= sz0 * (step(0.0, gx0) - 0.5); | |
gy0 -= sz0 * (step(0.0, gy0) - 0.5); | |
vec4 gx1 = ixy1 / 7.0; | |
vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5; | |
gx1 = fract(gx1); | |
vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); | |
vec4 sz1 = step(gz1, vec4(0.0)); | |
gx1 -= sz1 * (step(0.0, gx1) - 0.5); | |
gy1 -= sz1 * (step(0.0, gy1) - 0.5); | |
vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); | |
vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); | |
vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); | |
vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); | |
vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); | |
vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); | |
vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); | |
vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); | |
vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); | |
g000 *= norm0.x; | |
g010 *= norm0.y; | |
g100 *= norm0.z; | |
g110 *= norm0.w; | |
vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); | |
g001 *= norm1.x; | |
g011 *= norm1.y; | |
g101 *= norm1.z; | |
g111 *= norm1.w; | |
float n000 = dot(g000, Pf0); | |
float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); | |
float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); | |
float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); | |
float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); | |
float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); | |
float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); | |
float n111 = dot(g111, Pf1); | |
vec3 fade_xyz = fade(Pf0); | |
vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); | |
vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); | |
float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); | |
return 2.2 * n_xyz; | |
} | |
void main() { | |
// Displace UV by adding perlin nouise | |
vec2 displacedUv = vUv + cnoise(vec3(vUv * 7.0, uTime * 0.1)); | |
// Perlin noise | |
float strength = cnoise(vec3(displacedUv * 5.0, uTime * 0.2)); | |
// Outer glow | |
float outerGlow = distance(vUv, vec2(0.5)) * 5.0 - 1.4; | |
strength += outerGlow; | |
// Step | |
strength += step(-0.2, strength) * 0.8; | |
// Clamp | |
strength = clamp(strength, 0.0, 1.0); | |
// Final Color | |
vec3 color = mix(uColorStart, uColorEnd, strength); | |
gl_FragColor = vec4(color, 1.0); | |
} | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const portalVertexShader = document.getElementById("portalVertexShader") | |
.textContent; | |
const portalFragmentShader = document.getElementById("portalFragmentShader") | |
.textContent; | |
const firefliesVertexShader = document.getElementById("firefliesVertexShader") | |
.textContent; | |
const firefliesFragmentShader = document.getElementById( | |
"firefliesFragmentShader" | |
).textContent; | |
const debugObject = { | |
clearColor: "#1e2243", | |
portalColorStart: "#b91fac", | |
portalColorEnd: "#ffebf3" | |
}; | |
// Canvas | |
const canvas = document.querySelector("canvas.webgl"); | |
// Scene | |
const scene = new THREE.Scene(); | |
/** | |
* Loaders | |
*/ | |
// Texture loader | |
const textureLoader = new THREE.TextureLoader(); | |
// GLTF loader | |
const gltfLoader = new THREE.GLTFLoader(); | |
const bakedTexture = textureLoader.load( | |
"https://assets.codepen.io/22914/baked-02.jpg" | |
); | |
bakedTexture.encoding = THREE.sRGBEncoding; | |
/** | |
* Materials | |
*/ | |
// baked material | |
const bakedMaterial = new THREE.MeshBasicMaterial({ map: bakedTexture }); | |
// Do not flip the Y axes of the texture which is on by default for some reason | |
bakedTexture.flipY = false; | |
// Pole light material | |
const poleLightMaterial = new THREE.MeshBasicMaterial({ color: "#f0bf94" }); | |
// PortalLightMaterial | |
const portalLightMaterial = new THREE.ShaderMaterial({ | |
vertexShader: portalVertexShader, | |
fragmentShader: portalFragmentShader, | |
transparent: false, | |
blending: THREE.AdditiveBlending, | |
uniforms: { | |
uTime: { value: 0 }, | |
uColorStart: { value: new THREE.Color(debugObject.portalColorStart) }, | |
uColorEnd: { value: new THREE.Color(debugObject.portalColorEnd) } | |
} | |
}); | |
/** | |
* Model | |
*/ | |
gltfLoader.load("https://assets.codepen.io/22914/portal-2.glb", (gltf) => { | |
const bakedMesh = gltf.scene.children.find((child) => child.name === "baked"); | |
bakedMesh.material = bakedMaterial; | |
const portalLight = gltf.scene.children.find( | |
(child) => child.name === "portalCircle" | |
); | |
portalLight.material = portalLightMaterial; | |
gltf.scene.children | |
.filter((child) => child.name.includes("lampLight")) | |
.forEach((light) => { | |
light.material = poleLightMaterial; | |
}); | |
scene.add(gltf.scene); | |
}); | |
/** | |
* Fireflies | |
*/ | |
// Geometry | |
const firefliesGeometry = new THREE.BufferGeometry(); | |
const firefliesCount = 30; | |
const positionArray = new Float32Array(firefliesCount * 3); | |
const scaleArray = new Float32Array(firefliesCount); | |
for (let i = 0; i < firefliesCount; i++) { | |
new THREE.Vector3( | |
(Math.random() - 0.5) * 4, | |
Math.random() * 1.5, | |
(Math.random() - 0.5) * 4 | |
).toArray(positionArray, i * 3); | |
scaleArray[i] = Math.random(); | |
scaleArray[i] = Math.random(); | |
} | |
firefliesGeometry.setAttribute( | |
"position", | |
new THREE.BufferAttribute(positionArray, 3) | |
); | |
firefliesGeometry.setAttribute( | |
"aScale", | |
new THREE.BufferAttribute(scaleArray, 1) | |
); | |
const firefliesMaterial = new THREE.ShaderMaterial({ | |
vertexShader: firefliesVertexShader, | |
fragmentShader: firefliesFragmentShader, | |
transparent: true, | |
uniforms: { | |
uTime: { value: 0 }, | |
uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) }, | |
uSize: { value: 100 } | |
}, | |
blending: THREE.AdditiveBlending, | |
depthWrite: false | |
}); | |
const fireflies = new THREE.Points(firefliesGeometry, firefliesMaterial); | |
scene.add(fireflies); | |
/** | |
* Sizes | |
*/ | |
const sizes = { | |
width: window.innerWidth, | |
height: window.innerHeight | |
}; | |
window.addEventListener("resize", () => { | |
// Update sizes | |
sizes.width = window.innerWidth; | |
sizes.height = window.innerHeight; | |
// Update camera | |
camera.aspect = sizes.width / sizes.height; | |
camera.updateProjectionMatrix(); | |
// Update renderer | |
renderer.setSize(sizes.width, sizes.height); | |
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
/// Update fireflies | |
firefliesMaterial.uniforms.uPixelRatio.value = Math.min( | |
window.devicePixelRatio, | |
2 | |
); | |
}); | |
/** | |
* Camera | |
*/ | |
// Base camera | |
const camera = new THREE.PerspectiveCamera( | |
45, | |
sizes.width / sizes.height, | |
0.1, | |
100 | |
); | |
camera.position.x = -4; | |
camera.position.y = 2; | |
camera.position.z = -4; | |
scene.add(camera); | |
// Controls | |
const controls = new THREE.OrbitControls(camera, canvas); | |
controls.enableDamping = true; | |
// Don't go below the ground | |
controls.maxPolarAngle = Math.PI / 2 - 0.1; | |
// Clamp panning | |
const minPan = new THREE.Vector3(-0.2, -0.2, -0.2); | |
const maxPan = new THREE.Vector3(2, 2, 2); | |
const _v = new THREE.Vector3(); | |
controls.addEventListener("change", function () { | |
_v.copy(controls.target); | |
controls.target.clamp(minPan, maxPan); | |
_v.sub(controls.target); | |
camera.position.sub(_v); | |
}); | |
/** | |
* Renderer | |
*/ | |
const renderer = new THREE.WebGLRenderer({ | |
canvas: canvas, | |
antialias: true | |
}); | |
renderer.setSize(sizes.width, sizes.height); | |
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); | |
renderer.outputEncoding = THREE.sRGBEncoding; | |
renderer.setClearColor(debugObject.clearColor); | |
/** | |
* Animate | |
*/ | |
const clock = new THREE.Clock(); | |
const tick = () => { | |
const elapsedTime = clock.getElapsedTime(); | |
firefliesMaterial.uniforms.uTime.value = elapsedTime; | |
portalLightMaterial.uniforms.uTime.value = elapsedTime; | |
// Update controls | |
controls.update(); | |
// Render | |
renderer.render(scene, camera); | |
// Call tick again on the next frame | |
window.requestAnimationFrame(tick); | |
}; | |
tick(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r125/three.min.js"></script> | |
<script src="https://unpkg.com/three@0.125.2/examples/js/controls/OrbitControls.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script> | |
<script src="https://unpkg.com/three@0.125.2/examples/js/loaders/GLTFLoader.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* { | |
margin: 0; | |
padding: 0; | |
} | |
html, | |
body { | |
overflow: hidden; | |
} | |
.webgl { | |
position: fixed; | |
top: 0; | |
left: 0; | |
outline: none; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment