【Godot4.2】简单调色板生成

概述

Godot内部提供了ColorPickerColorPickerButton两个颜色选择控件,也提供了GradientTexture1DGradientTexture2D,来实现参数化的渐变图片。
但是有时候我们可能需要用Godot自己编写一些绘图程序,可能ColorPickerColorPickerButton就显得比较Low了,我们可能想自己实现调色板或者色环之类的。
本篇旨在阐述如何用简单的GDScript代码形式生成调色板,通过TextureRect控件来呈现,并进取色交互。

基础原理

通过构造Image,然后填色或设定每个像素颜色,然后再构造ImageTexture资源,就可以将程序生成的图片赋值给控件的Texture属性。这是本篇的基础。
我们创建一个用户界面场景,添加一个TextureRect控件:
image.png
TextureRect控件添加如下代码:

extends TextureRect


func _ready():
	# 构造Image
	var img = Image.new()
	img = img.create(100,100,true,Image.FORMAT_RGBA8)  # 限定尺寸和格式
	img.fill(Color.YELLOW_GREEN) # 填充纯色
	# 从Image创建ImageTexture,并赋值给TextureRect
	texture = ImageTexture.create_from_image(img)

上面的代码动态创建一张100×100px带有RGBA信息的图片,然后用Imagefill()方法,填充纯色。
运行场景后,就可以看到一个TextureRect显示了一张被填充为黄绿色的程序生成图片。
image.png
如果你还记得我在【Godot4.2】图片处理函数库 - textureDB一文中贴出的代码,生成纯色填充图片被写成了一个函数color_block。所以通过简单的函数封装,代码就变成了如下形式:

extends TextureRect


func _ready():
	texture = color_block(Vector2(100,100),Color.YELLOW_GREEN)


# 创建纯色块的ImageTexture
func color_block(size:Vector2,color:Color=Color.WHITE) -> ImageTexture:
	var img = Image.new()
	img = img.create(size.x,size.y,false,Image.FORMAT_RGBA8)
	img.fill(color)
	return ImageTexture.create_from_image(img)

fill()方法与遍历图片的每个像素,然后用set_pixel()赋予同一个颜色是等价的。
等价函数:

# 创建纯色块的ImageTexture
func color_block(size:Vector2,color:Color=Color.WHITE) -> ImageTexture:
	var img = Image.new()
	img = img.create(size.x,size.y,false,Image.FORMAT_RGBA8)
	# 遍历每一个像素
	for x in range(size.x):
		for y in range(size.y):
			img.set_pixel(x,y,Color.YELLOW_GREEN) # 设定该像素的颜色值
	return ImageTexture.create_from_image(img)

实现颜色渐变

我们编写一个函数gradient用来生成两种颜色的线性渐变。
同样我们需要像上面一样遍历图片的每一个像素,并设定颜色,而颜色值我们用全局函数leap进行线性插值,插值的比例我们采用x/100.0的水平坐标百分比。

extends TextureRect


func _ready():
	texture = gradient(Vector2(100,100))


# 一维渐变图片
func gradient(size:Vector2,color1:Color=Color.BLACK,color2:Color=Color.WHITE) -> ImageTexture:
	var img = Image.new()
	img = img.create(size.x,size.y,false,Image.FORMAT_RGBA8)
	# 遍历每一个像素
	for x in range(size.x):
		for y in range(size.y):
			img.set_pixel(x,y,lerp(color1,color2,x/100.0)) # 设定该像素的颜色值
	return ImageTexture.create_from_image(img)

默认我们只传入生成的图片尺寸,则会生成一个由黑到白的线性渐变图:
image.png
我们传入自定义的两种颜色,比如黄色和黄绿色:

texture = gradient(Vector2(100,100),Color.YELLOW,Color.YELLOW_GREEN)

则会生成如下的颜色渐变纹理。
image.png
这与Godot内置的gradientTexture1D效果是一致的。
image.png
image.png

基于GradientTexture1D和Gradient资源的版本

# 一维渐变图片
func gradient(size:Vector2,color1:Color=Color.BLACK,color2:Color=Color.WHITE) -> GradientTexture1D:
	var texture  = GradientTexture1D.new()
	# 构建Gradient资源
	var grad = Gradient.new()
	grad.set_color(0.0,color1)  # 在起始处添加颜色1
	grad.set_color(1.0,color2)  # 在末尾处添加颜色2
	texture.gradient = grad
	return texture

基于GradientTexture1DGradient资源创建渐变,好处是可以添加更多的颜色。

模仿ColorPicker控件的调色板

实现目标颜色的饱和度和亮度调制

下图是Godot内置的颜色选择控件——ColorPicker。其右侧的窄带选择色相,左侧的调色板则对同一色相进行不同亮度和饱和度的调制。
image.png
这里我们首先实现左侧,对同一色相进行不同亮度和饱和度的调制。

extends TextureRect

# 要调制亮度和饱和度的颜色
@export var color:Color = Color.RED

