mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-07-03 11:02:52 -04:00
a3ce30e449
Co-authored-by: Colin O'Rourke <Colin.ORourke@icloud.com>
208 lines
8.6 KiB
ReStructuredText
208 lines
8.6 KiB
ReStructuredText
.. _doc_drawable_textures:
|
|
|
|
Using DrawableTextures
|
|
======================
|
|
|
|
DrawableTextures are a type of Texture2D with additional functions for modifying
|
|
the texture via the GPU. This can be used for procedural texturing, real-time effects,
|
|
and much more.
|
|
|
|
Basic rectangle blitting
|
|
------------------------
|
|
|
|
The most basic operation on a drawable texture is
|
|
:ref:`blit_rect() <class_DrawableTexture2D_method_blit_rect>`.
|
|
Blitting (copying) the whole of one texture into the given rectangle on the ``DrawableTexture``.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
texture.blit_rect(Rect2(20, 50, 60, 60), preload("res://circle.svg"))
|
|
texture.blit_rect(Rect2(20, 50, 60, 60), preload("res://circle.svg"), Color.WHITE, 0)
|
|
|
|
The code above blits the circle texture into the rectangle ``(20, 50, 60, 60)``
|
|
on the ``DrawableTexture``. ``(20, 50)`` is the top left corner of the rectangle
|
|
being drawn to, and ``(60, 60)`` is its size. If you wanted to draw over the
|
|
whole texture, simply match the ``rect`` parameter to the ``DrawableTexture``'s
|
|
size.
|
|
|
|
The third parameter in ``blit_rect()`` is an optional parameter, ``modulate``.
|
|
This is a color that the output is multiplied by (in the default behavior). This
|
|
can be used to recolor, or even mask the drawn output of ``blit_rect()``. For
|
|
example, using a modulate of ``Color(0, 0, 1, 0)`` will only draw to and update
|
|
the blue values of each pixel on the ``DrawableTexture``.
|
|
|
|
``blit_rect()``'s fourth parameter, ``mipmap``, can specify a mipmap level to
|
|
draw to. You only need to use this if you want fine control over each mipmap
|
|
layer. Keep in mind, you do not need to adjust the rectangle size; it is
|
|
converted to a portion of the texture's whole size. If you just want to update
|
|
all the mipmap layers, you should draw to the texture, then call
|
|
``generate_mipmaps()`` on it.
|
|
|
|
Blend modes and texture blit shaders
|
|
------------------------------------
|
|
|
|
The drawing process for ``blit_rect()`` and DrawableTextures is governed by a
|
|
:ref:`texture blit shader <doc_texture_blit_shader>`. Even when the user does
|
|
not supply one, the engine has a default texture blit shader it uses.
|
|
|
|
.. code-block:: glsl
|
|
|
|
// Default texture blit shader.
|
|
|
|
shader_type texture_blit;
|
|
render_mode blend_mix;
|
|
|
|
uniform sampler2D source_texture0 : hint_blit_source0;
|
|
uniform sampler2D source_texture1 : hint_blit_source1;
|
|
uniform sampler2D source_texture2 : hint_blit_source2;
|
|
uniform sampler2D source_texture3 : hint_blit_source3;
|
|
|
|
void blit() {
|
|
// Copies from each whole source texture to a rect on each output texture.
|
|
COLOR0 = texture(source_texture0, UV) * MODULATE;
|
|
COLOR1 = texture(source_texture1, UV) * MODULATE;
|
|
COLOR2 = texture(source_texture2, UV) * MODULATE;
|
|
COLOR3 = texture(source_texture3, UV) * MODULATE;
|
|
}
|
|
|
|
The blend mode specified in ``render_mode`` defines how the output color value
|
|
is blended with the current color of the pixel on the DrawableTexture. The
|
|
engine defaults to ``blend_mix`` if no blend mode is specified in
|
|
``render_mode``. Texture blit shaders also support ``blend_add`` (additive),
|
|
``blend_sub`` (subtractive), ``blend_mul`` (multiply), and ``blend_disabled``
|
|
(alpha does not act as transparency and is written as-is).
|
|
The premultiplied alpha blend mode is *not* supported here.
|
|
|
|
It is also possible to use a different blend mode than the one specified in the shader.
|
|
To do so, you can instantiate and pass in a new :ref:`class_BlitMaterial`
|
|
in the script.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
var blit_material = BlitMaterial.new()
|
|
blit_material.blend_mode = BlitMaterial.BLEND_MODE_DISABLED
|
|
texture.blit_rect(Rect2(0, 0, 200, 200), load("res://icon.svg"), Color.WHITE, 0, blit_material)
|
|
|
|
If you want more complex behavior, you can write your own texture blit shader.
|
|
Create a new shader with the ``texture_blit`` shader type, write your shader
|
|
code, and load it into a material to pass to the function.
|
|
|
|
.. note::
|
|
|
|
The material is passed as a function parameter, rather than bound to the
|
|
resource. This makes it easier to perform multiple types of draws to the
|
|
same texture.
|
|
|
|
Using multiple blits in a single texture
|
|
----------------------------------------
|
|
|
|
DrawableTextures also have a :ref:`blit_rect_multi() <class_DrawableTexture2D_method_blit_rect_multi>`
|
|
method, which allows for up to 4 inputs and outputs in the same step.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
texture.blit_rect_multi(
|
|
Rect2(0, 0, 200, 200),
|
|
[preload("res://icon.svg"), preload("res://circle.svg")],
|
|
[other_drawable_texture]
|
|
)
|
|
|
|
The default behavior of this is to just match each input and output in order.
|
|
For example, this can be useful for drawing to an albedo, normal, and height
|
|
texture simultaneously.
|
|
|
|
Of course, this behavior can also be customized via the texture blit shader.
|
|
In the shader, the extra outputs are written to via ``COLOR1``, ``COLOR2``,
|
|
and ``COLOR3`` (with ``COLOR0`` being the primary output). The extra inputs
|
|
can be read as uniforms with ``hint_blit_source1``, ``hint_blit_source2``,
|
|
and ``hint_blit_source3``.
|
|
|
|
.. _doc_drawable_textures_example_1:
|
|
|
|
Example 1: Simple painting
|
|
--------------------------
|
|
|
|
One of the most intuitive uses for DrawableTextures is for, well, drawing!
|
|
For this example, we're going to start a new project, and create
|
|
a new UI scene with a Control node at its root.
|
|
|
|
Next, you'll need to create a :ref:`class_TextureRect` node, which is going to
|
|
be our user's canvas. Size it appropriately for your screen, and then attach a
|
|
new script to it. The start of this script should initialize the TextureRect's
|
|
texture to a new DrawableTexture.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
extends TextureRect
|
|
|
|
func _ready():
|
|
texture = DrawableTexture2D.new()
|
|
# Be careful; if the dimensions of the node are not equal to the size set here,
|
|
# our draw call later will seem to happen at the wrong spot.
|
|
texture.setup(500, 500, DrawableTexture2D.DRAWABLE_FORMAT_RGBA8, false)
|
|
|
|
Next, we need the TextureRect to respond to the player clicking and dragging as
|
|
if they are painting. To do this, we can override the ``_on_gui_input()`` method
|
|
from the TextureRect in our script, and parse InputMouseButton and
|
|
InputMouseMotion events:
|
|
|
|
.. code-block:: gdscript
|
|
|
|
var drawing = false
|
|
|
|
func _on_gui_input(event):
|
|
if event is InputEventMouseButton:
|
|
# Mouse click/unclick - start/stop drawing.
|
|
drawing = not drawing
|
|
if event is InputEventMouseMotion and drawing:
|
|
# Calculate rect to center our drawn rectangle on mouse position
|
|
# instead of mouse at top left.
|
|
var rect = Rect2(event.position.x - 10, event.position.y - 10, 20, 20)
|
|
texture.blit_rect(rect, null)
|
|
|
|
This should now draw black squares as you click and drag around the TextureRect.
|
|
For more natural drawing, we probably want to be drawing a circle shape, and
|
|
actually coloring it.
|
|
|
|
We can adjust what's being drawn by using a Texture to copy from, and the
|
|
modulate parameter. We will use this :download:`plain white circle texture <img/circle.svg>`,
|
|
which we load as the ``texture`` parameter in ``blit_rect()``,
|
|
and use a red color as the ``modulate`` parameter.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
if event is InputEventMouseMotion and drawing:
|
|
# Calculate rect to center our drawn rectangle on mouse position
|
|
# instead of mouse at top left.
|
|
var rect = Rect2(event.position.x - 10, event.position.y - 10, 20, 20)
|
|
texture.blit_rect(rect, preload("res://circle.svg"), Color.RED)
|
|
|
|
The drawing now looks much more natural and colorful. To further customize this,
|
|
you could connect a :ref:`class_ColorPickerButton` node to the script to store
|
|
the user's color choice for the ``modulate`` parameter of ``blit_rect()``. You
|
|
could also store a brush size variable, give the user a way to adjust it, and
|
|
incorporate it into the rectangle calculation so the user can draw larger or
|
|
smaller strokes.
|
|
|
|
.. code-block:: gdscript
|
|
|
|
var drawing = false
|
|
var my_color = Color.RED
|
|
var my_size = 20.0
|
|
|
|
func _on_gui_input(event):
|
|
if event is InputEventMouseButton:
|
|
# Mouse click/unclick - start/stop drawing.
|
|
drawing = not drawing
|
|
if event is InputEventMouseMotion and drawing:
|
|
# Calculate rect to center our drawn rectangle on mouse position
|
|
# instead of mouse at top left.
|
|
var rect = Rect2(event.position.x - my_size / 2, event.position.y - my_size / 2, my_size, my_size)
|
|
texture.blit_rect(rect, preload("res://circle.svg"), my_color)
|
|
|
|
func _on_color_picker_button_color_changed(color):
|
|
my_color = color
|
|
|
|
func _on_h_slider_value_changed(value):
|
|
my_size = value
|