Skip to content

Commit 6cee6d6

Browse files
authored
Merge pull request #4 from SmartCompiler/feature/3d-sphere
Add 3d sphere three js to the project
2 parents 02773b3 + 900601e commit 6cee6d6

17 files changed

+4341
-2177
lines changed

components/3d-sphere/app.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import {
2+
DirectionalLight,
3+
AmbientLight,
4+
IcosahedronGeometry,
5+
MeshStandardMaterial,
6+
Mesh,
7+
LinearFilter,
8+
WebGLRenderTarget
9+
} from 'three'
10+
import { getCamera, getRenderSize, getScene, getTick } from './render/init.js'
11+
// import postprocessing passes
12+
import { SavePass } from 'three/examples/jsm/postprocessing/SavePass.js'
13+
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
14+
import { BlendShader } from 'three/examples/jsm/shaders/BlendShader.js'
15+
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js'
16+
17+
import vertexParse from './shaders/vertex-pars.glsl'
18+
import vertexMain from './shaders/vertex-main.glsl'
19+
import fragmentPars from './shaders/fragment_pars.glsl'
20+
import fragmentMain from './shaders/fragment_main.glsl'
21+
22+
const startApp = () => {
23+
const scene = getScene()
24+
const { width, height } = getRenderSize()
25+
26+
// settings
27+
const MOTION_BLUR_AMOUNT = 0.725
28+
29+
// lighting
30+
const dirLight = new DirectionalLight('#36938a', 1.5)
31+
dirLight.position.set(10, 10, 2)
32+
33+
const ambientLight = new AmbientLight('#3aa294', 0.9)
34+
scene.add(dirLight, ambientLight)
35+
36+
// meshes
37+
const geometry = new IcosahedronGeometry(1, 300)
38+
const material = new MeshStandardMaterial({
39+
onBeforeCompile(shader) {
40+
// Storing a reference to the shader object
41+
material.userData.shader = shader
42+
43+
// uniforms
44+
shader.uniforms.uTime = { value: 0 }
45+
46+
const parsVertextString = /* glsl*/ `#include <displacementmap_pars_vertex>`
47+
shader.vertexShader = shader.vertexShader.replace(
48+
parsVertextString,
49+
parsVertextString + vertexParse
50+
)
51+
52+
const mainVertexString = /* glsl */ `#include <displacementmap_vertex>`
53+
shader.vertexShader = shader.vertexShader.replace(
54+
mainVertexString,
55+
mainVertexString + vertexMain
56+
)
57+
58+
const mainFragmentString = /* glsl */ `#include <normal_fragment_maps>`
59+
const parsFragmentString = /* glsl */ `#include <bumpmap_pars_fragment>`
60+
shader.fragmentShader = shader.fragmentShader.replace(
61+
parsFragmentString,
62+
parsFragmentString + fragmentPars
63+
)
64+
shader.fragmentShader = shader.fragmentShader.replace(
65+
mainFragmentString,
66+
mainFragmentString + fragmentMain
67+
)
68+
}
69+
})
70+
71+
const ico = new Mesh(geometry, material)
72+
scene.add(ico)
73+
74+
// postprocessing
75+
const renderTargetParameters = {
76+
minFilter: LinearFilter,
77+
magFilter: LinearFilter,
78+
stencilBuffer: false,
79+
}
80+
81+
// save pass
82+
const savePass = new SavePass(new WebGLRenderTarget(width, height, renderTargetParameters))
83+
84+
// blend pass
85+
const blendPass = new ShaderPass(BlendShader, 'tDiffuse1')
86+
blendPass.uniforms['tDiffuse2'].value = savePass.renderTarget.texture
87+
blendPass.uniforms['mixRatio'].value = MOTION_BLUR_AMOUNT
88+
89+
// output pass
90+
const outputPass = new ShaderPass(CopyShader)
91+
outputPass.renderToScreen = true
92+
93+
getTick(({ timestamp, timeDiff }) => {
94+
const time = timestamp / 10000;
95+
if (material?.userData?.shader?.uniforms?.uTime)
96+
material.userData.shader.uniforms.uTime.value = time
97+
})
98+
}
99+
100+
export default startApp

components/3d-sphere/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import startApp from './app';
2+
import { initEngine } from './render/init';
3+
4+
export const initSphere = async (sphereRef) => {
5+
await initEngine(sphereRef)
6+
startApp()
7+
}

