Compare commits
2 commits
3bc442f8c7
...
69e368725a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69e368725a | ||
|
|
654209442c |
3 changed files with 250 additions and 96 deletions
288
colorpicker.wren
288
colorpicker.wren
|
|
@ -5,115 +5,175 @@ import "luxe: draw" for PathStyle
|
|||
import "luxe: math" for Math
|
||||
import "luxe: color" for Color
|
||||
|
||||
var Color_spaces = [
|
||||
{
|
||||
"name": "RGB",
|
||||
"components": ["r", "g", "b"],
|
||||
"componentsFull": ["Red", "Green", "Blue"],
|
||||
},
|
||||
{
|
||||
"name": "HSV",
|
||||
"components": ["h", "s", "v"],
|
||||
"componentsFull": ["Hue", "Saturation", "Value"],
|
||||
},
|
||||
{
|
||||
"name": "HSL",
|
||||
"components": ["h", "s", "l"],
|
||||
"componentsFull": ["Hue", "Saturation", "Lightness"],
|
||||
},
|
||||
{
|
||||
"name": "Oklab",
|
||||
"components": ["L", "a", "b"],
|
||||
"componentsFull": ["Lightness", "red/green", "blue/yellow"],
|
||||
}
|
||||
]
|
||||
|
||||
var Modes = [
|
||||
//I wanted to do unicode icons but it doesnt render with the current font
|
||||
{
|
||||
"name": "Triangle",
|
||||
"icon": "tri", //▲
|
||||
},
|
||||
{
|
||||
"name": "Square",
|
||||
"icon": "box", //■
|
||||
},
|
||||
{
|
||||
"name": "Circle",
|
||||
"icon": "round", //●
|
||||
}
|
||||
]
|
||||
|
||||
class ColorPicker{
|
||||
static create(ui: Entity) : Control{
|
||||
//parameters - we could expose some of this if we wanted to
|
||||
var base_color = [1, 0, 0, 1]
|
||||
var base_hsv = Color.rgb2hsv(base_color)
|
||||
var base_value_gamma = 1.6
|
||||
var base_sat_gamma = 0.8
|
||||
var triangle_size = 35 //this is the inner triangle radius
|
||||
var outer_ring_size = 200
|
||||
var inner_ring_size = 140
|
||||
|
||||
//setup root
|
||||
var panel = UIWindow.create(ui)
|
||||
Control.set_size(panel, 350, 400)
|
||||
Control.set_id(panel, "panel.%(ID.unique())")
|
||||
Control.set_state_data(panel, [1, 0, 0, 1])
|
||||
//Control.set_state_data(panel, base_color) //turns out UIWindows use their own state, whoops
|
||||
|
||||
var color_view = Control.create(ui)
|
||||
Control.set_behave(color_view, UIBehave.fill)
|
||||
Control.set_margin(color_view, 0, 40, 0, 0)
|
||||
Control.set_behave(color_view, UIBehave.fill)
|
||||
Control.set_margin(color_view, 0, 40, 0, 0)
|
||||
Control.child_add(panel, color_view)
|
||||
Control.set_id(panel, "color_view.%(ID.unique())")
|
||||
Control.set_state_data(color_view, base_color) //stores state "golbally" for multiple elements in the colorpicker to access
|
||||
|
||||
var hsv_view = Control.create(ui)
|
||||
Control.set_behave(hsv_view, UIBehave.fill)
|
||||
Control.set_margin(hsv_view, 0, 0, 0, 0)
|
||||
Control.child_add(color_view, hsv_view)
|
||||
Control.set_id(panel, "hsv_view.%(ID.unique())")
|
||||
|
||||
var color_wheel = Control.create(ui)
|
||||
Control.set_size(color_wheel, 200, 200)
|
||||
Control.child_add(color_view, color_wheel)
|
||||
Control.set_state_data(color_wheel, {"ring":null, "triangle":null, "hue": 0})
|
||||
Control.set_size(color_wheel, outer_ring_size, outer_ring_size)
|
||||
Control.child_add(hsv_view, color_wheel)
|
||||
//the wheel has a bunch of state to have stuff be more solid (for example not reset hue when going to [0,0,0,_]) and interaction state
|
||||
Control.set_state_data(color_wheel, {"ring":null, "triangle":null,
|
||||
"hue": base_hsv.x, "value": base_hsv.z, "saturation": base_hsv.y})
|
||||
Control.set_process(color_wheel){|control, state, event, x,y,w,h|
|
||||
//this might not be nessecary anymore?
|
||||
if(event.control != control) return
|
||||
if(event.type == UIEvent.move){
|
||||
x = Control.get_pos_x_abs(control)
|
||||
y = Control.get_pos_y_abs(control)
|
||||
var center = [x + w/2, y + h/2]
|
||||
if(state["ring"] == "captured"){
|
||||
if(state["ring"] == "captured"){ //if we're editing the ring(hue)
|
||||
//hue from angle
|
||||
var diff = [event.x - center.x, event.y - center.y]
|
||||
var angle = Math.atan2(-diff.x, diff.y) + Num.pi
|
||||
var hue = angle / Num.tau
|
||||
|
||||
var color = Control.get_state_data(panel)
|
||||
var hsv = Color.rgb2hsv(color)
|
||||
hsv[0] = hue
|
||||
//change relevant values
|
||||
var color = Control.get_state_data(color_view)
|
||||
var hsv = [hue, state["saturation"], state["value"], color.a]
|
||||
state["hue"] = hue
|
||||
color = Color.hsv2rgb(hsv)
|
||||
Control.set_state_data(panel, color)
|
||||
Control.set_state_data(control, state)
|
||||
Control.set_state_data(color_view, color)
|
||||
UI.events_emit(control, UIEvent.change, state)
|
||||
UI.events_emit(panel, UIEvent.change, color)
|
||||
} else {
|
||||
UI.events_emit(color_view, UIEvent.change, color)
|
||||
} else if(state["triangle"] == "captured"){ //if we're editing the triangle (saturation & value)
|
||||
var diff = [event.x - center.x, event.y - center.y] //position rel to center
|
||||
Math.rotate(diff, 0, 0, state["hue"] * -360) //follow triangle rotation
|
||||
//constrain to triangle
|
||||
constrain_to_triangle(diff, triangle_size)
|
||||
|
||||
//get saturation based on distance to 2 edges
|
||||
var saturation = (-diff.y + triangle_size) / ((-diff.y + triangle_size) + (Math.dot2D(diff, dir_vec(Math.radians(30), 1)) + triangle_size))
|
||||
saturation = saturation.pow(base_sat_gamma)
|
||||
saturation = saturation.clamp(0, 1)
|
||||
|
||||
//similarly get value based on distance to other edge
|
||||
var value = Math.dot2D(diff, dir_vec(Math.radians(150), 1))
|
||||
value = value + triangle_size
|
||||
value = value / (triangle_size * 3)
|
||||
value = 1 - value
|
||||
value = value.pow(base_value_gamma)
|
||||
value = value.clamp(0, 1)
|
||||
|
||||
//calculate and apply relevant values
|
||||
var hue = state["hue"]
|
||||
var color = Control.get_state_data(color_view)
|
||||
var hsv = [hue, saturation, value, color.a]
|
||||
hsv[0] = hue
|
||||
state["value"] = value
|
||||
state["saturation"] = saturation
|
||||
color = Color.hsv2rgb(hsv)
|
||||
Control.set_state_data(control, state)
|
||||
Control.set_state_data(color_view, color)
|
||||
UI.events_emit(control, UIEvent.change, state)
|
||||
UI.events_emit(color_view, UIEvent.change, color)
|
||||
} else { //if we're not editing anything, lets check what we're hovering over!
|
||||
var distance = Math.dist2D(center, event)
|
||||
if(distance > 70 && distance < 100){
|
||||
//first reset hover state (can only be hover bc we checked for captured)
|
||||
state["ring"] = null
|
||||
state["triangle"] = null
|
||||
//then find actual state
|
||||
if(distance > inner_ring_size/2 && distance < outer_ring_size/2){
|
||||
state["ring"] = "hover"
|
||||
} else {
|
||||
state["ring"] = null
|
||||
} else if(distance < inner_ring_size/2) { //todo: triangle not round
|
||||
state["triangle"] = "hover"
|
||||
}
|
||||
}
|
||||
} else if(event.type == UIEvent.press && event.button == 1) {
|
||||
if(!state["ring"]) return
|
||||
state["ring"] = "captured"
|
||||
UI.capture(control)
|
||||
} else if(event.type == UIEvent.release && event.button == 1) {
|
||||
//if we click, check if we're hovering over anything and if so, change it to captured
|
||||
var hover_ring = state["ring"] != null //ring is hover (or captured)
|
||||
var hover_tri = state["triangle"] != null //triangle is hover (or captured)
|
||||
if(hover_ring || hover_tri) UI.capture(control) //if any of both happens, capture this
|
||||
if(hover_ring) state["ring"] = "captured"
|
||||
if(hover_tri) state["triangle"] = "captured"
|
||||
} else if(event.type == UIEvent.release && event.button == 1) { //if we release a click, let go
|
||||
x = Control.get_pos_x_abs(control)
|
||||
y = Control.get_pos_y_abs(control)
|
||||
var center = [x + w/2, y + h/2]
|
||||
//todo: avoid code dupe with hover code
|
||||
var distance = Math.dist2D(center, event)
|
||||
//first reset hover state (can only be hover bc we checked for captured)
|
||||
state["ring"] = null
|
||||
state["triangle"] = null
|
||||
//then find actual state
|
||||
if(distance > inner_ring_size/2 && distance < outer_ring_size/2){
|
||||
state["ring"] = "hover"
|
||||
} else if(distance < inner_ring_size/2) { //todo: triangle not round
|
||||
state["triangle"] = "hover"
|
||||
}
|
||||
UI.uncapture(control)
|
||||
}
|
||||
}
|
||||
Control.set_allow_input(color_wheel, true)
|
||||
//update values when some other UI that doesnt care about HSV changes the color
|
||||
Control.set_events(color_view) {|event|
|
||||
if(event.type == UIEvent.change){
|
||||
//construct wheel color
|
||||
var wheel_data = Control.get_state_data(color_wheel)
|
||||
var wheel_color = Color.hsv2rgb([wheel_data["hue"], wheel_data["saturation"], wheel_data["value"], 1])
|
||||
//ignore if its the same (we likely triggered this ourselves) (though this still fails often when 2 multiple land in the event queue)
|
||||
if(approx_rgb(wheel_color, event.change, 0.001)) return
|
||||
//update the wheel hsv if different
|
||||
var hsv = Color.rgb2hsv(event.change)
|
||||
//only change hue if saturation is nonzero and theres relevant change (saturation check is bc in rgb greyscale has no hue)
|
||||
if(hsv.y > 0.001 && !approx(wheel_data["hue"], hsv.x, 0.001)) wheel_data["hue"] = hsv.x
|
||||
//only change if theres relevant change
|
||||
if(!approx(wheel_data["saturation"], hsv.y, 0.001)) wheel_data["saturation"] = hsv.y
|
||||
if(!approx(wheel_data["value"], hsv.z, 0.001)) wheel_data["value"] = hsv.z
|
||||
//also trigger event in self to update stuff like image rotation
|
||||
UI.events_emit(color_wheel, UIEvent.change, wheel_data)
|
||||
}
|
||||
}
|
||||
|
||||
//ring visuals, interresting stuff happens in shader
|
||||
var color_ring = UIImage.create(ui)
|
||||
Control.set_size(color_ring, 200, 200)
|
||||
Control.set_size(color_ring, outer_ring_size, outer_ring_size)
|
||||
Control.set_contain(color_ring, UIContain.justify)
|
||||
var color_ring_mat = Material.create("materials/color_ring")
|
||||
Material.set_input(color_ring_mat, "wheel.outer_distance", 0.5)
|
||||
Material.set_input(color_ring_mat, "wheel.inner_distance", 0.5 * inner_ring_size / outer_ring_size)
|
||||
UIImage.set_material(color_ring, color_ring_mat)
|
||||
Control.child_add(color_wheel, color_ring)
|
||||
|
||||
//triangle visual, interresting stuff happens in shader
|
||||
var color_triangle = UIImage.create(ui)
|
||||
Control.set_margin(color_triangle, 30, 30, 0, 0)
|
||||
Control.set_size(color_triangle, 140, 140)
|
||||
Control.set_size(color_triangle, triangle_size * 4, triangle_size*4)
|
||||
Control.set_contain(color_triangle, UIContain.justify)
|
||||
var color_tri_mat = Material.create("materials/color_triangle")
|
||||
Material.set_input(color_tri_mat, "triangle.value_gamma", base_value_gamma)
|
||||
Material.set_input(color_tri_mat, "triangle.saturation_gamma", base_sat_gamma)
|
||||
Material.set_input(color_tri_mat, "triangle.hue", base_hsv.x)
|
||||
UIImage.set_material(color_triangle, color_tri_mat)
|
||||
Control.child_add(color_wheel, color_triangle)
|
||||
//rotates image when color wheel hue updates
|
||||
Control.set_events(color_wheel) {|event|
|
||||
if(event.type == UIEvent.change){
|
||||
var hue = event.change["hue"]
|
||||
|
|
@ -122,29 +182,60 @@ class ColorPicker{
|
|||
}
|
||||
}
|
||||
|
||||
//overlay does everything thats easier done with direct UI draw functions
|
||||
var color_wheel_overlay = Control.create(ui)
|
||||
Control.set_behave(color_wheel_overlay, UIBehave.fill)
|
||||
Control.set_margin(color_wheel_overlay, 0, 0, 0, 0)
|
||||
Control.set_render(color_wheel_overlay){|control, state, x, y, w, h|
|
||||
//prep data
|
||||
var depth = UI.draw_depth_of(control, 0)
|
||||
var center = [x + w/2, y + h/2]
|
||||
var style: PathStyle = PathStyle.new()
|
||||
var wheel_state = Control.get_state_data(color_wheel)
|
||||
var hover = wheel_state["ring"]
|
||||
|
||||
var hue = wheel_state["hue"]
|
||||
var angle = hue * Num.tau
|
||||
var direction = [angle.sin, -angle.cos]
|
||||
style.thickness = 4
|
||||
style.color = hover ? [0.5,0.5,0.5,1] : [0.22,0.22,0.22,1]
|
||||
var from = [center.x + direction.x * 70, center.y + direction.y * 70]
|
||||
var to = [center.x + direction.x * 100, center.y + direction.y * 100]
|
||||
UI.draw_line(control, from.x, from.y, to.x, to.y, depth, style)
|
||||
var color = Control.get_state_data(color_view)
|
||||
var wheel_data = Control.get_state_data(color_wheel)
|
||||
var hue = wheel_data["hue"]
|
||||
var value = wheel_data["value"].pow(1/base_value_gamma)
|
||||
var saturation = wheel_data["saturation"].pow(1/base_sat_gamma)
|
||||
|
||||
style.color = [0.22,0.22,0.22,1]
|
||||
//draw hue ring borders
|
||||
style.color = [0.22, 0.22, 0.22, 1]
|
||||
style.thickness = 2
|
||||
UI.draw_ring(control, center.x, center.y, depth, w/2, h/2, 0, 360, 8, style)
|
||||
UI.draw_ring(control, center.x, center.y, depth, w/2*0.7, h/2*0.7, 0, 360, 8, style)
|
||||
UI.draw_ring(control, center.x, center.y, depth, outer_ring_size/2, outer_ring_size/2, 0, 360, 8, style)
|
||||
UI.draw_ring(control, center.x, center.y, depth, inner_ring_size/2, inner_ring_size/2, 0, 360, 8, style)
|
||||
|
||||
//draw hue ring color rect
|
||||
style.thickness = 3
|
||||
if(wheel_data["ring"] != null) {
|
||||
style.color = [0.5, 0.5, 0.5, 1]
|
||||
} else {
|
||||
style.color = [0.22, 0.22, 0.22, 1]
|
||||
}
|
||||
var size = [20, 30]
|
||||
var angle_degree = hue * -360
|
||||
var angle_radian = hue * -Num.tau
|
||||
var avg_radius = (inner_ring_size + outer_ring_size) / 2 / 2
|
||||
var offset = [-angle_radian.sin * avg_radius - size.x/2, -angle_radian.cos * avg_radius - size.y/2]
|
||||
UI.draw_quad(control, center.x + offset.x, center.y + offset.y, depth, size.x, size.y, angle_degree, color)
|
||||
UI.draw_rect(control, center.x + offset.x, center.y + offset.y, depth, size.x, size.y, angle_degree, style)
|
||||
|
||||
//draw triangle color dot
|
||||
style.thickness = 3
|
||||
if(wheel_data["triangle"] != null) {
|
||||
style.color = [0.5, 0.5, 0.5, 1]
|
||||
} else {
|
||||
style.color = [0.22, 0.22, 0.22, 1]
|
||||
}
|
||||
var v_dist = (1 - value) * (triangle_size * 3) - triangle_size //distance in "value direction" in triangle
|
||||
var v_dir = dir_vec(Math.radians(150), 1) //"value direction"
|
||||
var v_norm = [-v_dir.y, v_dir.x] //direction orthogonal to "value direction"
|
||||
var width = value * triangle_size * 1.73205080756 //width (along value ortho) at current value value
|
||||
var pos = [v_dir.x * v_dist + (saturation*2-1) * width * v_norm.x, v_dir.y * v_dist + (saturation*2-1) * width * v_norm.y]
|
||||
//adjust for rotation
|
||||
Math.rotate(pos, 0, 0, hue * 360)
|
||||
var dot_size = 7
|
||||
UI.draw_circle(control, center.x + pos.x, center.y + pos.y, depth, dot_size, dot_size, 0, 360, 8, color)
|
||||
UI.draw_ring(control, center.x + pos.x, center.y + pos.y, depth, dot_size, dot_size, 0, 360, 8, style)
|
||||
}
|
||||
Control.child_add(color_wheel, color_wheel_overlay)
|
||||
|
||||
|
|
@ -225,15 +316,52 @@ class ColorPicker{
|
|||
*/
|
||||
}
|
||||
|
||||
static build_triangle_editor(ui: Entity, root: Control){
|
||||
Control.clear(root, UIClear.destroy)
|
||||
static approx_rgb(one: Color, other: Color, epsilson: Num) : Bool{
|
||||
return approx(one.x, other.x, epsilson) && approx(one.y, other.y, epsilson) && approx(one.z, other.z, epsilson)
|
||||
}
|
||||
|
||||
static build_square_editor(ui: Entity, root: Control){
|
||||
Control.clear(root, UIClear.destroy)
|
||||
static approx(one: Num, other: Num, epsilon: Num) : Bool{
|
||||
return (one - other).abs < epsilon
|
||||
}
|
||||
|
||||
static build_circle_editor(ui: Entity, root: Control){
|
||||
Control.clear(root, UIClear.destroy)
|
||||
//if this goes into the actual engine and not editor code this should take degree
|
||||
//but I like radians better :)
|
||||
static dir_vec(angle: Num, length: Num) : List{
|
||||
return [angle.cos * length, angle.sin * length]
|
||||
}
|
||||
|
||||
static constrain_to_triangle(pos: List, size: Num){
|
||||
//size is the inner radius, so we get half the base by multiplying with tan(60°)
|
||||
var base_width = size * 1.73205
|
||||
//first, we do the cheap calculation for [0, 1]
|
||||
var edge_dist = -(pos.y - size)
|
||||
if(edge_dist < 0){
|
||||
pos.y = size
|
||||
pos.x = pos.x.clamp(-base_width, base_width)
|
||||
return
|
||||
}
|
||||
//then, for the diagonal edges
|
||||
var edge_normal = dir_vec(Math.radians(30), 1)
|
||||
//first we check the distance from the edge
|
||||
edge_dist = Math.dot2D(pos, edge_normal)
|
||||
if(edge_dist < -size){
|
||||
//if the dot is outside the edge also get the position along the edge tangent so we can constrain it
|
||||
var edge_tangent = [edge_normal.y, -edge_normal.x]
|
||||
var tangent_dist = Math.dot2D(pos, edge_tangent).clamp(-base_width, base_width)
|
||||
//then construct the constrained position at the edge, but keeping the relative, constrained, tangent position
|
||||
pos.x = edge_normal.x * -size + edge_tangent.x * tangent_dist
|
||||
pos.y = edge_normal.y * -size + edge_tangent.y * tangent_dist
|
||||
return
|
||||
}
|
||||
//same as prev
|
||||
edge_normal = dir_vec(Math.radians(150), 1)
|
||||
edge_dist = Math.dot2D(pos, edge_normal)
|
||||
if(edge_dist < -size){
|
||||
var edge_tangent = [edge_normal.y, -edge_normal.x]
|
||||
var tangent_dist = Math.dot2D(pos, edge_tangent).clamp(-base_width, base_width)
|
||||
pos.x = edge_normal.x * -size + edge_tangent.x * tangent_dist
|
||||
pos.y = edge_normal.y * -size + edge_tangent.y * tangent_dist
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,12 +22,20 @@ material_basis = {
|
|||
layers = ["default"]
|
||||
inputs = {
|
||||
triangle.hue = {
|
||||
type = "float",
|
||||
value = 0.5,
|
||||
type = "float"
|
||||
value = 0.5
|
||||
},
|
||||
triangle.size = {
|
||||
type = "float",
|
||||
value = 0.25,
|
||||
type = "float"
|
||||
value = 0.25
|
||||
}
|
||||
triangle.value_gamma = {
|
||||
type = "float"
|
||||
value = 1
|
||||
}
|
||||
triangle.saturation_gamma = {
|
||||
type = "float"
|
||||
value = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,9 @@ input ColorWheel {
|
|||
|
||||
input ColorTriangle {
|
||||
float hue,
|
||||
float size
|
||||
float size,
|
||||
float value_gamma,
|
||||
float saturation_gamma
|
||||
}
|
||||
|
||||
stage vertex vert(
|
||||
|
|
@ -61,27 +63,36 @@ stage fragment frag_color_triangle(
|
|||
input { ColorTriangle triangle },
|
||||
fragment in { float4 color, float2 uv })
|
||||
{
|
||||
float TAU = 6.2831853071795864769252867665590057683943387987502116419498891846;
|
||||
float TAU = 6.283185307179586;
|
||||
float cos30 = 0.866025403784438; //cos(30°)
|
||||
float tan60 = 1.732050807568877;
|
||||
float tan60sq = 3.0;
|
||||
float sin30 = 0.5;
|
||||
|
||||
float2 centered_coords = in.uv - 0.5;
|
||||
float size = input.triangle.size;
|
||||
float2 centered_coords = in.uv - sin30;
|
||||
|
||||
float2 down = float2(0.0, -1.0);
|
||||
float2 top_right = float2(cos30, 0.5);
|
||||
float2 top_left = float2(-cos30, 0.5);
|
||||
float2 top_right = float2(cos30, sin30);
|
||||
float2 top_left = float2(-cos30, sin30);
|
||||
|
||||
float down_dist = dot(down, centered_coords);
|
||||
float top_right_dist = dot(top_right, centered_coords);
|
||||
float top_left_dist = dot(top_left, centered_coords);
|
||||
float down_dist = dot(down, centered_coords) + size;
|
||||
float top_right_dist = dot(top_right, centered_coords) + size;
|
||||
float top_left_dist = dot(top_left, centered_coords) + size;
|
||||
float dist = min(min(top_right_dist, top_left_dist), down_dist);
|
||||
dist += input.triangle.size;
|
||||
|
||||
float change = length(float2(dFdx(in.uv.x), dFdy(in.uv.y))); //this assumes the object is uniformly scaled
|
||||
float change = length(float2(dFdx(in.uv.x), dFdy(in.uv.y))); //this assumes the object is uniformly scaled (rotation is fine)
|
||||
float alpha = clamp(dist/change, 0.0, 1.0);
|
||||
|
||||
float3 hue = hue2rgb(input.triangle.hue);
|
||||
float hue = input.triangle.hue;
|
||||
float saturation = down_dist / (down_dist + top_right_dist);
|
||||
saturation = pow(saturation, input.triangle.saturation_gamma);
|
||||
float value = 1.0 - (top_left_dist / (size * tan60sq));
|
||||
value = pow(value, input.triangle.value_gamma);
|
||||
float3 color_rgb = hsv2rgb(float3(hue, saturation, value));
|
||||
//color_rgb = float3(fract(saturation)); //debug
|
||||
|
||||
float4 color = float4(float3(hue), alpha);
|
||||
float4 color = float4(color_rgb, alpha);
|
||||
|
||||
if(color.a <= 0.0) {
|
||||
discard;
|
||||
|
|
@ -91,6 +102,13 @@ stage fragment frag_color_triangle(
|
|||
stage.color[0] = color;
|
||||
}
|
||||
|
||||
float3 hsv2rgb(float3 hsv){
|
||||
float3 rgb = hue2rgb(hsv.x); //apply hue
|
||||
rgb = mix(float3(1.0), rgb, hsv.y); //apply saturation
|
||||
rgb = rgb * hsv.z; //apply value
|
||||
return rgb;
|
||||
}
|
||||
|
||||
float3 hue2rgb(float hue) {
|
||||
hue = fract(hue); //only use fractional part
|
||||
float r = abs(hue * 6.0 - 3.0) - 1.0; //red
|
||||
|
|
|
|||
Loading…
Reference in a new issue