I've created a custom shader that shows assigned values of vertices with contour mapping.Here is the fiddle example of my shader.
<html lang="en">
<head>
<title>Face Contour Example</title>
</head>
<body>
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="http://threejs.org/examples/js/modifiers/SubdivisionModifier.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
varying vec3 vColor;
void main(){
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vColor;
uniform vec3 uVec3Array[ 6 ];
void main(){
float r=vColor.r;
int numberOfColor=6;
float step=1.0/float(numberOfColor);
int index = int(r/step);
for(int i=0;i<6;i++){
if(i==index){
vec3 fragColor= uVec3Array[i];
gl_FragColor=vec4(fragColor,1.0);
}
}
// gl_FragColor=vec4(vColor,0);
}
</script>
<script type="text/javascript">
var camera, scene, renderer, mesh, material, controls;
init();
animate();
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 3;
// Create scene.
scene = new THREE.Scene();
var palette1={
"uVec3Array" : {
type: "v3v",
value: [
new THREE.Vector3( 0,0,1.0),
new THREE.Vector3( 66/255, 238/255, 244/255 ),
new THREE.Vector3( 0,1,0 ),
new THREE.Vector3( 1,1,0 ),
new THREE.Vector3( 1,0.4,0.2),
new THREE.Vector3( 1,0,0 )
] }
}
var palette2={
"uVec3Array" : {
type: "v3v",
value: [
new THREE.Vector3( 1,0,0 ),
new THREE.Vector3( 1,0.4,0.2),
new THREE.Vector3( 1,1,0 ),
new THREE.Vector3( 0,1,0 ),
new THREE.Vector3( 66/255, 238/255, 244/255 ),
new THREE.Vector3( 0,0,1.0)
] }
}
var selectedPalette=palette1;
// var fShader = document.getElementById('smoothPlotFShader').text;
// var vShader = document.getElementById('vertexShader').text;
var material;
var vertexShader=document.getElementById('vertexShader').textContent;
var fragmentShader= document.getElementById('fragmentShader').textContent ;
var material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
vertexColors: THREE.VertexColors,
uniforms:selectedPalette,
side:2
});
// var wireframe = new THREE.MeshLambertMaterial({color: 0x7777ff,wireframe:true});
var cubeGeometry = new THREE.CubeGeometry( 1,1,1 );
var modifier = new THREE.SubdivisionModifier( 3 );
modifier.modify( cubeGeometry );
var cube = new THREE.Mesh( cubeGeometry, material );
scene.add( cube );
//Create value array linearyly
var numberOfValues=cubeGeometry.vertices.length;
var maxValue=30;
var minValue=-30;
var values=[]
for(var i=0;i<numberOfValues;i++){
values.push(minValue+(maxValue-minValue)*i/numberOfValues);
}
applyValuesToMesh(values,cube);
//Orbit controls
controls = new THREE.OrbitControls( camera );
// Create ambient light and add to scene.
var light = new THREE.AmbientLight(0x404040); // soft white light
scene.add(light);
// Create directional light and add to scene.
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
function switchPlatte(){
if(selectedPalette===palette1){
selectedPalette=palette2;
}else{
selectedPalette=palette1;
}
material.uniforms=selectedPalette;
material.needsUpdate=true;
cube.geometry.elementsNeedUpdate = true;
console.log("Material colors are changed");
}
//This button switches plattes of material
var gui = new dat.GUI();
var obj = { changeColors:switchPlatte}
gui.add(obj,'changeColors');
}
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
var mesh;
function applyValuesToMesh(result,mesh){
var max=findMax(result);
var min=findMin(result);
function normalizeValue(value){
var range=max-min;
return (value-min)/range;
}
var normalizedResult=[];
for (r in result){
normalizedResult.push(normalizeValue(result[r]));
}
var numberOfNodes=mesh.geometry.vertices.length;
var faces=mesh.geometry.faces;
// debugger
for(f in faces){
faces[f].vertexColors[0]=new THREE.Color(normalizedResult[faces[f].a],0.5,0.5);
faces[f].vertexColors[1]=new THREE.Color(normalizedResult[faces[f].b],0.5,0.5);
faces[f].vertexColors[2]=new THREE.Color(normalizedResult[faces[f].c],0.5,0.5);
}
mesh.geometry.elementsNeedUpdate = true;
}
function findMax(array){
var max=-Infinity;
for (v in array){
if(array[v]>max) max=array[v];
}
return max;
}
function findMin(array){
var min=Infinity;
for (v in array){
if(array[v]<min) min=array[v];
}
return min;
}
</script>
</body>
The code may seem complicated. Basically, I am creating a subdivided cube and adding vertex colors to each face. However, vertex colors represent values (values can be anything, doesn't need to depend on geometry), not colors. If you look my shader, it extracts .r value of vertex colors and gets the corresponding color to that value from input uniforms. So basically material uniforms are the colors that will be used while plotting values of vertices.
My issue is If I change colors (uniforms) after the material is created; changes don't affect the material.
In the bottom of the code, I've added a gui button which switches colors (uniforms) of material when pressed. I've tried material needs update, elements need update and all the other updates presented by documentation but changes didn't be applied.
So how can I change those uniforms after material is created.