Surface Shader Examples 详解
2015-11-12 17:32
621 查看
Surface Shader Examples
这里是几个 SurfaceShaders的例子. 例子主要使用内置照明模型; 讲解如何实现基于 Surface
Shader Lighting.的自定义光照模型.
入门
我们将从一个非常简单的着色器开始。这里有一个把表面颜色设置为“白色”的shader。它使用了unity内置的Lambert (diffuse)照明模型。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设置
纯贴图
一个白人对象很无聊,让我们添加一个纹理。我们将添加一个 Propertiesblock(属性块)材质,它是一个的材质选择器。其他的变化以粗体显示如下。
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(边缘照明)来加强对象的边缘。我们将添加一些基于曲面法线夹角和视图方向的emissivelight(放射光)。为此,我们还将使用unity内置表面着色器变量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,但通常不同的材料,所以我们必须使用不同的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" }
法线贴图已经被移除:
反射贴图
这是一个使用unity内置worldRefl输入的shader。效果非常类似于unity内置的反射/漫反射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" }
使用了反射颜色后,我们得到了一个非常闪亮的士兵:
如果你想做受法线贴图影响的反射,需要稍微涉及: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,对象被“片状”水平环剪切。它通过基于世界一个像素的位置使用clip()方法(Cg/ HLSL函数)来实现。我们使用了unity内置表面着色器变量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" }
顶点修改器
可以使用“顶点修改器”功能,将修改输入顶点的着色器中的数据。这可以用于过程性动画,沿法线挤压动画等等。表面着色器使用带inout,appdata_full参数的(vertex:函数名)来编译指令顶点。
下面就是一个沿着指定法线贴图的顶点移动的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" }
沿着法线贴图顶点移动 生成了一个肥胖的士兵。
单顶点自定义数据
顶点着色器也可以使用顶点修饰符函数计算定制数据,然后再传递到表面着色器函数中的单像素。使用相同的编译指令顶点函数,但是函数(vertex:函数名)需要两个参数:inoutappdata_full和out Input。你可以自定义Input因为它不是系统内置属性。
注意:这种自定义输入成员的方式不能使用uv作为名字开头。
下面的例子在顶点计算功能中定义了一个定制float3 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计算。例如,它可以计算边缘照明的对象的顶点,而不是处理表面材质的单像素。
最终颜色修改
可以使用一个“最终颜色修改器”功能,来修改着色器最终颜色。用于表面着色器编译指令需要函数(finalcolor:函数名),并需要参数Input IN,SurfaceOutput o,inout fixed4 color 参数。下面是一个简单应用于最终颜色的的shader。这与色彩应用到表面反照率不同颜色:这个色彩也会受任何来自光照贴图,光探测器和类似的来源的影响。
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" }
最终颜色修改器自定义雾化效果
常见的用例最终颜色修饰符(见上图)将实现完全定制的雾。雾需要影响着色器最终计算像素的颜色,这是finalcolor修改器。还有一个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实现多点触控代码
- 在Unity中实现动画的正反播放代码
- unity实现摄像头跟随
- Unity3D上路_01-2D太空射击游戏
- Unity3D上路_02-第一视角射击游戏
- Unity3D上路_03-塔防游戏
- Unity3D上路_04-基础资源介绍
- Unity3D上路_05-网络相关
- [软件资讯]Unity已支持将3D游戏导出成Flash
- Unity 武器拖尾效果
- unity常见问题之20题
- 反编译ARB program to GLSL shader日记
- Unity3D动画存储插件
- unity 赛车相机跟随物体移动
- 虚幻3引擎
- Unity 预编译宏
- 基于顶点纹理的无限大海水仿真
- Unity AssetDatabase类
- VS2013中 命名空间“UnityEngine”中不存在类型或命名空间名称“UI”。是否缺少程序集引用?
- 图形加速卡技术 [专业的基础技术文章]