Apply color gradient to material on mesh – three.js

  • A+
Category:Languages

I have an STL file loaded into my scene with a single colour applied to a phong material

I'd like a way of applying two colours to this mesh's material with a gradient effect applied on the Z axis a like the example below.Gradient Vase]1

I have a feeling I may have to introduce shaders but I've not gotten this far with three.js.

 


Simple gradient shader, based on uvs:

var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000); camera.position.set(13, 25, 38); camera.lookAt(scene.position); var renderer = new THREE.WebGLRenderer({   antialias: true }); var canvas = renderer.domElement document.body.appendChild(canvas);  var controls = new THREE.OrbitControls(camera, renderer.domElement);  var geometry = new THREE.CylinderBufferGeometry(2, 5, 20, 32, 1, true); var material = new THREE.ShaderMaterial({   uniforms: {     color1: {       value: new THREE.Color("red")     },     color2: {       value: new THREE.Color("purple")     }   },   vertexShader: `     varying vec2 vUv;      void main() {       vUv = uv;       gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);     }   `,   fragmentShader: `     uniform vec3 color1;     uniform vec3 color2;        varying vec2 vUv;          void main() {              gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);     }   `,   wireframe: true }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh);    render();  function resize(renderer) {   const canvas = renderer.domElement;   const width = canvas.clientWidth;   const height = canvas.clientHeight;   const needResize = canvas.width !== width || canvas.height !== height;   if (needResize) {     renderer.setSize(width, height, false);   }   return needResize; }  function render() {   if (resize(renderer)) {     camera.aspect = canvas.clientWidth / canvas.clientHeight;     camera.updateProjectionMatrix();   }   renderer.render(scene, camera);   requestAnimationFrame(render); }
html, body {   height: 100%;   margin: 0;   overflow: hidden; }  canvas {   width: 100%;   height: 100%;   display;   block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Simple gradient shader, based on coordinates:

var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000); camera.position.set(13, 25, 38); camera.lookAt(scene.position); var renderer = new THREE.WebGLRenderer({   antialias: true }); var canvas = renderer.domElement document.body.appendChild(canvas);  var controls = new THREE.OrbitControls(camera, renderer.domElement);  var geometry = new THREE.CylinderBufferGeometry(2, 5, 20, 16, 4, true); geometry.computeBoundingBox(); var material = new THREE.ShaderMaterial({   uniforms: {     color1: {       value: new THREE.Color("red")     },     color2: {       value: new THREE.Color("purple")     },     bboxMin: {       value: geometry.boundingBox.min     },     bboxMax: {       value: geometry.boundingBox.max     }   },   vertexShader: `     uniform vec3 bboxMin;     uniform vec3 bboxMax;        varying vec2 vUv;      void main() {       vUv.y = (position.y - bboxMin.y) / (bboxMax.y - bboxMin.y);       gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);     }   `,   fragmentShader: `     uniform vec3 color1;     uniform vec3 color2;        varying vec2 vUv;          void main() {              gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0);     }   `,   wireframe: true }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh);    render();  function resize(renderer) {   const canvas = renderer.domElement;   const width = canvas.clientWidth;   const height = canvas.clientHeight;   const needResize = canvas.width !== width || canvas.height !== height;   if (needResize) {     renderer.setSize(width, height, false);   }   return needResize; }  function render() {   if (resize(renderer)) {     camera.aspect = canvas.clientWidth / canvas.clientHeight;     camera.updateProjectionMatrix();   }   renderer.render(scene, camera);   requestAnimationFrame(render); }
html, body {   height: 100%;   margin: 0;   overflow: hidden; }  canvas {   width: 100%;   height: 100%;   display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Gradient with vertex colours:

var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000); camera.position.set(0, 0, 10); var renderer = new THREE.WebGLRenderer({   antialias: true }); var canvas = renderer.domElement document.body.appendChild(canvas);   var geom = new THREE.TorusKnotGeometry(2.5, .5, 100, 16);  var rev = true;  var cols = [{   stop: 0,   color: new THREE.Color(0xf7b000) }, {   stop: .25,   color: new THREE.Color(0xdd0080) }, {   stop: .5,   color: new THREE.Color(0x622b85) }, {   stop: .75,   color: new THREE.Color(0x007dae) }, {   stop: 1,   color: new THREE.Color(0x77c8db) }];  setGradient(geom, cols, 'z', rev);  function setGradient(geometry, colors, axis, reverse) {    geometry.computeBoundingBox();    var bbox = geometry.boundingBox;   var size = new THREE.Vector3().subVectors(bbox.max, bbox.min);    var vertexIndices = ['a', 'b', 'c'];   var face, vertex, normalized = new THREE.Vector3(),     normalizedAxis = 0;    for (var c = 0; c < colors.length - 1; c++) {      var colorDiff = colors[c + 1].stop - colors[c].stop;      for (var i = 0; i < geometry.faces.length; i++) {       face = geometry.faces[i];       for (var v = 0; v < 3; v++) {         vertex = geometry.vertices[face[vertexIndices[v]]];         normalizedAxis = normalized.subVectors(vertex, bbox.min).divide(size)[axis];         if (reverse) {           normalizedAxis = 1 - normalizedAxis;         }         if (normalizedAxis >= colors[c].stop && normalizedAxis <= colors[c + 1].stop) {           var localNormalizedAxis = (normalizedAxis - colors[c].stop) / colorDiff;           face.vertexColors[v] = colors[c].color.clone().lerp(colors[c + 1].color, localNormalizedAxis);         }       }     }   } }  var mat = new THREE.MeshBasicMaterial({   vertexColors: THREE.VertexColors,   wireframe: true }); var obj = new THREE.Mesh(geom, mat); scene.add(obj);    render();  function resize(renderer) {   const canvas = renderer.domElement;   const width = canvas.clientWidth;   const height = canvas.clientHeight;   const needResize = canvas.width !== width || canvas.height !== height;   if (needResize) {     renderer.setSize(width, height, false);   }   return needResize; }  function render() {   if (resize(renderer)) {     camera.aspect = canvas.clientWidth / canvas.clientHeight;     camera.updateProjectionMatrix();   }   renderer.render(scene, camera);   obj.rotation.y += .01;   requestAnimationFrame(render); }
html, body {   height: 100%;   margin: 0;   overflow: hidden; }  canvas {   width: 100%;   height: 100%;   display;   block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>

Actually, it's up to you which approach to use: shaders, vertex colours, textures etc.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: