这里有Surface Shader的一些例子。下面的这些例子关注使用内建的光照模型;关于如何使用自定义光照模型的例子参见Surface Shader Lighting Examples

简单

  我们将会以一个非常简单的shader作为开始,并在此基础上逐渐完善。下面这个shader会把表面颜色置成“白色”。它使用内建的Lambert(漫反射)光照模型。

Shader "Example/Diffuse Simple" {
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = 1;
}
ENDCG
}
Fallback "Diffuse"
}

  以下是使用两个 lights 作用在某个模型上的样子:

纹理

  一个全白的对象太无聊了,所以让我们添加一个纹理。我们将会在shader中添加一个 Properties block,这样我门就能在材质中获得一个纹理选择器。以下粗体为其他改变:

Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}

法线贴图

  让我们添加些法线贴图:

Shader "Example/Diffuse Bump" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct 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),来使一个对象的边缘高亮。我们将会添加一些基于表面法线和视线方向夹角的散射光线。为此,我们将会使用内建的surface shader变量viewDir。

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
struct 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"
}

细节纹理

  为了有不同的效果,让我们添加一个与基础纹理结合的细节纹理。细节纹理在材质中使用相同的UV,但是通常使用不同的Tiling。所以我们必须使用不同的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
struct 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"
}

  使用一个棋盘格子纹理并没有多大的实际意义,但能说明效果:

屏幕空间的细节纹理

  屏幕空间的细节纹理是什么样的?这对士兵的头部模型来说并没有太大意义,但是能说明内建screenPos 输入是怎么用的:

 Shader "Example/ScreenPos" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Detail ("Detail", 2D) = "gray" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct 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"
}

  上面移除了法线贴图,仅仅是为了使代码更短。

立方体贴图反射

  这里有一个使用内建输入worldRefl的shader实现立方体贴图反射。它实际上和内建的反射/漫反射shader非常像:

Shader "Example/WorldRefl" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Cube ("Cubemap", CUBE) = "" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct 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"
}

  一旦将反射颜色赋值到Emission中,我们就能得到一个非常闪亮的士兵:

  如果你想实现由法线贴图作用的反射效果,只需稍微再加点内容:INTERNAL_DATA 需要被添加到输入结构中,而且在你写入法线输出后,WorldReflectionVector函数用来计算每个顶点反射向量。

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
struct 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"
}

  以下是一个法线贴图作用的光亮的士兵:

世界空间坐标下的切片

  这里的shader通过在接近水平的环上丢弃像素的方式,“切割”对象。它通过对单个世界坐标下的像素,使用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
struct 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 Extrusion

  在顶点shader中,使用用来修改输入顶点数据的“顶点修改器”函数是可能的。这能用于程序动画、法线挤压等情况。surface shader的编译指令 vertex:functionName用来实现它,其函数参数为appdata_full

  以下的shader用来在材质中随法线方向将顶点移动给定的量:

Shader "Example/Normal Extrusion" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_Amount ("Extrusion Amount", Range(-1,1)) = 0.5
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert vertex:vert
struct 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"
}

  随法线方向移动顶点,生成了一个胖士兵:

逐顶点计算自定义数据(Custom data computed per-vertex)

  使用顶点修改器也能用来计算顶点shader中的自定义数据,然后会被逐顶点传入到表面shader函数中。需要使用同样的编译指令vertex:functionName,但该函数在这里要接受两个参数inout appdata_full 和out Input。你还可以加入任意非内建的输入参数。

  注意:这种方式下的自定义成员名不能以“uv”开头,否则将会无效。

  下面的例子定义了自定义成员float customColor,在顶点函数中用于计算:

Shader "Example/Custom Vertex Data" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert vertex:vert
struct Input {
float2 uv_MainTex;
float3 customColor;
};
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(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"
}

  在这个例子中,customColor 被设置成法线的绝对值:

  更实际的用途可以是用来计算非内建输入变量所提供的任意每顶点数据;或者优化shader计算。比如,用对象顶点上,而不是表面shader里逐像素,计算边缘光照。

最终颜色修改器

  可以使用“最终颜色修改器”函数修改由shader计算出的最终颜色。surface shader的编译指令 finalcolor:functionName 用来实现它,其函数参数为Input IN, SurfaceOutput o, inout fixed4 color

  这里有一个简单地shader将染色(tint)应用到最终颜色中。这和仅仅把染色应用到表面Albedo颜色中是不同的:这个染色也会影响任何来自光照贴图、光照探测和类似外源的颜色。

 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
struct 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"
}

使用最终颜色修改器的自定义雾

  最终颜色修改器(见上文)通常用于完全实现自定义雾。雾需要影响最终计算出的像素shader颜色,这正是最终颜色修改器做的。

  这里的shader应用了基于离屏幕中心距离的雾染色。它联合了拥有自定义顶点数据(雾)的顶点修改器和最终颜色修改器。当雾用在正向渲染的附加通道中,需要消隐成黑色。这个例子处理这种情况,并很好地对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
