I have the following code on JSFiddle: https://jsfiddle.net/gentleman_goat66/o5wn3bpf/215/
What I want is the look on the red/green box but with the borders of the purple box. I created one with BoxGeometry
(the purple one) and the other (the red/green) manually created with vertices and fed to BufferGeometry
.
I want the pink box on this JSFiddle to keep it's borders but have the red/green gradient effect.
I created a custom shader which adds a nice black border around the geometry by taking advantage of the edges of UVs, but I can't figure out how to get it to create the red to yellow to green gradient effect that is possible with the MeshStandardMaterial
You can see I was able to sort of create a gradient by using the vUv.y
but the top face does not align when I do this and not sure how to get it to go to another color?
How can I modify the UVs and Shadercode to create this effect with my current custom shader?
Put another way, refer to this different JSFiddle: (https://jsfiddle.net/gentleman_goat66/o5wn3bpf/216/)
I want the pink box on this JSFiddle to keep it's borders but have the red/green gradient effect.
HTML
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <specularmap_pars_fragment>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
varying vec2 vUv;
void main() {
#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
vUv = uv;
//gl_Position = vec4( transformed, 1.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
varying vec2 vUv;
uniform float thickness;
float edgeFactor(vec2 p){
vec2 grid = abs(fract(p - 0.5) - 0.5) / fwidth(p) / thickness;
return min(grid.x, grid.y);
}
void main() {
float a = edgeFactor(vUv);
vec3 c = mix(vec3(0), vec3(0.1), a);
if(a >= 1.0){
//float changex = vUv.x;
//float changey = vUv.y;
//if(vUv.x > 0.5){
//changex = vUv.x - 0.5;
//}
//if(vUv.y > 0.5){
//changey = vUv.y - 0.5;
//}
c = vec3(1.0,vUv.y,1.0);
}
gl_FragColor = vec4(c, 1.0);
}
</script>
<div id="container">
</div>
JS
import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
class Heatmap {
constructor(){
console.log("Heatmap - constructor()");
//ThreeJS Variables
this.camera = null;
this.scene = null;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.orbital_controls = null;
this.ambientLight = null;
this.heatmap = new THREE.Object3D(); //This will hold all the meshes that make up the heatmap
//Animation Related Variables
this.clock = new THREE.Clock();
this.delta = 0;
this.fps = 60; //60 fps
this.interval = 1/this.fps;
this.seconds = 2; //seconds for animation
this.timeSoFar = 0;
this.targetTime = 3;
//Setup
this.scene = new THREE.Scene();
this.setUpCamera();
this.setUpRenderer();
this.setUpLighting();
this.setUpOrbitalControls();
//Add optional GridHelper for Debug
this.scene.add(new THREE.GridHelper());
//TODO: Create the heatmap here
this.createDataRectangle();
//Add the finished heatmap to the scene
this.scene.add(this.heatmap);
this.render();
this.animate();
}
setUpRenderer(){
console.log("Heatmap - setUpRenderer()");
let width = $('#container').width();
let height = $('#container').height();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width,height);
this.renderer.setClearColor(0x404040);
$("#container").html(this.renderer.domElement);
window.addEventListener("resize", event => {
this.camera.aspect = innerWidth / innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(innerWidth, innerHeight);
});
}
setUpCamera(){
console.log("Heatmap - setUpCamera()");
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);
this.camera.position.z = 4;
}
setUpLighting(){
console.log("Heatmap - setUpLighting()");
this.ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(this.ambientLight);
}
setUpOrbitalControls(){
console.log("Heatmap - setUpOrbitalControls()");
this.orbital_controls = new OrbitControls( this.camera, this.renderer.domElement );
this.orbital_controls.target.set(0, 0, 0);
this.orbital_controls.enableDamping = true;
this.orbital_controls.dampingFactor = 0.025;
this.orbital_controls.maxPolarAngle = Math.PI/2.0;
this.orbital_controls.minDistance = 2.0;
this.orbital_controls.maxDistance = 80.0;
this.orbital_controls.update();
this.orbital_controls.addEventListener( 'change', function(){
if (this.target.y < 0){
this.target.y = 0;
/* camera.position.y = -1; */
} else if (this.target.y > 1){
this.target.y = 1;
/* camera.position.y = 1; */
}
});
}
createDataRectangle(){ //TODO: Make this create at a position specified
console.log("Heatmap - createDataRectangle()");
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, 0, 1,//0, floor, bottom left
-1, 0, 1,//1, floor, bottom right
-1, 0, -1,//2, floor, top right
-2, 0, -1,//3 floor, top left
-2, 3, 1,//4 roof, bottom left
-1, 3, 1,//5 roof, bottom right
-1, 3, -1,//6 roof, top right
-2, 3, -1//7 roof, top left
];
const indices = [
0, 1, 2,//floor, first triangle
2, 3, 0,//floor, second triangle
4, 5, 6,//roof, first triangle
6, 7, 4,//roof, second triangle
3, 0, 4,//west wall, first triangle
3, 4, 7,//west wall, second triangle
0, 1, 5,//south wall, first triangle
0, 5, 4,//south wall, first triangle
1, 2, 6,//east wall, first triangle
1, 6, 5,//east wall, second triangle
2, 3, 7,//north wall, first triangle
2, 7, 6,//north wall, second triangle
];
const colors = [
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0, // top left
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0 // top left
];
var quad_uvs = [
0, 1,
0, 0,
1, 0,
1, 1,
0, 0,
1, 0,
1, 1,
0, 1
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.setAttribute('uv', new THREE.Float32BufferAttribute( quad_uvs, 2 ) );
geometry.computeVertexNormals();
console.log(geometry);
const material = new THREE.MeshStandardMaterial({side:THREE.FrontSide,vertexColors:true,color: 0xFFFFFF });
var shader_material = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
uniforms: {
thickness: {
value: 0.5
}
}
});
const sample_rectangle = new THREE.Mesh(geometry, material);
this.heatmap.add(sample_rectangle);
//Demo showing correct effect
const geometryBox = new THREE.BoxGeometry();
console.log(geometryBox);
const sample_box = new THREE.Mesh(geometryBox, shader_material);
sample_box.geometry.computeVertexNormals();
sample_box.scale.y = 3;
this.heatmap.add(sample_box);
}
animate(){
requestAnimationFrame(() => {
this.animate()
});
this.orbital_controls.update();//Required for Damping on OrbitControls
this.delta += this.clock.getDelta();
if (this.delta > this.interval) {
//this.timeSoFar = this.timeSoFar + (1 * morphDirection);
this.render();
this.delta = this.delta % this.interval;
}
}
render(){
this.renderer.render(this.scene, this.camera);
}
}
$(document).ready(function(){
console.log("DOM ready!");
new Heatmap();
});
CSS
body,html{
margin: 0;
width: 100%;
height: 100%;
}
#container {
width: 100%;
height: 100%;
background-color: black;
margin: 0;
}
UPDATE:
Here is an example of how I want the color with boxGeometry
as well:
https://jsfiddle.net/gentleman_goat66/o5wn3bpf/249/
I just need that to work with the shader now.
I solved the issue, the key is that the color vector information has to be passed in correctly and the shader has to get the attribute color and work with it accordingly.
Refer to my JSFiddle here: https://jsfiddle.net/gentleman_goat66/o5wn3bpf/249/
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <specularmap_pars_fragment>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
varying vec2 vUv;
attribute vec3 color;
varying vec3 vColor;
void main() {
#include <beginnormal_vertex>
#include <morphnormal_vertex>
#include <begin_vertex>
#include <morphtarget_vertex>
vUv = uv;
vColor = color;
//gl_Position = vec4( transformed, 1.0 );
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
varying vec2 vUv;
varying vec3 vColor;
uniform float thickness;
float edgeFactor(vec2 p){
vec2 grid = abs(fract(p - 0.5) - 0.5) / fwidth(p) / thickness;
return min(grid.x, grid.y);
}
void main() {
float a = edgeFactor(vUv);
vec3 c = mix(vec3(0), vec3(0.1), a);
if(a >= 1.0){
c = vec3(vColor.r,vColor.g,vColor.b);
}
gl_FragColor = vec4(c, 1.0);
}
</script>
<div id="container">
</div>