Skip to content

Commit ea1b882

Browse files
authored
Merge pull request #10606 from tetrapod00/first-3d-update
Improve First 3D shader tutorial
2 parents 1afdbaf + 287ad02 commit ea1b882

File tree

2 files changed

+70
-57
lines changed

2 files changed

+70
-57
lines changed

tutorials/shaders/shader_reference/shading_language.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ There are two possible interpolation qualifiers:
812812
| **smooth** | The value is interpolated in a perspective-correct fashion. This is the default.|
813813
+-------------------+---------------------------------------------------------------------------------+
814814

815+
.. _doc_shading_language_uniforms:
815816

816817
Uniforms
817818
--------

tutorials/shaders/your_first_shader/your_first_3d_shader.rst

Lines changed: 69 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,21 @@ Setting up
6363

6464
Add a new :ref:`MeshInstance3D <class_MeshInstance3D>` node to your scene.
6565

66-
In the inspector tab beside "Mesh" click "<empty>" and select "New PlaneMesh".
67-
Then click on the image of a plane that appears.
66+
In the inspector tab, set the MeshInstance3D's **Mesh** property to a new
67+
:ref:`PlaneMesh <class_planemesh>` resource, by clicking on ``<empty>`` and
68+
choosing **New PlaneMesh**. Then expand the resource by clicking on the image of
69+
a plane that appears.
6870

69-
This adds a :ref:`PlaneMesh <class_planemesh>` to our scene.
71+
This adds a plane to our scene.
7072

71-
Then, in the viewport, click in the upper left corner on the button that says
72-
"Perspective". A menu will appear. In the middle of the menu are options for how
73-
to display the scene. Select 'Display Wireframe'.
73+
Then, in the viewport, click in the upper left corner on the **Perspective** button.
74+
In the menu that appears, select **Display Wireframe**.
7475

7576
This will allow you to see the triangles making up the plane.
7677

7778
.. image:: img/plane.webp
7879

79-
Now set ``Subdivide Width`` and ``Subdivide Depth`` of the :ref:`PlaneMesh <class_planemesh>` to ``32``.
80+
Now set **Subdivide Width** and **Subdivide Depth** of the :ref:`PlaneMesh <class_planemesh>` to ``32``.
8081

8182
.. image:: img/plane-sub-set.webp
8283

@@ -87,12 +88,13 @@ and thus allow us to add more detail.
8788
.. image:: img/plane-sub.webp
8889

8990
:ref:`PrimitiveMeshes <class_primitivemesh>`, like PlaneMesh, only have one
90-
surface, so instead of an array of materials there is only one. Click
91-
beside "Material" where it says "<empty>" and select "New ShaderMaterial".
92-
Then click the sphere that appears.
91+
surface, so instead of an array of materials there is only one. Set the
92+
**Material** to a new ShaderMaterial, then expand the material by clicking on
93+
the sphere that appears.
9394

94-
Now click beside "Shader" where it says "<empty>" and select "New Shader...". Leave
95-
the default settings, give your shader a name and click "Create".
95+
Now set the material's **Shader** to a new Shader by clicking ``<empty>`` and
96+
select **New Shader...**. Leave the default settings, give your shader a name,
97+
and click **Create**.
9698

9799
Click on the shader in the inspector, and the shader editor should now pop up. You
98100
are ready to begin writing your first Spatial shader!
@@ -116,7 +118,7 @@ appear in the final scene. We will be using it to offset the height of each vert
116118
and make our flat plane appear like a little terrain.
117119

118120
With nothing in the ``vertex()`` function, Godot will use its default vertex
119-
shader. We can easily start to make changes by adding a single line:
121+
shader. We can start to make changes by adding a single line:
120122

121123
.. code-block:: glsl
122124
@@ -130,12 +132,12 @@ Adding this line, you should get an image like the one below.
130132

131133
Okay, let's unpack this. The ``y`` value of the ``VERTEX`` is being increased.
132134
And we are passing the ``x`` and ``z`` components of the ``VERTEX`` as arguments
133-
to ``cos`` and ``sin``; that gives us a wave-like appearance across the ``x``
134-
and ``z`` axes.
135+
to :ref:`cos() <shader_func_cos>` and :ref:`sin() <shader_func_sin>`; that gives
136+
us a wave-like appearance across the ``x`` and ``z`` axes.
135137

136-
What we want to achieve is the look of little hills; after all. ``cos`` and
137-
``sin`` already look kind of like hills. We do so by scaling the inputs to the
138-
``cos`` and ``sin`` functions.
138+
What we want to achieve is the look of little hills; after all. ``cos()`` and
139+
``sin()`` already look kind of like hills. We do so by scaling the inputs to the
140+
``cos()`` and ``sin()`` functions.
139141

140142
.. code-block:: glsl
141143
@@ -166,30 +168,19 @@ shader, outside the ``vertex()`` function.
166168
uniform sampler2D noise;
167169
168170
This will allow you to send a noise texture to the shader. Now look in the
169-
inspector under your material. You should see a section called "Shader Params".
170-
If you open it up, you'll see a section called "noise".
171+
inspector under your material. You should see a section called **Shader Parameters**.
172+
If you open it up, you'll see a parameter called "Noise".
171173

172-
Click beside it where it says "<empty>" and select "New NoiseTexture2D". Then in
173-
your :ref:`NoiseTexture2D <class_noisetexture2D>` click beside where it says "Noise" and select "New
174-
FastNoiseLite".
175-
176-
.. note:: :ref:`FastNoiseLite <class_fastnoiselite>` is used by the NoiseTexture2D to
177-
generate a heightmap.
174+
Set this **Noise** parameter to a new :ref:`NoiseTexture2D <class_noisetexture2D>`.
175+
Then in your NoiseTexture2D, set its **Noise** property to a new
176+
:ref:`FastNoiseLite <class_fastnoiselite>`. The FastNoiseLite class is used by
177+
the NoiseTexture2D to generate a heightmap.
178178

179179
Once you set it up and should look like this.
180180

181181
.. image:: img/noise-set.webp
182182

183-
Now, access the noise texture using the ``texture()`` function. ``texture()``
184-
takes a texture as the first argument and a ``vec2`` for the position on the
185-
texture as the second argument. We use the ``x`` and ``z`` channels of
186-
``VERTEX`` to determine where on the texture to look up. Note that the PlaneMesh
187-
coordinates are within the [-1,1] range (for a size of 2), while the texture
188-
coordinates are within [0,1], so to normalize we divide by the size of the
189-
PlaneMesh by 2.0 and add 0.5. ``texture()`` returns a ``vec4`` of the ``r, g, b,
190-
a`` channels at the position. Since the noise texture is grayscale, all of the
191-
values are the same, so we can use any one of the channels as the height. In
192-
this case we'll use the ``r``, or ``x`` channel.
183+
Now, access the noise texture using the ``texture()`` function:
193184

194185
.. code-block:: glsl
195186
@@ -198,10 +189,27 @@ this case we'll use the ``r``, or ``x`` channel.
198189
VERTEX.y += height;
199190
}
200191
201-
Note: ``xyzw`` is the same as ``rgba`` in GLSL, so instead of ``texture().x``
202-
above, we could use ``texture().r``. See the `OpenGL documentation
203-
<https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors>`_ for more
204-
details.
192+
:ref:`texture() <shader_func_texture>` takes a texture as the first argument and
193+
a ``vec2`` for the position on the texture as the second argument. We use the
194+
``x`` and ``z`` channels of ``VERTEX`` to determine where on the texture to look
195+
up.
196+
197+
Since the PlaneMesh coordinates are within the ``[-1.0, 1.0]`` range (for a size
198+
of ``2.0``), while the texture coordinates are within ``[0.0, 1.0]``, to remap
199+
the coordinates we divide by the size of the PlaneMesh by ``2.0`` and add
200+
``0.5`` .
201+
202+
``texture()`` returns a ``vec4`` of the ``r, g, b, a`` channels at the position.
203+
Since the noise texture is grayscale, all of the values are the same, so we can
204+
use any one of the channels as the height. In this case we'll use the ``r``, or
205+
``x`` channel.
206+
207+
.. note::
208+
209+
``xyzw`` is the same as ``rgba`` in GLSL, so instead of ``texture().x``
210+
above, we could use ``texture().r``. See the `OpenGL documentation
211+
<https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors>`_ for more
212+
details.
205213

206214
Using this code you can see the texture creates random looking hills.
207215

@@ -214,7 +222,8 @@ texture, now let's learn how they work.
214222
Uniforms
215223
--------
216224

217-
Uniform variables allow you to pass data from the game into the shader. They are
225+
:ref:`Uniform variables <doc_shading_language_uniforms>` allow you to pass data
226+
from the game into the shader. They are
218227
very useful for controlling shader effects. Uniforms can be almost any datatype
219228
that can be used in the shader. To use a uniform, you declare it in your
220229
:ref:`Shader<class_Shader>` using the keyword ``uniform``.
@@ -228,11 +237,11 @@ Let's make a uniform that changes the height of the terrain.
228237
229238
Godot lets you initialize a uniform with a value; here, ``height_scale`` is set
230239
to ``0.5``. You can set uniforms from GDScript by calling the function
231-
``set_shader_parameter()`` on the material corresponding to the shader. The value
232-
passed from GDScript takes precedence over the value used to initialize it in
233-
the shader.
240+
:ref:`set_shader_parameter() <class_ShaderMaterial_method_set_shader_parameter>`
241+
on the material corresponding to the shader. The value passed from GDScript
242+
takes precedence over the value used to initialize it in the shader.
234243

235-
::
244+
.. code-block:: gdscript
236245
237246
# called from the MeshInstance3D
238247
mesh.material.set_shader_parameter("height_scale", 0.5)
@@ -245,8 +254,8 @@ the shader.
245254
``get_surface_material()`` or ``material_override``.
246255

247256
Remember that the string passed into ``set_shader_parameter()`` must match the name
248-
of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the
249-
uniform variable anywhere inside your :ref:`Shader<class_Shader>`. Here, we will
257+
of the uniform variable in the shader. You can use the
258+
uniform variable anywhere inside your shader. Here, we will
250259
use it to set the height value instead of arbitrarily multiplying by ``0.5``.
251260

252261
.. code-block:: glsl
@@ -264,16 +273,17 @@ especially useful for animations.
264273
Interacting with light
265274
----------------------
266275

267-
First, turn wireframe off. To do so, click in the upper-left of the Viewport
268-
again, where it says "Perspective", and select "Display Normal".
269-
Additionally in the 3D scene toolbar, turn off preview sunlight.
276+
First, turn wireframe off. To do so, open the **Perspective** menu in the
277+
upper-left of the viewport again, and select **Display Normal**. Additionally in
278+
the 3D scene toolbar, turn off preview sunlight.
270279

271280
.. image:: img/normal.webp
272281

273282
Note how the mesh color goes flat. This is because the lighting on it is flat.
274283
Let's add a light!
275284

276-
First, we will add an :ref:`OmniLight3D<class_OmniLight3D>` to the scene.
285+
First, we will add an :ref:`OmniLight3D<class_OmniLight3D>` to the scene, and
286+
drag it up so it is above the terrain.
277287

278288
.. image:: img/light.webp
279289

@@ -300,7 +310,7 @@ do that by passing in a second noise texture.
300310
uniform sampler2D normalmap;
301311
302312
Set this second uniform texture to another :ref:`NoiseTexture2D <class_noisetexture2D>` with another
303-
:ref:`FastNoiseLite <class_fastnoiselite>`. But this time, check **As Normalmap**.
313+
:ref:`FastNoiseLite <class_fastnoiselite>`. But this time, check **As Normal Map**.
304314

305315
.. image:: img/normal-set.webp
306316

@@ -312,20 +322,19 @@ wrapping the texture around the mesh automatically.
312322
Lastly, in order to ensure that we are reading from the same places on the noise
313323
texture and the normalmap texture, we are going to pass the ``VERTEX.xz``
314324
position from the ``vertex()`` function to the ``fragment()`` function. We do
315-
that with varyings.
325+
that using a :ref:`varying <doc_shading_language_varyings>`.
316326

317-
Above the ``vertex()`` define a ``vec2`` called ``tex_position``. And inside the
318-
``vertex()`` function assign ``VERTEX.xz`` to ``tex_position``.
327+
Above the ``vertex()`` define a ``varying vec2`` called ``tex_position``. And
328+
inside the ``vertex()`` function assign ``VERTEX.xz`` to ``tex_position``.
319329

320330
.. code-block:: glsl
321331
322332
varying vec2 tex_position;
323333
324334
void vertex() {
325-
...
326335
tex_position = VERTEX.xz / 2.0 + 0.5;
327336
float height = texture(noise, tex_position).x;
328-
...
337+
VERTEX.y += height * height_scale;
329338
}
330339
331340
And now we can access ``tex_position`` from the ``fragment()`` function.
@@ -345,6 +354,9 @@ We can even drag the light around and the lighting will update automatically.
345354

346355
.. image:: img/normalmap2.webp
347356

357+
Full code
358+
---------
359+
348360
Here is the full code for this tutorial. You can see it is not very long as
349361
Godot handles most of the difficult stuff for you.
350362

0 commit comments

Comments
 (0)