查看: 2484|回复: 3

[Shaders] 编写表面着色器:表面着色器示例

[复制链接]

2317

主题

54

听众

2万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
20645
精华
62

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2014-8-31 23:39:37 |显示全部楼层

Here are some examples of Surface Shaders. The examples below focus on using built-in lighting models; examples on how to implement custom lighting models are in Surface Shader Lighting Examples.下面是一些表面着色器(Surface Shaders)的示例。下面的示例都是使用的内置光照模式(lighting models),关于如何实现自定义光照模式可以参考 表面着色器光照范例(Surface Shader Lighting Examples)。

Simple 简单的示例We'll start with a very simple shader and build up on that. Here's a shader that just sets surface color to "white". It uses built-in Lambert (diffuse) lighting model.我们从分析和建立一个简单的着色器开始。下面是这个着色器仅仅设置了表面颜色( surface color)为"白色"。它使用了内置的 Lambert (diffuse)光照模式(lighting model)。

Shader "Example/Diffuse Simple" {
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float4 color : COLOR;
      };
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = 1;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }Here's how it looks like on a model with two lights set up:下面是效果。它看起来像是在模型上设置了两个光照(lights)。



Texture 纹理An all-white object is quite boring, so let's add a texture. We'll add a Properties block to the shader, so we get a texture selector in our Material. Other changes are in bold below.看一个白模是相当枯燥的。所以我们来给它添加纹理(texture)。我们将在着色器的属性(Properties)块中添加。在材质球(Material)中我们选择一个纹理(texture)。有变化的地方在下面用粗体字表示出来了。  Shader "Example/Diffuse Texture" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
      };
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"

  }



Normal mapping 法线贴图Let's add some normal mapping:我们来添加一些法线贴图(normal map)。  Shader "Example/Diffuse Bump" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
        float2 uv_MainTex;
        float2 uv_BumpMap;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
      ENDCG
    }
    Fallback "Diffuse"
  }



Rim Lighting 边缘光照Now, try to add some Rim Lighting to highlight the edges of an object. We'll add some emissive light based on angle between surface normal and view direction. For that, we'll use viewDir built-in surface shader variable.现在我们试着添加边缘光照(Rim Lighting),在对象的边缘部分增加亮度。我们要在表面法线(surface normal)和视图方向(view direction)的基础上添加散射光照(emissive light)。为了实现它,我们要使用 viewDir,这是表面着色器(surface shader)内置的一个变量。

Shader "Example/Rim" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
      _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 viewDir;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      float4 _RimColor;
      float _RimPower;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
          half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
          o.Emission = _RimColor.rgb * pow (rim, _RimPower);
      }
      ENDCG
    }
    Fallback "Diffuse"
  }



Detail Texture 细节纹理For a different effect, let's add a detail texture that is combined with the base texture. Detail texture uses the same UVs, but usually different Tiling in the Material, so we have to use different input UV coordinates.为了实现不同的效果。让我们来添加细节纹理(detail)。它是与基础纹理(base texture)的结合。细节纹理(detail texture)与基础纹理(base texture)使用相同的UV。但是在材质球(Material)中平铺(Tiling)值通常是不同的。所以我们必须输入结构(input s***cture)中使用不同的UV坐标。  Shader "Example/Detail" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _Detail ("Detail", 2D) = "gray" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float2 uv_Detail;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      sampler2D _Detail;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
      ENDCG
    }
    Fallback "Diffuse"
  }Using a checker texture does not make much practical sense, but illustrates what happens:使用一个方格纹理(chercker texture)并没有实际的意义。但至少让我明白了会产生什么效果:


Detail Texture in Screen Space 在屏幕空间使用细节纹理How about a detail texture in screen space? It does not make much sense for a soldier head model, but illustrates how a built-in screenPos input might be used:怎样在屏幕空间(screen space)使用细节纹理(detail texture)?它对一个士兵的头部模型没有多大意义。但它说明了在输入结构(input s***cture)中如何使用内置的screenPos。  Shader "Example/ScreenPos" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _Detail ("Detail", 2D) = "gray" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float4 screenPos;
      };
      sampler2D _MainTex;
      sampler2D _Detail;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
          screenUV *= float2(8,6);
          o.Albedo *= tex2D (_Detail, screenUV).rgb * 2;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }I removed normal mapping from the shader above, just to make it shorter:从着色器中删除法线贴图(normal mapping)只是为了试着色器代码简短一点。