components/3d-sphere/render/init.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import {
2+
Scene,
3+
PerspectiveCamera,
4+
WebGLRenderer,
5+
PCFSoftShadowMap,
6+
WebGLRenderTarget
7+
} from 'three'
8+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
9+
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
10+
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
11+
import TickManager from './tick-manager.js'
12+
13+
let scene,
14+
camera,
15+
renderer,
16+
composer,
17+
controls,
18+
renderWidth,
19+
renderHeight,
20+
renderAspectRatio
21+
const renderTickManager = new TickManager()
22+
23+
export const initEngine = async (sphereRef) => {
24+
scene = new Scene()
25+
26+
renderWidth = 370
27+
renderHeight = 370
28+
29+
renderAspectRatio = renderWidth / renderHeight
30+
31+
camera = new PerspectiveCamera(75, renderAspectRatio, 0.1, 100)
32+
camera.position.z = 2.2
33+
34+
renderer = new WebGLRenderer({ antialias: true, alpha: true})
35+
renderer.setSize(renderWidth, renderHeight)
36+
37+
// shadow
38+
renderer.shadowMap.enabled = true
39+
renderer.shadowMap.type = PCFSoftShadowMap
40+
console.log(renderer.domElement)
41+
sphereRef.current.appendChild(renderer.domElement)
42+
43+
const target = new WebGLRenderTarget(renderWidth, renderHeight, {
44+
samples: 8
45+
})
46+
composer = new EffectComposer(renderer, target)
47+
48+
const renderPass = new RenderPass(scene, camera)
49+
composer.addPass(renderPass)
50+
51+
controls = new OrbitControls(camera, renderer.domElement)
52+
controls.enableDamping = false
53+
controls.enableZoom = false
54+
controls.enablePan = false
55+
56+
window.addEventListener(
57+
'resize',
58+
() => {
59+
camera.aspect = renderAspectRatio
60+
camera.updateProjectionMatrix()
61+
composer.setSize(renderWidth, renderHeight)
62+
},
63+
false
64+
)
65+
66+
renderTickManager.startLoop()
67+
}
68+
69+
export const getRenderer = () => renderer
70+
71+
export const getRenderSize = () => ({ width: renderWidth, height: renderHeight })
72+
73+
export const getScene = () => scene
74+
75+
export const getCamera = () => camera
76+
77+
export const getControls = () => controls
78+
79+
export const getComposer = () => composer
80+
81+
82+
export const addPass = (pass) => {
83+
composer.addPass(pass)
84+
}
85+
86+
export const getTick = (fn) => {
87+
if (renderTickManager) {
88+
const _tick = (e) => {
89+
fn(e.data)
90+
}
91+
renderTickManager.addEventListener('tick', _tick)
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { getComposer, getControls, getRenderer } from './init.js';
2+
3+
// animation params
4+
const localData = {
5+
timestamp: 0,
6+
timeDiff: 0,
7+
frame: null,
8+
};
9+
const localFrameOpts = {
10+
data: localData,
11+
};
12+
13+
const frameEvent = new MessageEvent('tick', localFrameOpts);
14+
15+
class TickManager extends EventTarget {
16+
constructor({ timestamp, timeDiff, frame } = localData) {
17+
super();
18+
19+
this.timestamp = timestamp;
20+
this.timeDiff = timeDiff;
21+
this.frame = frame;
22+
}
23+
startLoop() {
24+
const composer = getComposer();
25+
const renderer = getRenderer();
26+
const controls = getControls();
27+
28+
if (!renderer) {
29+
throw new Error('Updating Frame Failed : Uninitialized Renderer');
30+
}
31+
32+
let lastTimestamp = performance.now();
33+
34+
const animate = (timestamp, frame) => {
35+
this.timestamp = timestamp ?? performance.now();
36+
this.timeDiff = timestamp - lastTimestamp;
37+
38+
const timeDiffCapped = Math.min(Math.max(this.timeDiff, 0), 100);
39+
40+
// performance tracker start
41+
42+
controls.update();
43+
44+
composer.render();
45+
// renderer.render(scene, camera);
46+
47+
this.tick(timestamp, timeDiffCapped, frame);
48+
49+
// performance tracker end
50+
};
51+
52+
renderer.setAnimationLoop(animate);
53+
}
54+
tick(timestamp, timeDiff, frame) {
55+
localData.timestamp = timestamp;
56+
localData.frame = frame;
57+
localData.timeDiff = timeDiff;
58+
this.dispatchEvent(frameEvent);
59+
}
60+
}
61+
62+
export default TickManager;
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
precision mediump float;
3+
4+
5+
varying vec3 vPosition;
6+
varying vec2 vUv;
7+
varying vec3 vNormal;
8+
varying float vDisplacement;
9+
10+
void main() {
11+
12+
gl_FragColor = vec4(vec3(vDisplacement), 1);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
normal = perturbNormalArb( - vViewPosition, normal, vec2(dFdx(vDisplacement), dFdy(vDisplacement)), faceDirection );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
uniform float uTime;
3+
varying float vDisplacement;
4+
5+
6+
vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {
7+
8+
vec3 vSigmaX = dFdx( surf_pos.xyz );
9+
vec3 vSigmaY = dFdy( surf_pos.xyz );
10+
vec3 vN = surf_norm; // normalized
11+
12+
vec3 R1 = cross( vSigmaY, vN );
13+
vec3 R2 = cross( vN, vSigmaX );
14+
15+
float fDet = dot( vSigmaX, R1 ) * faceDirection;
16+
17+
vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
18+
return normalize( abs( fDet ) * surf_norm - vGrad );
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
// Pattern
3+
vec3 coords = normal;
4+
coords.y += uTime;
5+
6+
vec3 noisyPattern = vec3(noise(coords));
7+
float pattern = wave(noisyPattern + uTime);
8+
// Varying
9+
vDisplacement = pattern;
10+
11+
float displacement = vDisplacement / 3.0;
12+
13+
transformed += normalize(objectNormal) * displacement;

0 commit comments

Comments
 (0)