three.js custom phong material

by Milan Gladiš

HTML

<!-- 
Custom three.js Shader / ShaderMaterial 
with all MeshPhongMaterial's shader features.
the basic 'phong template' is setup for uniforms, 
vertex and fragment shaders, and code is simply 
injected in program to allow for changing the vertex
positions and the diffuse color before default lighting is applied.

by @gsynuh 
-->

<!-- three.js r84 -->
<script src="https://labs.gsynuh.com/three.84.min.js"></script>

<!-- vertex shader code included in the program outside main() -->
<script type="x-shader/x-vertex" id="vertCode" >
uniform float time;
</script>

<!-- vertex shader code included at the end of the main() function -->
<script type="x-shader/x-vertex" id="vertMain" >

float maxOffLen = 0.3;
vec2 p = uv * vec2(PI*4.3);

vec3 offset = normalize(normal) * vec3(sin(time * 4.0 + p.y ) * maxOffLen - maxOffLen);

gl_Position.x += offset.x;
gl_Position.y += offset.y;
gl_Position.z += offset.z;
</script>


<!-- Shader code included in the fragment program , outside of the main() function. -->
<script type="x-shader/x-fragment" id="fragCode" >
uniform float time;

//pNoise from https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
//  Classic Perlin 3D Noise 
//  by Stefan Gustavson
//
vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}

float cnoise(vec3 P){
  vec3 Pi0 = floor(P); // Integer part for indexing
  vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
  Pi0 = mod(Pi0, 289.0);
  Pi1 = mod(Pi1, 289.0);
  vec3 Pf0 = fract(P); // Fractional part for interpolation
  vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
  vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
  vec4 iy = vec4(Pi0.yy, Pi1.yy);
  vec4 iz0 = Pi0.zzzz;
  vec4 iz1 = Pi1.zzzz;

  vec4 ixy = permute(permute(ix) + iy);
  vec4 ixy0 = permute(ixy + iz0);
  vec4 ixy1 = permute(ixy + iz1);

  vec4 gx0 = ixy0 / 7.0;
  vec4 gy0 = fract(floor(gx0) / 7.0) -...

CSS

body {
	margin: 0;
}

JavaScript

(function() {
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

  var renderer = new THREE.WebGLRenderer();
  renderer.setClearColor( 0xE8DED2,1.0);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  var geometry = new THREE.SphereGeometry(4, 64, 64);

  var uniforms = THREE.UniformsUtils.merge([
    THREE.UniformsLib['ambient'],
    THREE.UniformsLib['lights'],
    THREE.UniformsUtils.clone(THREE.ShaderLib.phong.uniforms),
	{
      "diffuse": {type: 'c',value: new THREE.Color(0xFF00FF)},
      "dirSpecularWeight": {type: "v3",value: new THREE.Vector3(1, 1, 1)},
      "time": {type: 'f',value: 0.0}
    }
  ]);

  var vertex = [
    "#define PHONG",
    "varying vec3 vViewPosition;",
    "varying vec2 vUv;",
    "varying vec3 vNormal;",
    THREE.ShaderChunk["common"],
    THREE.ShaderChunk["uv_pars_vertex"],
    THREE.ShaderChunk["uv2_pars_vertex"],
    THREE.ShaderChunk["displacementmap_pars_vertex"],
    THREE.ShaderChunk["envmap_pars_vertex"],
    THREE.ShaderChunk["color_pars_vertex"],
    THREE.ShaderChunk["fog_pars_vertex"],
    THREE.ShaderChunk["morphtarget_pars_vertex"],
    THREE.ShaderChunk["skinning_pars_vertex"],
    THREE.ShaderChunk["shadowmap_pars_vertex"],
    THREE.ShaderChunk["logdepthbuf_pars_vertex"],
    THREE.ShaderChunk["clipping_planes_pars_vertex"],
	document.getElementById("vertCode").text,
    "void main() {",
    THREE.ShaderChunk["uv_vertex"],
    THREE.ShaderChunk["uv2_vertex"],
    THREE.ShaderChunk["color_vertex"],
    THREE.ShaderChunk["beginnormal_vertex"],
    THREE.ShaderChunk["morphnormal_vertex"],
    THREE.ShaderChunk["skinbase_vertex"],
    THREE.ShaderChunk["skinnormal_vertex"],
    THREE.ShaderChunk["defaultnormal_vertex"],
    "vNormal = normalize( transformedNormal);",
    THREE.ShaderChunk["begin_vertex"],
    THREE.ShaderChunk["displacementmap_vertex"],
   ...