Cubemap Reflection 立方图反射Here's a shader that does cubemapped reflection using built-in worldRefl input. It's actually very similar to built-in Reflective/Diffuse shader:在这里要在输入结构(input s***cture)中使用内置的worldRefl 来做立方图反射(cubemap reflection)。它实际是与内置的 Reflective/Diffuse着色器非常类似。  Shader "Example/WorldRefl" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _Cube ("Cubemap", CUBE) = "" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float3 worldRefl;
      };
      sampler2D _MainTex;
      samplerCUBE _Cube;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
          o.Emission = texCUBE (_Cube, IN.worldRefl).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }And since it assigns the reflection color as Emission, we get a very shiny soldier:因为它指定了和自发光(Emission)一样的反射颜色(reflection color),所以我们得到了一个非常有光泽的士兵。If you want to do reflections that are affected by normal maps, it needs to be slightly more involved: INTERNAL_DATA needs to be added to the Input s***cture, and WorldReflectionVector function used to compute per-pixel reflection vector after you've written the Normal output.如果你想在这基础上做反射效果,它是要受到法线贴图(normal map)的影响的。这需要在输入结构(input s***cture)中加入一个稍微复杂的 INTERNAL_DATA。在世界反射向量(WorldReflectionVector)函数中计算每个像素(per-pixel)反射向量(reflection vector),然后你要在输出结构(output s***cture)中写入法线(normal)。  Shader "Example/WorldRefl Normalmap" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
      _Cube ("Cubemap", CUBE) = "" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 worldRefl;
          INTERNAL_DATA
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      samplerCUBE _Cube;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
          o.Emission = texCUBE (_Cube, WorldReflectionVector (IN, o.Normal)).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }Here's a normal mapped shiny soldier:这是一个贴了法线贴图(normal map)的有光泽的士兵。



Slices via World Space Position 通过世界空间位置进行切割Here's a shader that "slices" the object by discarding pixels in nearly horizontal rings. It does that by using clip() Cg/HLSL function based on world position of a pixel. We'll use worldPos built-in surface shader variable.在这个着色器里,被"切割" 所抛弃的像素(pixels)形状是几乎与水平位置平行的环状。它是基于世界位置的像索(pixel)通过使用Cg/HLSL语言的 clip()函数实现。我们将使用表面着色器(surface shader)内置的worldPos变量。

  Shader "Example/Slices" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      Cull Off
      CGPROGRAM
      #pragma surface surf Lambert
      s***ct Input {
          float2 uv_MainTex;
          float2 uv_BumpMap;
          float3 worldPos;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      void surf (Input IN, inout SurfaceOutput o) {
          clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
      ENDCG
    }
    Fallback "Diffuse"
  }



Normal Ex***sion with Vertex Modifier 法线挤压与顶点变化It is possible to use a "vertex modifier" function that will modify incoming vertex data in the vertex shader. This can be used for procedural animation, ex***sion along normals and so on. Surface shader compilation directive vertex:functionName is used for that, with a function that takes inout appdata_full parameter.它可以在顶点着色器(vertex shader)中使用"顶点修饰(vertex modifier)"函数修改传入的顶点(vertex)数据。它能作用在程序动画上。比如顺着法线挤压等等。表面着色器(surface shader)是通过编译vertex:functionName函数指令来使用它。这个函数传入的参数是 inout appdata_full 。Here's a shader that moves vertices along their normals by the amount specified in the material:这个着色器它在材质(material)里面顶点是随着法线变化的:

Shader "Example/Normal Ex***sion" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _Amount ("Ex***sion Amount", Range(-1,1)) = 0.5
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert vertex:vert
      s***ct Input {
          float2 uv_MainTex;
      };
      float _Amount;
      void vert (inout appdata_full v) {
          v.vertex.xyz += v.normal * _Amount;
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }Moving vertices along their normals makes a fat soldier:顶点随着它的法线(normal)变化后生成一个浮肿的士兵:



Custom data computed per-vertex 用自定义数据计算每个顶点变化Using a vertex modifier function it is also possible to compute custom data in a vertex shader, which then will be passed to the surface shader function per-pixel. The same compilation directive vertex:functionName is used, but the function should take two parameters: inout appdata_full and out Input. You can fill in any Input member that is not a built-in value there.在一个顶点着色器(vertex shader)中通过计算自定义数据也可以实现上面的顶点变化函数(vertux modifier function)的效果。我们这就通过表面着色器函数(surface shader function)来计算每个顶点(per-vertex)变化。同样需要使用编译vertex:functionName这个函数指令。但是这次我们传入两个参数: inout appdata_full 和 out Input o 。在这里你能传入任何一个属于输入结构(input s***cture)的成员,而不是内置的值。Example below defines a custom float3 customColor member, which is computed in a vertex function:下面的例子定义了一个自定义的float3 customColor成员,它将在顶点函数(vertex function)中运算:  