func _ready():
	# 构造Image
	var img = Image.new()
	img = img.create(100,100,true,Image.FORMAT_RGBA8)
	# 遍历像素进行填色
	for x in range(100):
		for y in range(100):
			var color_x = lerp(Color.WHITE,color,x/100.0)      # 水平饱和度逐渐增加
			var color_y = lerp(color_x,Color.BLACK,y/100.0)    # 垂直亮度逐减
			img.set_pixel(x,y,color_y)
	# 生成并显示图片
	texture = ImageTexture.create_from_image(img)

默认红色调制出的图片如下:
image.png
这里代码使用了导出变量color用于指定要调制的目标颜色,所以我们只需要修改一下,就可以调制任何其他颜色的色板出来。
image.png
image.png

实现目标颜色选择色带

接着我们实现右侧窄带部分,这部分的用于选择基础的目标颜色。
image.png

写法1

我这里用最笨的方法就是观察ColorPicker这个目标颜色部分是由纯红色逐渐到黄色再到绿色…,然后就用RGB三色混合的方式模拟了一遍这个过程:

extends TextureRect

func _ready():
	var img = Image.new()
	img = img.create(10,255*6,true,Image.FORMAT_RGBA8)
	
	for x in range(10.0):
		# 红色逐渐混合绿色 -- 由红变黄
		for val in range(255):
			img.set_pixel(x,val,Color8(255,val,0))
		# 从红绿混合中逐渐减去红色比重 -- 由黄变绿
		for val in range(255):
			img.set_pixel(x,val + 255,Color8(255 - val,255,0))
		# 绿色逐渐混合蓝色
		for val in range(255):
			img.set_pixel(x,val + 255 * 2,Color8(0,255,val))
		# 蓝绿混合中逐渐减去绿色比重
		for val in range(255):
			img.set_pixel(x,val + 255 * 3,Color8(0,255-val,255))
		# 蓝色逐渐混合红色 -- 由蓝变紫
		for val in range(255):
			img.set_pixel(x,val + 255 * 4,Color8(val,0,255))
		# 红蓝混合色中逐渐减去蓝色比重 -- 由紫变红
		for val in range(255):
			img.set_pixel(x,val + 255 * 5,Color8(255,0,255-val))
		
	texture = ImageTexture.create_from_image(img)

生成的效果:
image.png

写法2

上面的代码写的不是太简洁,所以代码可以简化如下:

extends TextureRect

func _ready():
	var img = Image.new()
	img = img.create(10,100 * 6,true,Image.FORMAT_RGBA8)
	
	var colors = [Color(1,0,0),Color(1,1,0),Color(0,1,0),Color(0,1,1),Color(0,0,1),Color(1,0,1),Color(1,0,0)]
	
	for i in range(colors.size()):
		if i+1 < colors.size():
			var color_A = colors[i]
			var color_B = colors[i+1]
			for x in range(10.0):
				# 红色逐渐混合绿色
				for val in range(100.0):
					img.set_pixel(x,val + 100 * i,lerp(color_A,color_B,val/100.0))
		
	texture = ImageTexture.create_from_image(img)
写法3

基于GradientTexture1DGradient资源创建的写法:

extends TextureRect


func _ready():
	var colors = [Color(1,0,0),Color(1,1,0),Color(0,1,0),Color(0,1,1),Color(0,0,1),Color(1,0,1),Color(1,0,0)]
	# 构造Image
	var grad_texture = GradientTexture2D.new()
	# 垂直方向进行渐变
	grad_texture.fill_from = Vector2(0,0)
	grad_texture.fill_to = Vector2(0,1)
	# 构造Gradient资源
	var grad = Gradient.new()
	# 删除默认的两个点
	grad.remove_point(1)
	grad.remove_point(0)
	# 添加新的点
	for i in range(colors.size()):
		grad.add_point(float(i)/6.0,colors[i])
	
	# 赋值
	grad_texture.gradient = grad
	# 显示
	texture = grad_texture

自定义控件与颜色选择交互演示

通过上面探讨的基础,我们已经可以实现自己的颜色选择器功能,为了让代码清晰,我封装了ColorsBarHSBRect两个自定义控件。
代码分别如下:

ColorsBar源码

# ======================================================================
# 名称:ColorsBar
# 类型:自定义控件
# 描述:创建一个色相选择的渐变图,可以选择RGB混合模式下所有可能值的颜色
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:20243910:31:42
# 最后修改时间:202431019:31:46
# ======================================================================
@tool
class_name ColorsBar extends TextureRect

signal color_changed(color:Color)  # 选择颜色后触发

# ============================= 参数 =============================
var pos:Vector2    # 鼠标位置
var can_move:bool  # 能否移动
# ============================= 虚函数 =============================
# 初始化
func _init():
	create_colors()

# 处理鼠标交互
func _gui_input(event):
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT:  # 鼠标左键
			can_move = event.is_pressed()            # 按下开启can_move
			if can_move:
				get_color(event)
	
	if event is InputEventMouseMotion:               # 鼠标移动
		if can_move:
			get_color(event)
