这里有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. windows 无法分析或处理 pass 报错问题汇总

    日光月华 发表于 2015-2-9 22:02:42 https://www.itsk.com/thread-346404-1-1.html 系统封装失败遇到windows 无法分析或处理 pass ...

  2. Java队列工具类(程序仅供练习)

    public class QueueUtils<T> { public int defaultSize; public Object[] data; public int front = ...

  3. windows防火墙命令详解

    Old command 针对win7以下版本<包含win7> Example 1: 启用一个程序 Old command New command netsh firewall add al ...

  4. SPSS数据分析—卡方检验

    t检验和方差分析主要针对于连续变量,秩和检验主要针对有序分类变量,而卡方检验主要针对无序分类变量(也可以用于连续变量,但需要做离散化处理),用途同样非常广泛,基于卡方统计量也衍生出来很多统计方法. 卡 ...

  5. [hadoop] 一些基础概念

    一.云的概念 1.云计算的概念 随时 随地 使用任何设备 获得任何服务 2.趋势 )资料开始回归集中处理(存储大量资料) 随时存取 降低遗失风险 减少传输成本 促进团队协作 )网页变为预设开发平台(网 ...

  6. 树(二)——二叉树

    目录 本章主要讲解内容为: 树的非递归遍历算法,两种版本 树的扩展前缀以及前缀中缀构建方法 源码 btree.cpp btree.h 基础知识 一.定义 二叉树的递归定义:二叉树是每个结点最多含有两棵 ...

  7. React Native 文本输入

    TextInput是一个允许用户输入文本的基础组件.它有一个名为onChangeText的属性,此属性接受一个函数,而此函数会在文本变化时被调用.另外还有一个名为onSubmitEditing的属性, ...

  8. BestCoder Round #39

    -------好久没更新博客了,发现还是需要不断总结才能进步,所以还是把最近打的一些比赛记录一下. T1:Delete (hdu 5210) 题目大意: 给出n个数,然后要删掉k个,要求剩下的数中 不 ...

  9. CSS中相对定位与绝对定位

    看了几个讲解定位的博客,觉得还不错,分享之: 博客一:http://blog.sina.com.cn/s/blog_4bcf4a5e010008o0.html 文章中,主要需要参考的有两点: 1,相对 ...

  10. 数据结构《16》----自动补齐实现《一》----Trie 树

    1. 简述 Trie 树是一种高效的字符串查找的数据结构.可用于搜索引擎中词频统计,自动补齐等. 在一个Trie 树中插入.查找某个单词的时间复杂度是 O(len), len是单词的长度. 如果采用平 ...