|
| 1 | +.. _doc_drawable_textures: |
| 2 | + |
| 3 | +DrawableTextures |
| 4 | +==================== |
| 5 | + |
| 6 | +DrawableTextures are a type of Texture2D with additional functions for modifying the texture via the GPU. |
| 7 | + |
| 8 | +Basic Blit_Rect |
| 9 | +--------------- |
| 10 | + |
| 11 | +The most basic operation on a drawable texture is blit_rect() - |
| 12 | +Blitting (copying) the whole of one texture into the given rectangle on the ``DrawableTexture``. |
| 13 | + |
| 14 | +.. code-block:: gdscript |
| 15 | +
|
| 16 | + texture.blit_rect(Rect2(20, 50, 60, 60), load("res://circle.svg")) |
| 17 | + texture.blit_rect(Rect2(20, 50, 60, 60), load("res://circle.svg"), Color.WHITE, 0) |
| 18 | +
|
| 19 | +The code above blits the circle texture into the rectangle (20, 50, 60, 60) on the ``DrawableTexture``. |
| 20 | +(20,50) is the top left corner of the rectangle being drawn to, and (60, 60) is its size. |
| 21 | +If you wanted to draw over the whole texture, simply match the Rect parameter to the ``DrawableTexture``'s size. |
| 22 | + |
| 23 | +The next parameter in blit_rect() is an optional parameter, Modulate. |
| 24 | +This is a color that the output is multiplied by (in the default behavior). |
| 25 | +This can be used to recolor, or even mask the drawn output of blit_rect(). |
| 26 | +Using a modulate of Color(0, 0, 1, 0) for example, will only draw to and update the Blue values of each pixel on the DrawableTexture. |
| 27 | + |
| 28 | +blit_rect()'s 4th parameter, Mipmap, can specify a mipmap level to draw to. |
| 29 | +You only need to use this if you want very fine control over each mipmap layer. |
| 30 | +Keep in mind, you do not need to adjust the rectangle size - it is converted to a portion of the textures whole size. |
| 31 | +If you just want to update all the mipmap layers, you should draw to the texture, and then call generate_mipmaps() on it |
| 32 | + |
| 33 | +Blend Modes & TextureBlit Shaders |
| 34 | +--------------------------------- |
| 35 | + |
| 36 | +The drawing process for blit_rect() and DrawableTextures is governed by a TextureBlit GDShader. |
| 37 | +Even when the user does not supply one, the engine has a default TextureBlit shader it uses. |
| 38 | + |
| 39 | +.. code-block:: glsl |
| 40 | +
|
| 41 | + // Default Texture Blit shader. |
| 42 | +
|
| 43 | + shader_type texture_blit; |
| 44 | + render_mode blend_mix; |
| 45 | +
|
| 46 | + uniform sampler2D source_texture0 : hint_blit_source0; |
| 47 | + uniform sampler2D source_texture1 : hint_blit_source1; |
| 48 | + uniform sampler2D source_texture2 : hint_blit_source2; |
| 49 | + uniform sampler2D source_texture3 : hint_blit_source3; |
| 50 | +
|
| 51 | + void blit() { |
| 52 | + // Copies from each whole source texture to a rect on each output texture. |
| 53 | + COLOR0 = texture(source_texture0, UV) * MODULATE; |
| 54 | + COLOR1 = texture(source_texture1, UV) * MODULATE; |
| 55 | + COLOR2 = texture(source_texture2, UV) * MODULATE; |
| 56 | + COLOR3 = texture(source_texture3, UV) * MODULATE; |
| 57 | + } |
| 58 | +
|
| 59 | +The Blend_mode defines the how the output color value |
| 60 | +is blended with the current color of the pixel on the ``DrawableTexture``. |
| 61 | +While the engine defaults to Blend_Mix, these shaders also support |
| 62 | +blend_disabled, blend_sub, blend_add, and blend_mul. |
| 63 | + |
| 64 | +If you just want to use a different blend_mode than the default, |
| 65 | +you can instantiate and pass in a new ``BlitMaterial`` in the GDScript code. |
| 66 | + |
| 67 | +.. code-block:: gdscript |
| 68 | +
|
| 69 | + var myMat = BlitMaterial.new() |
| 70 | + myMat.blend_mode = BlitMaterial.BLEND_MODE_DISABLED |
| 71 | + texture.blit_rect(Rect2(0, 0, 200, 200), load("res://icon.svg"), Color.WHITE, 0, myMat) |
| 72 | +
|
| 73 | +If you want more complicated behavior, then you can write your own TextureBlit Shader! |
| 74 | +Create a new TextureBlit GDShader, write your Shader code, and load it into a material |
| 75 | +to pass to the function. The Material is passed as a function parameter rather than bound |
| 76 | +to the resource to make it easier to perform multiple different types of Draws to the same texture. |
| 77 | + |
| 78 | +Blit_Rect Multi |
| 79 | +--------------- |
| 80 | + |
| 81 | +DrawableTextures also have a Blit_Rect multi function, |
| 82 | +to allow for up to 4 inputs and outputs in the same step. |
| 83 | + |
| 84 | +.. code-block:: gdscript |
| 85 | +
|
| 86 | + texture.blit_rect_multi(Rect2(0, 0, 200, 200), [load("res://icon.svg"), load("res://circle.svg")], [otherDrawTex]) |
| 87 | +
|
| 88 | +The default behavior of this is to just match each input and output in order |
| 89 | +which can be useful for, say, drawing to an Albedo, Normal, and Depth texture simultaneously. |
| 90 | + |
| 91 | +Of course, this behavior too can be customized via the TextureBlit shader. |
| 92 | +In the GDShader, the extra outputs are written to via COLOR1, COLOR2, and COLOR3 |
| 93 | +And the extra inputs can be read as uniforms with hint_blit_source1, hint_blit_source2, and hint_blit_source3 |
| 94 | + |
| 95 | +.. _doc_drawable_textures_example_1: |
| 96 | + |
| 97 | +Example 1: Simple Painting |
| 98 | +-------------------------- |
| 99 | +One of the most intuitive uses for DrawableTextures is for, well, drawing! |
| 100 | +Its easier than ever to set up a canvas the user can paint. |
| 101 | +For this example, were going to start a new project, and create |
| 102 | +a new UI Scene with a Control Node at its root. |
| 103 | +Next, you'll want to create a TextureRect Node which is going to be our user's canvas. |
| 104 | +Size it appropriately for your screen, and then attach a new GDScript to it. |
| 105 | +The start of this script should initialize the TextureRect's texture to a new DrawableTexture. |
| 106 | + |
| 107 | +.. code-block:: gdscript |
| 108 | +
|
| 109 | + extends TextureRect |
| 110 | +
|
| 111 | + func _ready(): |
| 112 | + texture = DrawableTexture2D.new() |
| 113 | + # Be Careful - if the dimensions of the Node != the setup size here |
| 114 | + # our draw call later will seem to happen at the wrong spot |
| 115 | + texture.setup(500, 500, DrawableTexture2D.DRAWABLE_FORMAT_RGBA8, false) |
| 116 | +
|
| 117 | +Next, we just need the TextureRect to respond to the player clicking and dragging as if they are painting! |
| 118 | +To do this, we can connect the _on_gui_input() Signal from the TextureRect to our script, |
| 119 | +and parse InputMouseButton and InputMouseMotion events |
| 120 | + |
| 121 | +.. code-block:: gdscript |
| 122 | +
|
| 123 | + var drawing: bool = false |
| 124 | +
|
| 125 | + func _on_gui_input(event: InputEvent) -> void: |
| 126 | + if event is InputEventMouseButton: |
| 127 | + # Mouse click/unclick - start/stop drawing |
| 128 | + drawing = !drawing |
| 129 | + if event is InputEventMouseMotion and drawing: |
| 130 | + # Calculate rect to center our drawn rectangle on mouse position |
| 131 | + # instead of mouse at top left |
| 132 | + var p = event.position |
| 133 | + var rect: Rect2 = Rect2(p.x - 10, p.y - 10, 20, 20) |
| 134 | + texture.blit_rect(rect, null) |
| 135 | +
|
| 136 | +This should now draw black squares as you click and drag around the TextureRect. |
| 137 | +For more natural drawing, we probably want to be drawing a circle shape, and actually coloring it! |
| 138 | +We can adjust whats being drawn by using a Texture to copy from, and the modulate parameter. |
| 139 | +I downloaded a plain white circle texture, which I load as the Texture parameter in Blit_Rect, |
| 140 | +and use Red as my Modulate parameter. |
| 141 | + |
| 142 | +.. code-block:: gdscript |
| 143 | +
|
| 144 | + if event is InputEventMouseMotion and drawing: |
| 145 | + # Calculate rect to center our drawn rectangle on mouse position |
| 146 | + # instead of mouse at top left |
| 147 | + var p = event.position |
| 148 | + var rect: Rect2 = Rect2(p.x - 10, p.y - 10, 20, 20) |
| 149 | + texture.blit_rect(rect, load("res://circle.svg"), Color.RED) |
| 150 | +
|
| 151 | +Now the drawing looks much more natural and colorful! |
| 152 | +To further customize this, you could connect a ColorPickerButton Node to the script |
| 153 | +to store a users Color choice for the Modulate Parameter of Blit_Rect. |
| 154 | +You could also store a Brush Size variable, give the user a way to adjust it, |
| 155 | +and incorporate it into the Rectangle calculation so the user can draw bigger or smaller strokes. |
| 156 | + |
| 157 | +.. code-block:: gdscript |
| 158 | +
|
| 159 | + var drawing: bool = false |
| 160 | + var myColor: Color = Color.RED |
| 161 | + var mySize: float = 20.0 |
| 162 | +
|
| 163 | + func _on_gui_input(event: InputEvent) -> void: |
| 164 | + if event is InputEventMouseButton: |
| 165 | + # Mouse click/unclick - start/stop drawing |
| 166 | + drawing = !drawing |
| 167 | + if event is InputEventMouseMotion and drawing: |
| 168 | + # Calculate rect to center our drawn rectangle on mouse position |
| 169 | + # instead of mouse at top left |
| 170 | + var p = event.position |
| 171 | + var rect: Rect2 = Rect2(p.x - mySize/2, p.y - mySize/2, mySize, mySize) |
| 172 | + texture.blit_rect(rect, load("res://circle.svg"), myColor) |
| 173 | +
|
| 174 | + func _on_color_picker_button_color_changed(color: Color) -> void: |
| 175 | + myColor = color |
| 176 | +
|
| 177 | + func _on_h_slider_value_changed(value: float) -> void: |
| 178 | + mySize = value |
0 commit comments