【Unity Shader】手游中高光效果的几种实现方法
2016-01-10 11:08
3171 查看
前言
由于手机设备的性能限制,很多效果的计算都得精简和优化才能达到目地。而在Unity中的高光效果,也给予了多种不同的方案,有用于主机的,用于手机的,有限制一盏像素灯的,开发者可以根据不同的情况酌情选择。Unity的Surface Shader提供了很好的封装性,复杂的灯光处理都不需要开发者去关注,也有提供修改光照函数的接口。但是由于项目特殊性,往往这么简单方便的事情会阻碍使用者的自由发挥。所以我们在很多优秀的游戏作品中都能看到很多自研发Shader都是基于Vert&Frag的,也因此让我们看到很多不同的实现原理和方式(Unity大法好)。对于次世代游戏来说,光是必不可少的,尤其是高光和法线效果。在这里,就介绍在手游上能使用的几种实现方法,并且采用Vert&FragShader。
方案
高光效果跟法线,视向,光向都脱不开干系,主要是如何利用它们和如何组织计算。本文大致分为如下几种方式实现:在顶点函数,世界空间中计算
在顶点函数,切线空间中计算
在片元函数,世界空间中计算
在片元函数,切线空间中计算
分析
在顶点和在片元函数中处理是效率和效果都是不一样的,由于在顶点函数处理是逐顶点计算的,所以是比较快的,但是计算不精准;而在片元中是逐像素计算,所以效率较慢(虽然慢不了多少,但是积少成多),计算更精准。在世界空间和在切线空间中计算也是得出不一样的效果,在切线空间下效果更加细腻平滑,效果也更好。
实现
在顶点函数,世界空间中计算:Shader "Yogi/Specular(Vert-World)" { Properties { _MainTex("Base(RGB) Gloss(A)", 2D) = "white" {} _Specular("Specular", Range(0, 10)) = 1 _Shininess("Shininess", Range(0.01, 1)) = 0.5 } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_fwdbase sampler2D _MainTex; fixed4 _MainTex_ST; fixed _Specular; fixed _Shininess; struct a2v { fixed4 vertex : POSITION; fixed3 normal : NORMAL; fixed4 texcoord : TEXCOORD0; }; struct v2f { fixed4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; fixed3 spec : TEXCOORD1; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); fixed3 normal = mul((fixed3x3)_Object2World, SCALED_NORMAL); fixed3 viewDir = normalize(WorldSpaceViewDir(v.vertex)); fixed3 lightDir = normalize(WorldSpaceLightDir(v.vertex)); fixed nh = saturate(dot(normal, normalize(viewDir + lightDir))); o.spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular; return o; } fixed4 frag(v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv.xy); fixed4 o = c; o.rgb += i.spec * c.a; return o; } ENDCG } } FallBack "Mobile/Diffuse" }
在顶点函数,切线空间中计算:
Shader "Yogi/Specular(Vert-Tangent)" { Properties { _MainTex("Base(RGB) Gloss(A)", 2D) = "white" {} _Specular("Specular", Range(0, 10)) = 1 _Shininess("Shininess", Range(0.01, 1)) = 0.5 } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_fwdbase sampler2D _MainTex; fixed4 _MainTex_ST; fixed _Specular; fixed _Shininess; struct a2v { fixed4 vertex : POSITION; fixed3 normal : NORMAL; fixed4 tangent : TANGENT; fixed4 texcoord : TEXCOORD0; }; struct v2f { fixed4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; fixed3 spec : TEXCOORD1; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); TANGENT_SPACE_ROTATION; fixed3 normal = mul(rotation, SCALED_NORMAL); float3 viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex))); float3 lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); fixed nh = saturate(dot(normal, normalize(viewDir + lightDir))); o.spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular; return o; } fixed4 frag(v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv.xy); fixed4 o = c; o.rgb += i.spec * c.a; return o; } ENDCG } } FallBack "Mobile/Diffuse" }
在片元函数,世界空间中计算:
Shader "Yogi/Specular(Frag-World)" { Properties { _MainTex("Base(RGB) Gloss(A)", 2D) = "white" {} _Specular("Specular", Range(0, 10)) = 1 _Shininess("Shininess", Range(0.01, 1)) = 0.5 } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_fwdbase sampler2D _MainTex; fixed4 _MainTex_ST; fixed _Specular; fixed _Shininess; struct a2v { fixed4 vertex : POSITION; fixed3 normal : NORMAL; fixed4 texcoord : TEXCOORD0; }; struct v2f { fixed4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; fixed3 normal : TEXCOORD1; fixed3 viewDir : TEXCOORD2; fixed3 lightDir : TEXCOORD3; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.normal = mul((float3x3)_Object2World, SCALED_NORMAL); o.viewDir = normalize(WorldSpaceViewDir(v.vertex)); o.lightDir = normalize(_WorldSpaceLightPos0.xyz); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv.xy); fixed4 o = c; fixed nh = saturate(dot(i.normal, normalize(i.viewDir + i.lightDir))); fixed3 spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular; o.rgb += spec * c.a; return o; } ENDCG } } FallBack "Mobile/Diffuse" }
在片元函数,切线空间中计算:
Shader "Yogi/Specular(Frag-Tangent)" { Properties { _MainTex("Base(RGB) Gloss(A)", 2D) = "white" {} _Specular("Specular", Range(0, 10)) = 1 _Shininess("Shininess", Range(0.01, 1)) = 0.5 } SubShader { Pass { CGPROGRAM #include "UnityCG.cginc" #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #pragma multi_compile_fwdbase sampler2D _MainTex; fixed4 _MainTex_ST; fixed _Specular; fixed _Shininess; struct a2v { fixed4 vertex : POSITION; fixed3 normal : NORMAL; fixed4 tangent : TANGENT; fixed4 texcoord : TEXCOORD0; }; struct v2f { fixed4 pos : SV_POSITION; fixed2 uv : TEXCOORD0; fixed3 normal : TEXCOORD1; fixed3 viewDir : TEXCOORD2; fixed3 lightDir : TEXCOORD3; }; v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); TANGENT_SPACE_ROTATION; o.normal = mul(rotation, v.normal); o.viewDir = normalize(mul(rotation, ObjSpaceViewDir(v.vertex))); o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); return o; } fixed4 frag(v2f i) : SV_Target { fixed4 c = tex2D(_MainTex, i.uv.xy); fixed4 o = c; fixed nh = saturate(dot(i.normal, normalize(i.viewDir + i.lightDir))); fixed3 spec = _LightColor0.rgb * pow(nh, _Shininess * 128) * _Specular; o.rgb += spec * c.a; return o; } ENDCG } } FallBack "Mobile/Diffuse" }
四个高光效果对比:
最后
开发者可以根据不同的手机配置使用不同的Shader,或者做多个SubShader,通过设定ShaderLod切换。相关文章推荐
- Unity之动态加载场景资源
- 【Unity入门】编辑器常用视图介绍
- Unity_Shader开发_图形学基础(五)--------2016.1.9
- 【Unity闲谈】自动处理2.5D(伪3D)游戏中 物体的遮挡关系
- unity 架构设计的学习
- 总结使用Unity3D优化游戏运行性能的经验
- Unity 解决 An asset is marked with HideFlags.DontSave but is included in the build 问题。
- 关于Unity3D接入SDK之后,游戏中的文字输入框无法输入内容的问题
- 【Unity Shader】一个用于手游的次世代角色Shader
- Unity3D将来时:IL2CPP(上)
- Unity PSD Export Tool
- Unity3D游戏开发者该如何有效利用其特性
- 用于Unity3D项目的简单音频管理代码AudioManager
- 关于Unity启动时间过长(启动黑屏时间长)的问题
- Unity 基础 XML文件解析
- 关于Unity打Android包自动添加权限的问题
- Unity打包内部prefab和读取外部assetbundle的方法详解。
- 如何有效避免Unity3D游戏开发编辑器的“坑”
- unity animator的一种使用方式
- unity 中clear和深度ztest zwrite问题