Skip to content

Commit

Permalink
Merge pull request #4 from SmartCompiler/feature/3d-sphere
Browse files Browse the repository at this point in the history
Add 3d sphere three js to the project
  • Loading branch information
behnamrhp authored Mar 30, 2024
2 parents 02773b3 + 900601e commit 6cee6d6
Show file tree
Hide file tree
Showing 17 changed files with 4,341 additions and 2,177 deletions.
100 changes: 100 additions & 0 deletions components/3d-sphere/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
DirectionalLight,
AmbientLight,
IcosahedronGeometry,
MeshStandardMaterial,
Mesh,
LinearFilter,
WebGLRenderTarget
} from 'three'
import { getCamera, getRenderSize, getScene, getTick } from './render/init.js'
// import postprocessing passes
import { SavePass } from 'three/examples/jsm/postprocessing/SavePass.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { BlendShader } from 'three/examples/jsm/shaders/BlendShader.js'
import { CopyShader } from 'three/examples/jsm/shaders/CopyShader.js'

import vertexParse from './shaders/vertex-pars.glsl'
import vertexMain from './shaders/vertex-main.glsl'
import fragmentPars from './shaders/fragment_pars.glsl'
import fragmentMain from './shaders/fragment_main.glsl'

const startApp = () => {
const scene = getScene()
const { width, height } = getRenderSize()

// settings
const MOTION_BLUR_AMOUNT = 0.725

// lighting
const dirLight = new DirectionalLight('#36938a', 1.5)
dirLight.position.set(10, 10, 2)

const ambientLight = new AmbientLight('#3aa294', 0.9)
scene.add(dirLight, ambientLight)

// meshes
const geometry = new IcosahedronGeometry(1, 300)
const material = new MeshStandardMaterial({
onBeforeCompile(shader) {
// Storing a reference to the shader object
material.userData.shader = shader

// uniforms
shader.uniforms.uTime = { value: 0 }

const parsVertextString = /* glsl*/ `#include <displacementmap_pars_vertex>`
shader.vertexShader = shader.vertexShader.replace(
parsVertextString,
parsVertextString + vertexParse
)

const mainVertexString = /* glsl */ `#include <displacementmap_vertex>`
shader.vertexShader = shader.vertexShader.replace(
mainVertexString,
mainVertexString + vertexMain
)

const mainFragmentString = /* glsl */ `#include <normal_fragment_maps>`
const parsFragmentString = /* glsl */ `#include <bumpmap_pars_fragment>`
shader.fragmentShader = shader.fragmentShader.replace(
parsFragmentString,
parsFragmentString + fragmentPars
)
shader.fragmentShader = shader.fragmentShader.replace(
mainFragmentString,
mainFragmentString + fragmentMain
)
}
})

const ico = new Mesh(geometry, material)
scene.add(ico)

// postprocessing
const renderTargetParameters = {
minFilter: LinearFilter,
magFilter: LinearFilter,
stencilBuffer: false,
}

// save pass
const savePass = new SavePass(new WebGLRenderTarget(width, height, renderTargetParameters))

// blend pass
const blendPass = new ShaderPass(BlendShader, 'tDiffuse1')
blendPass.uniforms['tDiffuse2'].value = savePass.renderTarget.texture
blendPass.uniforms['mixRatio'].value = MOTION_BLUR_AMOUNT

// output pass
const outputPass = new ShaderPass(CopyShader)
outputPass.renderToScreen = true

getTick(({ timestamp, timeDiff }) => {
const time = timestamp / 10000;
if (material?.userData?.shader?.uniforms?.uTime)
material.userData.shader.uniforms.uTime.value = time
})
}

export default startApp
7 changes: 7 additions & 0 deletions components/3d-sphere/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import startApp from './app';
import { initEngine } from './render/init';

export const initSphere = async (sphereRef) => {
await initEngine(sphereRef)
startApp()
}
93 changes: 93 additions & 0 deletions components/3d-sphere/render/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
Scene,
PerspectiveCamera,
WebGLRenderer,
PCFSoftShadowMap,
WebGLRenderTarget
} from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import TickManager from './tick-manager.js'

let scene,
camera,
renderer,
composer,
controls,
renderWidth,
renderHeight,
renderAspectRatio
const renderTickManager = new TickManager()

