Created
January 18, 2022 11:48
-
-
Save kyechan99/514716cfdeba0e9425614c7f080c40a7 to your computer and use it in GitHub Desktop.
AFRAME colorpicker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AFRAME.registerComponent('colorpicker', { | |
schema: { | |
colorWheel: { type: 'selector', default: '#colorWheel' }, | |
lightWheel: { type: 'selector', default: '#lightWheel' } | |
}, | |
init: function() { | |
this.color = '#FFFFFF'; | |
this.lightness = 1; | |
this.h = 0; | |
this.s = 0; | |
this.initWheel(); | |
this.initCursor(); | |
this.initListener(); | |
}, | |
hslToHex: function(h,s,l) { | |
let r, g, b; | |
if (s === 0) { | |
r = g = b = l; // achromatic | |
} else { | |
const hue2rgb = (p, q, t) => { | |
if (t < 0) t += 1; | |
if (t > 1) t -= 1; | |
if (t < 1 / 6) return p + (q - p) * 6 * t; | |
if (t < 1 / 2) return q; | |
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; | |
return p; | |
}; | |
const q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
const p = 2 * l - q; | |
r = hue2rgb(p, q, h + 1 / 3); | |
g = hue2rgb(p, q, h); | |
b = hue2rgb(p, q, h - 1 / 3); | |
} | |
const toHex = x => { | |
const hex = Math.round(x * 255).toString(16); | |
return hex.length === 1 ? '0' + hex : hex; | |
}; | |
return `#${toHex(r)}${toHex(g)}${toHex(b)}`; | |
}, | |
initWheel: function() { | |
this.data.colorWheel.getObject3D('mesh').material = new THREE.ShaderMaterial({ | |
uniforms: { | |
brightness: { | |
type: 'f', | |
value: this.lightness | |
} | |
}, | |
vertexShader: '\ | |
varying vec2 vUv;\ | |
void main() {\ | |
vUv = uv;\ | |
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);\ | |
gl_Position = projectionMatrix * mvPosition;\ | |
}\ | |
', | |
fragmentShader: '\ | |
#define M_PI2 6.28318530718\n \ | |
uniform float brightness;\ | |
varying vec2 vUv;\ | |
vec3 hsb2rgb(in vec3 c){\ | |
vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, \ | |
0.0, \ | |
1.0 );\ | |
rgb = rgb * rgb * (3.0 - 2.0 * rgb);\ | |
return c.z * mix( vec3(1.0), rgb, c.y);\ | |
}\ | |
\ | |
void main() {\ | |
vec2 toCenter = vec2(0.5) - vUv;\ | |
float angle = atan(toCenter.y, toCenter.x);\ | |
float radius = length(toCenter) * 2.0;\ | |
vec3 color = hsb2rgb(vec3((angle / M_PI2) + 0.5, radius, brightness));\ | |
gl_FragColor = vec4(color, 1.0);\ | |
}\ | |
' | |
}); | |
this.data.lightWheel.getObject3D('mesh').material = new THREE.ShaderMaterial({ | |
uniforms: { | |
color1: { | |
value: new THREE.Color("black") | |
}, | |
color2: { | |
value: new THREE.Color("white") | |
} | |
}, | |
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);\ | |
}\ | |
' | |
}); | |
}, | |
initCursor: function() { | |
this.colorCursor = document.createElement('a-entity'); | |
this.lightCursor = document.createElement('a-entity'); | |
const cursorGeo = new THREE.TorusBufferGeometry(0.025, 0.005, 2, 18); | |
this.cursorMat = new THREE.MeshBasicMaterial({ | |
color: '#000', | |
transparent: true | |
}); | |
this.colorCursor.setObject3D('mesh', new THREE.Mesh(cursorGeo, this.cursorMat)); | |
this.lightCursor.setObject3D('mesh', new THREE.Mesh(cursorGeo, this.cursorMat)); | |
this.colorCursor.setAttribute('position', { x: 0, y: 0, z: 0.01 }); | |
this.lightCursor.setAttribute('position', { x: 0, y: 1, z: 0.01 }); | |
this.data.colorWheel.appendChild(this.colorCursor); | |
this.data.lightWheel.appendChild(this.lightCursor); | |
}, | |
initListener: function() { | |
this.data.colorWheel.addEventListener("click", (e)=>{ | |
let point = e.detail.intersection.uv | |
point.x = point.x * 2 - 1 | |
point.y = point.y * 2 - 1 | |
var polarPosition = { | |
r: Math.sqrt(point.x * point.x + point.y * point.y), | |
theta: Math.PI + Math.atan2(point.y, point.x) | |
}; | |
var angle = ((polarPosition.theta * (180 / Math.PI)) + 180) % 360; | |
this.h = angle / 360; | |
this.s = polarPosition.r; | |
this.recalculationColor(); | |
this.colorCursor.setAttribute('position', { | |
x: point.x, | |
y: point.y, | |
z: 0.01 | |
}); | |
}); | |
this.data.lightWheel.addEventListener("click", (e) => { | |
let pointY = e.detail.intersection.uv.y; | |
this.lightness = pointY; | |
this.data.colorWheel.getObject3D('mesh').material.uniforms.brightness.value = this.lightness; | |
this.recalculationColor(); | |
this.lightCursor.setAttribute('position', { | |
x: 0, | |
y: pointY * 2 - 1, | |
z: 0.01 | |
}); | |
if (pointY < 0.6) | |
this.cursorMat.color = new THREE.Color('#FFF'); | |
else | |
this.cursorMat.color = new THREE.Color('#000'); | |
}); | |
}, | |
recalculationColor: function() { | |
this.color = this.hslToHex(this.h, this.s, this.lightness - this.s * (0.6 * this.lightness)); | |
this.el.dispatchEvent( new CustomEvent('color_changed', { | |
detail: { | |
color: this.color | |
} | |
})); | |
} | |
}); | |
/* | |
Use in HTML | |
<a-entity | |
secondary-hand="colorpicker: #colorpicker;" | |
oculus-touch-controls="hand: left; model:false" | |
> | |
<a-entity colorpicker="colorWheel: #colorWheel; lightWheel: #lightWheel;" id="colorpicker" class="wheels"> | |
<a-circle id="colorWheel" position="0 0 -3" rotation="0 0 0" class="wheels"></a-circle> | |
<a-plane id="lightWheel" position="1.2 0 -3" width="0.1" height="2" class="wheels"></a-plane> | |
</a-entity> | |
</a-entity> | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment