mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-03 05:48:42 +03:00
Merge pull request #10606 from tetrapod00/first-3d-update
Improve First 3D shader tutorial
This commit is contained in:
@@ -812,6 +812,7 @@ There are two possible interpolation qualifiers:
|
||||
| **smooth** | The value is interpolated in a perspective-correct fashion. This is the default.|
|
||||
+-------------------+---------------------------------------------------------------------------------+
|
||||
|
||||
.. _doc_shading_language_uniforms:
|
||||
|
||||
Uniforms
|
||||
--------
|
||||
|
||||
@@ -63,20 +63,21 @@ Setting up
|
||||
|
||||
Add a new :ref:`MeshInstance3D <class_MeshInstance3D>` node to your scene.
|
||||
|
||||
In the inspector tab beside "Mesh" click "<empty>" and select "New PlaneMesh".
|
||||
Then click on the image of a plane that appears.
|
||||
In the inspector tab, set the MeshInstance3D's **Mesh** property to a new
|
||||
:ref:`PlaneMesh <class_planemesh>` resource, by clicking on ``<empty>`` and
|
||||
choosing **New PlaneMesh**. Then expand the resource by clicking on the image of
|
||||
a plane that appears.
|
||||
|
||||
This adds a :ref:`PlaneMesh <class_planemesh>` to our scene.
|
||||
This adds a plane to our scene.
|
||||
|
||||
Then, in the viewport, click in the upper left corner on the button that says
|
||||
"Perspective". A menu will appear. In the middle of the menu are options for how
|
||||
to display the scene. Select 'Display Wireframe'.
|
||||
Then, in the viewport, click in the upper left corner on the **Perspective** button.
|
||||
In the menu that appears, select **Display Wireframe**.
|
||||
|
||||
This will allow you to see the triangles making up the plane.
|
||||
|
||||
.. image:: img/plane.webp
|
||||
|
||||
Now set ``Subdivide Width`` and ``Subdivide Depth`` of the :ref:`PlaneMesh <class_planemesh>` to ``32``.
|
||||
Now set **Subdivide Width** and **Subdivide Depth** of the :ref:`PlaneMesh <class_planemesh>` to ``32``.
|
||||
|
||||
.. image:: img/plane-sub-set.webp
|
||||
|
||||
@@ -87,12 +88,13 @@ and thus allow us to add more detail.
|
||||
.. image:: img/plane-sub.webp
|
||||
|
||||
:ref:`PrimitiveMeshes <class_primitivemesh>`, like PlaneMesh, only have one
|
||||
surface, so instead of an array of materials there is only one. Click
|
||||
beside "Material" where it says "<empty>" and select "New ShaderMaterial".
|
||||
Then click the sphere that appears.
|
||||
surface, so instead of an array of materials there is only one. Set the
|
||||
**Material** to a new ShaderMaterial, then expand the material by clicking on
|
||||
the sphere that appears.
|
||||
|
||||
Now click beside "Shader" where it says "<empty>" and select "New Shader...". Leave
|
||||
the default settings, give your shader a name and click "Create".
|
||||
Now set the material's **Shader** to a new Shader by clicking ``<empty>`` and
|
||||
select **New Shader...**. Leave the default settings, give your shader a name,
|
||||
and click **Create**.
|
||||
|
||||
Click on the shader in the inspector, and the shader editor should now pop up. You
|
||||
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
|
||||
and make our flat plane appear like a little terrain.
|
||||
|
||||
With nothing in the ``vertex()`` function, Godot will use its default vertex
|
||||
shader. We can easily start to make changes by adding a single line:
|
||||
shader. We can start to make changes by adding a single line:
|
||||
|
||||
.. code-block:: glsl
|
||||
|
||||
@@ -130,12 +132,12 @@ Adding this line, you should get an image like the one below.
|
||||
|
||||
Okay, let's unpack this. The ``y`` value of the ``VERTEX`` is being increased.
|
||||
And we are passing the ``x`` and ``z`` components of the ``VERTEX`` as arguments
|
||||
to ``cos`` and ``sin``; that gives us a wave-like appearance across the ``x``
|
||||
and ``z`` axes.
|
||||
to :ref:`cos() <shader_func_cos>` and :ref:`sin() <shader_func_sin>`; that gives
|
||||
us a wave-like appearance across the ``x`` and ``z`` axes.
|
||||
|
||||
What we want to achieve is the look of little hills; after all. ``cos`` and
|
||||
``sin`` already look kind of like hills. We do so by scaling the inputs to the
|
||||
``cos`` and ``sin`` functions.
|
||||
What we want to achieve is the look of little hills; after all. ``cos()`` and
|
||||
``sin()`` already look kind of like hills. We do so by scaling the inputs to the
|
||||
``cos()`` and ``sin()`` functions.
|
||||
|
||||
.. code-block:: glsl
|
||||
|
||||
@@ -166,30 +168,19 @@ shader, outside the ``vertex()`` function.
|
||||
uniform sampler2D noise;
|
||||
|
||||
This will allow you to send a noise texture to the shader. Now look in the
|
||||
inspector under your material. You should see a section called "Shader Params".
|
||||
If you open it up, you'll see a section called "noise".
|
||||
inspector under your material. You should see a section called **Shader Parameters**.
|
||||
If you open it up, you'll see a parameter called "Noise".
|
||||
|
||||
Click beside it where it says "<empty>" and select "New NoiseTexture2D". Then in
|
||||
your :ref:`NoiseTexture2D <class_noisetexture2D>` click beside where it says "Noise" and select "New
|
||||
FastNoiseLite".
|
||||
|
||||
.. note:: :ref:`FastNoiseLite <class_fastnoiselite>` is used by the NoiseTexture2D to
|
||||
generate a heightmap.
|
||||
Set this **Noise** parameter to a new :ref:`NoiseTexture2D <class_noisetexture2D>`.
|
||||
Then in your NoiseTexture2D, set its **Noise** property to a new
|
||||
:ref:`FastNoiseLite <class_fastnoiselite>`. The FastNoiseLite class is used by
|
||||
the NoiseTexture2D to generate a heightmap.
|
||||
|
||||
Once you set it up and should look like this.
|
||||
|
||||
.. image:: img/noise-set.webp
|
||||
|
||||
Now, access the noise texture using the ``texture()`` function. ``texture()``
|
||||
takes a texture as the first argument and a ``vec2`` for the position on the
|
||||
texture as the second argument. We use the ``x`` and ``z`` channels of
|
||||
``VERTEX`` to determine where on the texture to look up. Note that the PlaneMesh
|
||||
coordinates are within the [-1,1] range (for a size of 2), while the texture
|
||||
coordinates are within [0,1], so to normalize we divide by the size of the
|
||||
PlaneMesh by 2.0 and add 0.5. ``texture()`` returns a ``vec4`` of the ``r, g, b,
|
||||
a`` channels at the position. Since the noise texture is grayscale, all of the
|
||||
values are the same, so we can use any one of the channels as the height. In
|
||||
this case we'll use the ``r``, or ``x`` channel.
|
||||
Now, access the noise texture using the ``texture()`` function:
|
||||
|
||||
.. code-block:: glsl
|
||||
|
||||
@@ -198,10 +189,27 @@ this case we'll use the ``r``, or ``x`` channel.
|
||||
VERTEX.y += height;
|
||||
}
|
||||
|
||||
Note: ``xyzw`` is the same as ``rgba`` in GLSL, so instead of ``texture().x``
|
||||
above, we could use ``texture().r``. See the `OpenGL documentation
|
||||
<https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors>`_ for more
|
||||
details.
|
||||
:ref:`texture() <shader_func_texture>` takes a texture as the first argument and
|
||||
a ``vec2`` for the position on the texture as the second argument. We use the
|
||||
``x`` and ``z`` channels of ``VERTEX`` to determine where on the texture to look
|
||||
up.
|
||||
|
||||
Since the PlaneMesh coordinates are within the ``[-1.0, 1.0]`` range (for a size
|
||||
of ``2.0``), while the texture coordinates are within ``[0.0, 1.0]``, to remap
|
||||
the coordinates we divide by the size of the PlaneMesh by ``2.0`` and add
|
||||
``0.5`` .
|
||||
|
||||
``texture()`` returns a ``vec4`` of the ``r, g, b, a`` channels at the position.
|
||||
Since the noise texture is grayscale, all of the values are the same, so we can
|
||||
use any one of the channels as the height. In this case we'll use the ``r``, or
|
||||
``x`` channel.
|
||||
|
||||
.. note::
|
||||
|
||||
``xyzw`` is the same as ``rgba`` in GLSL, so instead of ``texture().x``
|
||||
above, we could use ``texture().r``. See the `OpenGL documentation
|
||||
<https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Vectors>`_ for more
|
||||
details.
|
||||
|
||||
Using this code you can see the texture creates random looking hills.
|
||||
|
||||
@@ -214,7 +222,8 @@ texture, now let's learn how they work.
|
||||
Uniforms
|
||||
--------
|
||||
|
||||
Uniform variables allow you to pass data from the game into the shader. They are
|
||||
:ref:`Uniform variables <doc_shading_language_uniforms>` allow you to pass data
|
||||
from the game into the shader. They are
|
||||
very useful for controlling shader effects. Uniforms can be almost any datatype
|
||||
that can be used in the shader. To use a uniform, you declare it in your
|
||||
:ref:`Shader<class_Shader>` using the keyword ``uniform``.
|
||||
@@ -228,11 +237,11 @@ Let's make a uniform that changes the height of the terrain.
|
||||
|
||||
Godot lets you initialize a uniform with a value; here, ``height_scale`` is set
|
||||
to ``0.5``. You can set uniforms from GDScript by calling the function
|
||||
``set_shader_parameter()`` on the material corresponding to the shader. The value
|
||||
passed from GDScript takes precedence over the value used to initialize it in
|
||||
the shader.
|
||||
:ref:`set_shader_parameter() <class_ShaderMaterial_method_set_shader_parameter>`
|
||||
on the material corresponding to the shader. The value passed from GDScript
|
||||
takes precedence over the value used to initialize it in the shader.
|
||||
|
||||
::
|
||||
.. code-block:: gdscript
|
||||
|
||||
# called from the MeshInstance3D
|
||||
mesh.material.set_shader_parameter("height_scale", 0.5)
|
||||
@@ -245,8 +254,8 @@ the shader.
|
||||
``get_surface_material()`` or ``material_override``.
|
||||
|
||||
Remember that the string passed into ``set_shader_parameter()`` must match the name
|
||||
of the uniform variable in the :ref:`Shader<class_Shader>`. You can use the
|
||||
uniform variable anywhere inside your :ref:`Shader<class_Shader>`. Here, we will
|
||||
of the uniform variable in the shader. You can use the
|
||||
uniform variable anywhere inside your shader. Here, we will
|
||||
use it to set the height value instead of arbitrarily multiplying by ``0.5``.
|
||||
|
||||
.. code-block:: glsl
|
||||
@@ -264,16 +273,17 @@ especially useful for animations.
|
||||
Interacting with light
|
||||
----------------------
|
||||
|
||||
First, turn wireframe off. To do so, click in the upper-left of the Viewport
|
||||
again, where it says "Perspective", and select "Display Normal".
|
||||
Additionally in the 3D scene toolbar, turn off preview sunlight.
|
||||
First, turn wireframe off. To do so, open the **Perspective** menu in the
|
||||
upper-left of the viewport again, and select **Display Normal**. Additionally in
|
||||
the 3D scene toolbar, turn off preview sunlight.
|
||||
|
||||
.. image:: img/normal.webp
|
||||
|
||||
Note how the mesh color goes flat. This is because the lighting on it is flat.
|
||||
Let's add a light!
|
||||
|
||||
First, we will add an :ref:`OmniLight3D<class_OmniLight3D>` to the scene.
|
||||
First, we will add an :ref:`OmniLight3D<class_OmniLight3D>` to the scene, and
|
||||
drag it up so it is above the terrain.
|
||||
|
||||
.. image:: img/light.webp
|
||||
|
||||
@@ -300,7 +310,7 @@ do that by passing in a second noise texture.
|
||||
uniform sampler2D normalmap;
|
||||
|
||||
Set this second uniform texture to another :ref:`NoiseTexture2D <class_noisetexture2D>` with another
|
||||
:ref:`FastNoiseLite <class_fastnoiselite>`. But this time, check **As Normalmap**.
|
||||
:ref:`FastNoiseLite <class_fastnoiselite>`. But this time, check **As Normal Map**.
|
||||
|
||||
.. image:: img/normal-set.webp
|
||||
|
||||
@@ -312,20 +322,19 @@ wrapping the texture around the mesh automatically.
|
||||
Lastly, in order to ensure that we are reading from the same places on the noise
|
||||
texture and the normalmap texture, we are going to pass the ``VERTEX.xz``
|
||||
position from the ``vertex()`` function to the ``fragment()`` function. We do
|
||||
that with varyings.
|
||||
that using a :ref:`varying <doc_shading_language_varyings>`.
|
||||
|
||||
Above the ``vertex()`` define a ``vec2`` called ``tex_position``. And inside the
|
||||
``vertex()`` function assign ``VERTEX.xz`` to ``tex_position``.
|
||||
Above the ``vertex()`` define a ``varying vec2`` called ``tex_position``. And
|
||||
inside the ``vertex()`` function assign ``VERTEX.xz`` to ``tex_position``.
|
||||
|
||||
.. code-block:: glsl
|
||||
|
||||
varying vec2 tex_position;
|
||||
|
||||
void vertex() {
|
||||
...
|
||||
tex_position = VERTEX.xz / 2.0 + 0.5;
|
||||
float height = texture(noise, tex_position).x;
|
||||
...
|
||||
VERTEX.y += height * height_scale;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
.. image:: img/normalmap2.webp
|
||||
|
||||
Full code
|
||||
---------
|
||||
|
||||
Here is the full code for this tutorial. You can see it is not very long as
|
||||
Godot handles most of the difficult stuff for you.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user