Shader "Example/Custom Vertex Data" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert vertex:vert
      s***ct Input {
          float2 uv_MainTex;
          float3 customColor;
      };
      void vert (inout appdata_full v, out Input o) {
          o.customColor = abs(v.normal);
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
          o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
          o.Albedo *= IN.customColor;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }In this example customColor is set to the absolute value of the normal:在这个例子里customColor值设置的是法线(normal)的绝对值。More practical uses could be computing any per-vertex data that is not provided by built-in Input variables; or optimizing shader computations. For example, it's possible to compute Rim lighting at object's vertices, instead of doing that in the surface shader per-pixel.计算所有每个顶点(per-vertex)数据可以有更多的用途。它不是提供内置的输入结构(input s***cture)内的变量,或者优化着色器计算。例如:它可以计算在对象的顶点((vertex)上计算边缘光照(Rim lighting),而不是在表面着色器(surface shader)的每个像索(per-pixel)内做。



Final Color Modifier 最终颜色变化It is possible to use a "final color modifier" function that will modify final color computed by the shader. Surface shader compilation directive finalcolor:functionName is used for that, with a function that takes Input IN, SurfaceOutput o, inout fixed4 color parameters.这可以使用了一个"最终颜色变化(final color modifier)"函数,这个函数将通过着色器计算变化的最终颜色。为了实现它要使用finalcolor:functionName这个表面着色器(surface shader)编译命令。这个函数传入的参数是Input IN, SurfaceOutput o, inout fixed4 color 。Here's a simple shader that applies tint to final color. This is different from just applying tint to surface Albedo color: this tint will also affect any color that came from lightmaps, light probes and similar extra sources.下面是一个简单的着色器(shader),它适用于给最终颜色着色。这是一个特别的仅适用于给表面反射率的颜色(surface Albedo color)着色。 这个色调也会影响任何颜色的光照贴图(lightmap), 光照探测(light probes)和类似的特别资源。

Shader "Example/Tint Final Color" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _ColorTint ("Tint", Color) = (1.0, 0.6, 0.6, 1.0)
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert finalcolor:mycolor
      s***ct Input {
          float2 uv_MainTex;
      };
      fixed4 _ColorTint;
      void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
      {
          color *= _ColorTint;
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
           o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }



Custom Fog with Final Color Modifier 自定义雾效与最终颜色变化Common use case for final color modifier (see above) would be implementing completely custom Fog. Fog needs to affect the final computed pixel shader color, which is exactly what the finalcolor modifier does.在共用最终颜色变化(final color modifier)的情况下将完全实现自定义雾效。雾效需要受到最终计算像索的着色器颜色的影响。这正是finalcolor做的。Here's a shader that applies fog tint based on distance from screen center. This combines both the vertex modifier with custom vertex data (fog) and final color modifier. When used in forward rendering additive pass, Fog needs to fade to black color, and this example handles that as well with a check for UNITY_PASS_FORWARDADD.下面这个着色器适用于给基于屏幕中心距离远近的雾效来着色。这结合了顶点变化(vertex modifier)与自定义顶点数据(custom vertex data)(雾效)还有最终颜色变化(final color modifier)。当附加到正向渲染(forward render)通道(pass)中使用时,雾效(Fog)需要淡入淡出黑色。在这个例子中控制它还不如检查 UNITY_PASS_FORWARDADD。  

Shader "Example/Fog via Final Color" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _FogColor ("Fog Color", Color) = (0.3, 0.4, 0.7, 1.0)
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert finalcolor:mycolor vertex:myvert
      s***ct Input {
          float2 uv_MainTex;
          half fog;
      };
      void myvert (inout appdata_full v, out Input data)
      {
          float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex);
          data.fog = min (1, dot (hpos.xy, hpos.xy) * 0.1);
      }
      fixed4 _FogColor;
      void mycolor (Input IN, SurfaceOutput o, inout fixed4 color)
      {
          fixed3 fogColor = _FogColor.rgb;
          #ifdef UNITY_PASS_FORWARDADD
          fogColor = 0;
          #endif
          color.rgb = lerp (color.rgb, fogColor, IN.fog);
      }
      sampler2D _MainTex;
      void surf (Input IN, inout SurfaceOutput o) {
           o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
      }
      ENDCG
    }
    Fallback "Diffuse"
  }


回复

使用道具 举报

hyui    

1

主题

2

听众

6671

积分

高级设计师

Rank: 6Rank: 6

纳金币
2715
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2014-9-1 04:46:54 |显示全部楼层
感谢分享!
回复

使用道具 举报

16

主题

1

听众

1万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
3
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2014-9-6 15:05:57 |显示全部楼层
多谢分享
回复

使用道具 举报

heise    

4

主题

3

听众

4249

积分

中级设计师

Rank: 5Rank: 5

纳金币
105
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

发表于 2014-9-7 01:51:39 |显示全部楼层

谢谢分享,支持!!!!!!!!!!!!!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|纳金网 ( 闽ICP备08008928号

GMT+8, 2024-4-18 22:22 , Processed in 0.100994 second(s), 33 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部