├── 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 | --------------------------------------------------------------------------------