export const initEngine = async (sphereRef) => {
scene = new Scene()

renderWidth = 370
renderHeight = 370

renderAspectRatio = renderWidth / renderHeight

camera = new PerspectiveCamera(75, renderAspectRatio, 0.1, 100)
camera.position.z = 2.2

renderer = new WebGLRenderer({ antialias: true, alpha: true})
renderer.setSize(renderWidth, renderHeight)

// shadow
renderer.shadowMap.enabled = true
renderer.shadowMap.type = PCFSoftShadowMap
console.log(renderer.domElement)
sphereRef.current.appendChild(renderer.domElement)

const target = new WebGLRenderTarget(renderWidth, renderHeight, {
samples: 8
})
composer = new EffectComposer(renderer, target)

const renderPass = new RenderPass(scene, camera)
composer.addPass(renderPass)

controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = false
controls.enableZoom = false
controls.enablePan = false

window.addEventListener(
'resize',
() => {
camera.aspect = renderAspectRatio
camera.updateProjectionMatrix()
composer.setSize(renderWidth, renderHeight)
},
false
)

renderTickManager.startLoop()
}

export const getRenderer = () => renderer

export const getRenderSize = () => ({ width: renderWidth, height: renderHeight })

export const getScene = () => scene

export const getCamera = () => camera

export const getControls = () => controls

export const getComposer = () => composer


export const addPass = (pass) => {
composer.addPass(pass)
}

export const getTick = (fn) => {
if (renderTickManager) {
const _tick = (e) => {
fn(e.data)
}
renderTickManager.addEventListener('tick', _tick)
}
}
62 changes: 62 additions & 0 deletions components/3d-sphere/render/tick-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { getComposer, getControls, getRenderer } from './init.js';

// animation params
const localData = {
timestamp: 0,
timeDiff: 0,
frame: null,
};
const localFrameOpts = {
data: localData,
};

const frameEvent = new MessageEvent('tick', localFrameOpts);

class TickManager extends EventTarget {
constructor({ timestamp, timeDiff, frame } = localData) {
super();

this.timestamp = timestamp;
this.timeDiff = timeDiff;
this.frame = frame;
}
startLoop() {
const composer = getComposer();
const renderer = getRenderer();
const controls = getControls();

if (!renderer) {
throw new Error('Updating Frame Failed : Uninitialized Renderer');
}

let lastTimestamp = performance.now();

const animate = (timestamp, frame) => {
this.timestamp = timestamp ?? performance.now();
this.timeDiff = timestamp - lastTimestamp;

const timeDiffCapped = Math.min(Math.max(this.timeDiff, 0), 100);

// performance tracker start

controls.update();

composer.render();
// renderer.render(scene, camera);

this.tick(timestamp, timeDiffCapped, frame);

// performance tracker end
};

renderer.setAnimationLoop(animate);
}
tick(timestamp, timeDiff, frame) {
localData.timestamp = timestamp;
localData.frame = frame;
localData.timeDiff = timeDiff;
this.dispatchEvent(frameEvent);
}
}

export default TickManager;
13 changes: 13 additions & 0 deletions components/3d-sphere/shaders/fragment.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

precision mediump float;


varying vec3 vPosition;
varying vec2 vUv;
varying vec3 vNormal;
varying float vDisplacement;

void main() {

gl_FragColor = vec4(vec3(vDisplacement), 1);
}
2 changes: 2 additions & 0 deletions components/3d-sphere/shaders/fragment_main.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

normal = perturbNormalArb( - vViewPosition, normal, vec2(dFdx(vDisplacement), dFdy(vDisplacement)), faceDirection );
20 changes: 20 additions & 0 deletions components/3d-sphere/shaders/fragment_pars.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

uniform float uTime;
varying float vDisplacement;


vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {

vec3 vSigmaX = dFdx( surf_pos.xyz );
vec3 vSigmaY = dFdy( surf_pos.xyz );
vec3 vN = surf_norm; // normalized

vec3 R1 = cross( vSigmaY, vN );
vec3 R2 = cross( vN, vSigmaX );

float fDet = dot( vSigmaX, R1 ) * faceDirection;

vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );
return normalize( abs( fDet ) * surf_norm - vGrad );

}
13 changes: 13 additions & 0 deletions components/3d-sphere/shaders/vertex-main.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

// Pattern
vec3 coords = normal;
coords.y += uTime;

vec3 noisyPattern = vec3(noise(coords));
float pattern = wave(noisyPattern + uTime);
// Varying
vDisplacement = pattern;

float displacement = vDisplacement / 3.0;

transformed += normalize(objectNormal) * displacement;
Loading

0 comments on commit 6cee6d6

Please sign in to comment.