[译]Unity3D Shader教程(四)Basic Shader

[译]Unity3D Shader教程(四)Basic Shader

2021年6月26日 0 作者 老王

原文链接:
Shader Tutorials by Ronja


1 概要

在前三个教程中,我解释了让Shader工作的一些基础部分。 在这一部分中,我将向您展示如何填写其余部分。
我没有展示的主要内容是实际执行的代码。 那是因为您一开始不需要太多代码来运行着色器,而且所有花哨的代码都在专门的教程中。
着色器

2 What we have so far

到目前为止我们所知道的。
下面Shader中的所有部分再前三个教程中的某一个都有解释。 如果您有其他想法,请随时告诉我。

Shader "Tutorial/001-004_Basic_Unlit"{
  //show values to edit in inspector
  //在Inspactor面板展示变量
  Properties{
    _Color ("Tint", Color) = (0, 0, 0, 1)
    _MainTex ("Texture", 2D) = "white" {}
  }

  SubShader{
    //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
    //材质完全不透明,与其他不透明几何体同时渲染
    Tags{ "RenderType"="Opaque" "Queue"="Geometry" }

    Pass{
      CGPROGRAM

      //texture and transforms of the texture
      //纹理和纹理变换
      sampler2D _MainTex;
      float4 _MainTex_ST;

      //tint of the texture
      //纹理的色调
      fixed4 _Color;

      //the mesh data thats read by the vertex shader
      //顶点着色器读取的网格数据
      struct appdata{
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
      };

      //the data thats passed from the vertex to the fragment shader and interpolated by the rasterizer
      //从顶点传递到片段着色器并由光栅化器插值的数据
      struct v2f{
        float4 position : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      ENDCG
    }
  }
  Fallback "VertexLit"
}

3 Setting up the shader stages

设置着色器阶段。
每个着色器阶段都表示为一个 hlsl 函数。 要定义程序中的哪个函数代表哪个阶段,需要在程序中添加 #pragma 语句。 顶点阶段重要的是它接收顶点数据并返回(处理后的数据)给光栅器进行插值,而片元阶段重要的是它接收插值后的值并返回一个写入渲染目标的向量。 所以,代码看起来像这样:

//define vertex and fragment shader functions
//定义顶点与片元着色器的函数
#pragma vertex vert
#pragma fragment frag

//the vertex shader function
//顶点着色器函数
v2f vert(appdata v){
  //vertex stage stuff
  //顶点阶段的工作
}

//the fragment shader function
//片元着色器函数
fixed4 frag(v2f i) : SV_TARGET{
  //fragment stage code
  //片元阶段的代码
}

4 Vertex stage

顶点阶段。
在定义顶点阶段的函数的开头,我们创建了将在最后返回给插值器的结构狗提。(在顶点着色器中)我们将用数据填充并返回它。
顶点阶段的主要工作是将顶点位置从本地对象(局部)空间转换到可以渲染它们的裁剪空间。这是通过矩阵乘法在内部完成的,但我们不必了解此矩阵乘法的具体细节,因为 unity 为我们提供了函数(这些内置的函数被定义为)进行矩阵乘法。为了在我们的着色器中使用这些宏(以及许多其他有用的代码),我们通过 #include "UnityCG.cginc" 导入一个包含文件。将位置从局部空间转换到剪辑空间的函数称为 UnityObjectToClipPos。 UnityCG.cginc 文件还有一个宏TRANSFORM_TEX ,可以帮助我们将与顶点数据一起传递的 uv 坐标转换为我们在编辑器中设置的平铺和偏移量的 uv 坐标,其参数为基本 uv 坐标以及纹理的名称。
添加上这些代码后,顶点函数应如下所示:

//the vertex shader function
//顶点着色器函数
v2f vert(appdata v){
  v2f o;
  //convert the vertex positions from object space to clip space so they can be rendered correctly
  //将局部空间的顶点位置变换到裁剪空间,以便模型能正确渲染
  o.position = UnityObjectToClipPos(v.vertex);
  //apply the texture transforms to the UV coordinates and pass them to the v2f struct
  //对uv坐标进行纹理变换,并将变换后的值转递给v2f结构体
  o.uv = TRANSFORM_TEX(v.uv, _MainTex);
  return o;
}

5 Fragment stage

片元阶段。
在片元阶段,我们采用插值器插值之后的值作为输入,同时使用uniform变量来确定该像素应该具有什么颜色。 最简单的版本可能只是 return float4(1,1,1,1); 其将返回完全白色的网格。但在大多数情况下,我们希望使用更复杂的结果并使用纹理。
为了访问纹理,我们通常使用 tex2D 函数,该函数将纹理作为其第一个参数,将 uv 坐标作为第二个参数,然后返回该坐标处的颜色。 在本例中,我们将保存结果颜色,将其与我们定义为uniform变量的颜色相乘并返回结果。

//the fragment shader function
//片元着色器函数
fixed4 frag(v2f i) : SV_TARGET{
    //read the texture color at the uv coordinate
    //读取uv坐标处的纹理颜色
  fixed4 col = tex2D(_MainTex, i.uv);
  //multiply the texture color and tint color
  //将纹理颜色与主色调相乘
  col *= _Color;
  //return the final color to be drawn on screen
  //返回将绘制到屏幕上的颜色
  return col;
}

至此,咱们就有了咱们的第一个Shader!

Shader "Tutorial/001-004_Basic_Unlit"{
  //show values to edit in inspector
  Properties{
    _Color ("Tint", Color) = (0, 0, 0, 1)
    _MainTex ("Texture", 2D) = "white" {}
  }

  SubShader{
    //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
    Tags{ "RenderType"="Opaque" "Queue"="Geometry" }

    Pass{
      CGPROGRAM

      //include useful shader functions
      #include "UnityCG.cginc"

      //define vertex and fragment shader functions
      #pragma vertex vert
      #pragma fragment frag

      //texture and transforms of the texture
      sampler2D _MainTex;
      float4 _MainTex_ST;

      //tint of the texture
      fixed4 _Color;

      //the mesh data thats read by the vertex shader
      struct appdata{
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
      };

      //the data thats passed from the vertex to the fragment shader and interpolated by the rasterizer
      struct v2f{
        float4 position : SV_POSITION;
        float2 uv : TEXCOORD0;
      };

      //the vertex shader function
      v2f vert(appdata v){
        v2f o;
        //convert the vertex positions from object space to clip space so they can be rendered correctly
        o.position = UnityObjectToClipPos(v.vertex);
        //apply the texture transforms to the UV coordinates and pass them to the v2f struct
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        return o;
      }

      //the fragment shader function
      fixed4 frag(v2f i) : SV_TARGET{
          //read the texture color at the uv coordinate
        fixed4 col = tex2D(_MainTex, i.uv);
        //multiply the texture color and tint color
        col *= _Color;
        //return the final color to be drawn on screen
        return col;
      }

      ENDCG
    }
  }
  Fallback "VertexLit"
}