struct Input {
float2 uv_MainTex;
half fog;
};
void myvert (inout appdata_full v, out Input data)
{
UNITY_INITIALIZE_OUTPUT(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"
}

线性雾

Shader "Example/Linear Fog" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGPROGRAM
#pragma surface surf Lambert finalcolor:mycolor vertex:myvert sampler2D _MainTex;
uniform half4 unity_FogColor;
uniform half4 unity_FogStart;
uniform half4 unity_FogEnd; struct Input {
float2 uv_MainTex;
half fog;
}; void myvert (inout appdata_full v, out Input data) {
UNITY_INITIALIZE_OUTPUT(Input,data);
float pos = length(mul (UNITY_MATRIX_MV, v.vertex).xyz);
float diff = unity_FogEnd.x - unity_FogStart.x;
float invDiff = 1.0f / diff;
data.fog = clamp ((unity_FogEnd.x - pos) * invDiff, 0.0, 1.0);
}
void mycolor (Input IN, SurfaceOutput o, inout fixed4 color) {
fixed3 fogColor = unity_FogColor.rgb;
#ifdef UNITY_PASS_FORWARDADD
fogColor = 0;
#endif
color.rgb = lerp (fogColor, color.rgb, IN.fog);
} void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

Unity Shader——Writing Surface Shaders(1)——Surface Shader Examples的更多相关文章

  1. Unity Shader——Writing Surface Shaders(3)——Surface Shader Lighting Examples

    Surface Shader 光照例子 这里有一些自定义光照模型和Surface Shaders的例子.通常的Surface Shader例子在这里. 由于延迟光照在某些自定义的逐材质光照模型中表现得 ...

  2. Unity Shader——Writing Surface Shaders(2)——Custom Lighting models in Surface Shaders

    Surface Shader中的自定义光照模型 当你在编写 Surface Shaders 时,是在描述一个表面的属性(反射颜色.法线……),而且光的交互过程是由一个光照模型来计算的.内建的光照模型有 ...

  3. Unity Shader——Writing Surface Shaders(0)

    从今天起,开始翻译Unity关于shader的官方文档.翻译水平比较一般,目的主要是通过翻译来提升对shader的见解,也让其他人更容易的了解shader.以下开始正文内容: 编写Surface Sh ...

  4. Writing Surface Shaders

    [Writing Surface Shaders] Writing shaders that interact with lighting is complex. There are differen ...

  5. Unity Shaders Vertex & Fragment Shader入门

    http://blog.csdn.net/candycat1992/article/details/40212735 三个月以前,在一篇讲卡通风格的Shader的最后,我们说到在Surface Sha ...

  6. Unity 的“Vertex Lit Rendering path“中 shader Pass 的注意事项

    "MADFINGER/Environment/Unlit (Supports Lightmap)"是 ShadowGun 示例中最简单的 shader 了,如下: // Unlit ...

  7. 【Unity Shader实战】卡通风格的Shader(一)

    写在前面 本系列其他文章: 卡通风格的Shader(二) 呜,其实很早就看到了这类Shader,实现方法很多,效果也有些许不一样.从这篇开始,陆续学习一下接触到的卡通类型Shader的编写. 本篇的最 ...

  8. Unity加载模块深度解析(Shader)

    作者:张鑫链接:https://zhuanlan.zhihu.com/p/21949663来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 接上一篇 加载模块深度解析(二 ...

  9. Unity3D Shader官方教程翻译(十九)----Shader语法,编写表面着色器

    Writing Surface Shaders Writing shaders that interact with lighting is complex. There are different ...

随机推荐

  1. HTML 图像<img>

    定义和用法: img元素向网页中嵌入一副图像. 请注意:从技术上讲,<img>标签并不会在网页中插入图像,而是从网页上链接图像.<img>标签创建的是被引用图像的占位空间. 属 ...

  2. C#中combobox不可编辑与不可选择

    不可编辑:comboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 将Style属性改为csDropDownL ...

  3. css —— 图片环绕+首行缩进

    1.利用css实现图片环绕文字的效果: 只需要给img标签设置float:left/right即可: 2.实现上段文字首行缩进两个字的效果: 使用 text-indent: 2em;即可(em为相对单 ...

  4. c#网络通信框架networkcomms内核解析之九 自定义处理方法的运行机制

    NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本  gplv3协议 我们自己写的处理方法都称之为自定义处理方法 比如,我们在服务器上写的与登陆相关的处理方法 ...

  5. c++英文单词频度统计程序

    英文单词频度统计程序(c++版) 写一个程序,分析一个文本文件(英文文章)中各个次出现的频率,并且把频率最高的十个词打印出来. 分析过程: (1)  简单设想大致分为两大步骤: 1.经过文本文件的读操 ...

  6. Maven排除项目中同名不同版本的jar

    今天突然发现web项目打包后的exe居然有200M+了,心想不应该有这么大的啊,于是检查了一番发现引用的jar有130+个,仔细一瞅发现好多同名的但是不同版本的jar,比如说有commons-http ...

  7. 计算机网络(5)-----ICMP协议和PING程序

    控制报文协议(Internet Control Message Protocol) 定义 它是TCP/IP协议族的一个子协议,用于在IP主机.路由器之间传递控制消息.控制消息是指网络通不通.主机是否可 ...

  8. js 判断数据类型的方法及实现

    转载自 http://blog.csdn.net/xujiaxuliang/archive/2009/10/21/4708353.aspx null 与 undefined 区别: null 是js的 ...

  9. Django+python+BeautifulSoup垂直搜索爬虫

    http://blog.sina.com.cn/s/blog_709475a10100wmln.html

  10. Flex文件结构

    一.文件.目录及其作用.project:描述工程信息,如 本工程名称.工程注释.相关工程信息.编译参数等 .flexProperties:记录与Flex本身相关的信息 .actionScriptPro ...