# 获取颜色
func get_color(event:InputEvent):
	var img:Image = texture.get_image()
	# 记录鼠标局部坐标
	pos.y = clamp(event.position.y,0,size.y-1)
	pos.x = clamp(event.position.x,0,size.x)
	# 触发color_changed信号,返回获取的目标颜色
	emit_signal("color_changed",img.get_pixelv(pos))
	# 请求重绘
	queue_redraw()

func _draw():
	# 绘制白色矩形框
	var rect = Rect2(Vector2(-1,pos.y-1),Vector2(size.x+1,3))
	draw_rect(rect,Color.WHITE,false,1)

# ============================= 核心函数 =============================
# 创建可选颜色
func create_colors():
	var colors = [Color(1,0,0),Color(1,1,0),Color(0,1,0),Color(0,1,1),Color(0,0,1),Color(1,0,1),Color(1,0,0)]
	# 构造Image
	var grad_texture = GradientTexture2D.new()
	# 修改GradientTexture2D的图片尺寸,与TextureRect尺寸保持一致,方便鼠标交互时采样
	grad_texture.width = size.x
	grad_texture.height = size.y
	# 垂直方向进行渐变
	grad_texture.fill_from = Vector2(0,0)
	grad_texture.fill_to = Vector2(0,1)
	# 构造Gradient资源
	var grad = Gradient.new()
	# 删除默认的两个点
	grad.remove_point(1)
	grad.remove_point(0)
	# 添加新的点
	for i in range(colors.size()):
		grad.add_point(float(i)/6.0,colors[i])
	
	# 赋值
	grad_texture.gradient = grad
	# 显示
	texture = grad_texture

HSBRect源码

# ======================================================================
# 名称:HSBRect
# 类型:自定义控件
# 描述:基于给定颜色,进行亮度和饱和度调制,生成一个矩形的颜色渐变区域,
# 可以用鼠标交互进行颜色选择
# 作者:巽星石
# Godot版本:v4.2.1.stable.official [b09f793f5]
# 创建时间:20243910:31:42
# 最后修改时间:202431019:31:46
# ======================================================================
@tool
class_name HSBRect extends TextureRect

signal color_changed(color:Color)  # 选择颜色后触发

# ============================= 参数 =============================
pos:Vector2 = Vector2(size.x-1,0)    # 鼠标位置
var can_move:bool  # 能否移动

# 要调制亮度和饱和度的颜色
@export var color:Color = Color.RED:
	set(val):
		color = val
		create_hsb()
		# 触发color_changed信号
		var img:Image = texture.get_image()
		emit_signal("color_changed",img.get_pixelv(pos))

# ============================= 虚函数 =============================
# 初始化
func _init():
	create_hsb()

# 处理鼠标交互
func _gui_input(event):
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT:  # 鼠标左键
			can_move = event.is_pressed()            # 按下开启can_move
			if can_move:
				get_color(event)
	
	if event is InputEventMouseMotion:               # 鼠标移动
		if can_move:
			get_color(event)

func _draw():
	# 绘制白色矩形框
	var rect = Rect2(Vector2(pos.x-1,pos.y-1),Vector2(3,3))
	draw_rect(rect,Color.WHITE,false,1)

# 获取颜色
func get_color(event:InputEvent):
	var img:Image = texture.get_image()
	# 记录鼠标局部坐标
	pos.y = clamp(event.position.y,0,size.y-1)
	pos.x = clamp(event.position.x,0,size.x-1)
	# 触发color_changed信号,返回获取的目标颜色
	emit_signal("color_changed",img.get_pixelv(pos))
	# 请求重绘
	queue_redraw()

# ============================= 核心函数 =============================
# 创建由色相、饱和度和亮度渐变而成的区域
func create_hsb():
	# 构造Image
	var img = Image.new()
	img = img.create(size.x,size.y,true,Image.FORMAT_RGBA8)
	# 遍历像素进行填色
	for x in range(size.x):
		for y in range(size.y):
			var color_x = lerp(Color.WHITE,color,x/size.x)      # 水平饱和度逐渐增加
			var color_y = lerp(color_x,Color.BLACK,y/size.y)    # 垂直亮度逐减
			img.set_pixel(x,y,color_y)
	# 生成并显示图片
	texture = ImageTexture.create_from_image(img)

使用测试

我们创建一个测试场景,分别添加ColorsBarHSBRect实例,以及一个ColorRect
image.png
场景根节点代码如下:

extends Control

@onready var colors_bar = $ColorsBar   # 颜色选择条
@onready var hsb_rect = $HSBRect       # 色相-饱和度-亮度混色盘
@onready var color_rect = $ColorRect

# 颜色选择条选择颜色后HSBRect的色相发生相应变化
func _on_colors_bar_color_changed(color):
	hsb_rect.color = color

# HSBRect选择颜色后,在ColorRect显示选择的颜色
func _on_hsb_rect_color_changed(color):
	color_rect.color = color

主要是处理ColorsBarHSBRectcolor_changed信号,让ColorsBarHSBRect以及ColorRect三者联动起来。
运行后的效果:
在这里插入图片描述

gif图有点失真:
在这里插入图片描述

  • 19
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巽星石

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值