Published
Edited
Aug 28, 2020
5 forks
5 stars
Insert cell
md`# Simple vertex shader using three.js`


Insert cell
md`## Setup your scene`
Insert cell
{
let camera,
renderer,
scene,
controls,
mesh,
cubeGroup,
height=400;
init();
function createCamera() {
// Create a Camera
const fov = 25; // AKA Field of View
const aspect = width / height;
const near = 0.1; // the near clipping plane
const far = 1000; // the far clipping plane

camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(-4, 2, 4);

}
function createLights() {
// Create a directional light
const ambientLight = new THREE.HemisphereLight(0xddeeff, 0x202020, 9);
const mainLight = new THREE.DirectionalLight(0xffffff, 3.0);
scene.add(ambientLight);

// move the light back and up a bit
mainLight.position.set(10, 10, 10);

// remember to add the light to the scene
scene.add(ambientLight, mainLight);
}
function createMaterials() {
const cubeShader = new THREE.ShaderMaterial({
uniforms: {
colorA: {type: 'vec3', value: new THREE.Color(0xff0000)},
colorB: {type: 'vec3', value: new THREE.Color(0x0000FF)}
},
vertexShader: vertexShader(),
fragmentShader: fragmentShader()
});
return {
cubeShader
}
}
function createGeometries() {
const cube = new THREE.BoxGeometry( 1, 1, 1 );
return {
cube
}
}
function createMeshes() {
const materials = createMaterials();
const geometries = createGeometries();
const cubeMesh = new THREE.Mesh( geometries.cube, materials.cubeShader );
const group = new THREE.Group();
group.add(cubeMesh);
cubeGroup = group;

// Add the mesh to the scene
scene.add(group);
}
function createRenderer() {
// create the renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});

renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.gammaFactor = 2.2;
renderer.gammaOutput = true;
renderer.physicallyCorrectLights = true;
}
function init() {
// create a Scene
scene = new THREE.Scene();
// Set the background color
scene.background = new THREE.Color('#00DDDC');
createCamera();
createLights();
createMeshes();
createRenderer();
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.rotateSpeed = 0.1;
invalidation.then(() => (controls.dispose(), renderer.dispose()));
}
function render() {
renderer.render(scene, camera);
}
function update() {
/*********** PUT ANIMATION LOGIC HERE **********/
cubeGroup.rotation.x += 0.01;
cubeGroup.rotation.y += 0.01;
cubeGroup.rotation.z += 0.01;
/***********************************************/
}
function onWindowResize() {
camera.aspect = width / height;;
camera.updateProjectionMatrix();
renderer.setSize(width, height)
}
window.addEventListener('resize', onWindowResize)
renderer.setAnimationLoop(() => {
update();
render();
controls.update()
})
invalidation.then(() => {
controls.dispose();
renderer.dispose();
window.removeEventListener('resize', onWindowResize);
});
yield renderer.domElement
}
Insert cell
md`## Setup a simple vertex shader`
Insert cell

function vertexShader() {
return `
varying vec3 vUv;

void main() {
vUv = position;

vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
}
`
}

Insert cell
md`
### modelViewMatrix and projectionMartix explained.

"ModelView matrix is the concatenation of Model matrix and View Matrix. View Matrix defines the position(location and orientation) of the camera, while model matrix defines the frame’s position of the primitives you are going to draw.
Projection matrix defines the characteristics of your camera, such as clip planes, field of view, projection method etc." [quoted from Kronos group](https://community.khronos.org/t/difference-between-modelview-matrix-and-projection-matrix/22437/2)
`
Insert cell
md`## Setup a fragment shader`
Insert cell
function fragmentShader() {
return `
uniform vec3 colorA;
uniform vec3 colorB;
varying vec3 vUv;

void main() {
gl_FragColor = vec4(mix(colorA, colorB, vUv.z), 1.0);
}
`
}
Insert cell
md`We use the model position defined in the vertexShader as varying vUv and passed to the fragmentShader. The colorA and colorB are uniforms and provided as variables from our material in our threejs code.`
Insert cell
md`*Note that this example doesn't take into account the light calculation in the fragment shader so the mesh will render flat.*`
Insert cell
THREE = {
const THREE = window.THREE = await require("three@0.99.0/build/three.min.js");
await require("three@0.99.0/examples/js/controls/OrbitControls.js").catch(() => {});
return window.THREE;
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more