├── css
└── styles.css
├── index.html
└── js
└── main.js
/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #9bb5c6;
3 | text-align: center;
4 | }
5 |
6 | canvas {
7 | width: 150px;
8 | height: 150px;
9 | }
10 |
11 | .canvas-wrapper {
12 | display: inline-block;
13 | }
14 |
15 | #js-canvas-default {
16 | background-color: #ff0000;
17 | }
18 |
19 | #js-canvas-custom {
20 | background-color: #0000ff;
21 | }
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Extending the Built-in Phong Material Shader in Three.js
5 |
6 |
7 |
8 |
9 |
10 |
11 | Standard
12 |
13 |
14 |
15 |
16 |
17 |
18 | Custom
19 |
20 |
21 |
22 |
23 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * The goal of this code is to demonstrate one way to extend Three.js's
3 | * standard Phong material shader. The default cube uses the standard Phong
4 | * material. The custom cube uses the modified Phong vertex shader in
5 | * index.html.
6 | *
7 | * Basic scene pulled from Three.js docs:
8 | * https://threejs.org/docs/index.html#manual/introduction/Creating-a-scene
9 | */
10 |
11 | var cubeColorHex = 0x00ff00;
12 |
13 | var defaultCanvas = document.getElementById('js-canvas-default');
14 | var defaultMaterial = new THREE.MeshPhongMaterial({ color: cubeColorHex });
15 |
16 | var customCanvas = document.getElementById('js-canvas-custom');
17 | var customVertexShader = document.getElementById('js-custom-vertex-shader').textContent;
18 | var customUniforms = THREE.UniformsUtils.merge([
19 | THREE.ShaderLib.phong.uniforms,
20 | { diffuse: { value: new THREE.Color(cubeColorHex) } },
21 | { time: { value: 0.0 } }
22 | ]);
23 | var customMaterial = new THREE.ShaderMaterial({
24 | uniforms: customUniforms,
25 | vertexShader: customVertexShader,
26 | fragmentShader: THREE.ShaderLib.phong.fragmentShader,
27 | lights: true,
28 | name: 'custom-material'
29 | });
30 |
31 | animateScene(defaultCanvas, defaultMaterial);
32 | animateScene(customCanvas, customMaterial);
33 |
34 | function animateScene(canvas, material) {
35 | var width = canvas.clientWidth;
36 | var height = canvas.clientHeight;
37 |
38 | var scene = new THREE.Scene();
39 | var camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
40 | camera.position.z = 5;
41 |
42 | var renderer = new THREE.WebGLRenderer({ canvas: canvas });
43 | renderer.setSize(width, height);
44 |
45 | var geometry = new THREE.BoxBufferGeometry(3, 3, 3, 4, 4, 4);
46 |
47 | // If this is the custom cube, add random float for every vertex
48 | if (material.name === 'custom-material') {
49 | var offset = vertexOffsets(geometry.attributes.position.count);
50 | geometry.addAttribute('offset', new THREE.BufferAttribute(offset, 1));
51 | }
52 |
53 | var cube = new THREE.Mesh(geometry, material);
54 |
55 | scene.add(cube);
56 |
57 | var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
58 | scene.add(ambientLight);
59 |
60 | var pointLight = new THREE.PointLight(0xffffff, 1, 100);
61 | pointLight.position.set(3, 3, 3);
62 | scene.add(pointLight);
63 |
64 | var animate = function(timestamp) {
65 | requestAnimationFrame(animate);
66 |
67 | cube.rotation.x += 0.005;
68 | cube.rotation.y += 0.005;
69 |
70 | // If this the custom cube, pass the timestamp to the shader
71 | if (cube.material.name === 'custom-material') {
72 | cube.material.uniforms.time.value = timestamp;
73 | }
74 |
75 | renderer.render(scene, camera);
76 | };
77 |
78 | animate(0.0);
79 | }
80 |
81 | function vertexOffsets(numberOfOffsets) {
82 | var offsets = new Float32Array(numberOfOffsets);
83 | for (var i = 0; i < numberOfOffsets; i++) { offsets[i] = Math.random(); }
84 | return offsets;
85 | }
86 |
--------------------------------------------------------------------------------