转发,请保持地址:http://blog.csdn.net/stalendp/article/details/50953869
最近在总结OpenGL知识,想到了以前搜索到Dota2相关的文章:《Dota2-Character Shader Masks Guide》,而且dota2的模型在网上也可以下载到,所以非常值得用来作为光照相关知识的总结。我用Ogre模型做实验,效果如下:
这个Shader只是为了总结shader的知识点,对于实现可能比较粗糙了点,所以见谅。
我准备分两部分来讲这个Shader的实现:1)用Surface Shader的方法来展示《Dota2-Character Shader Masks Guide》的算法。2)用Vertex/Fragment的方法来解释,会更加偏向于Surface Shader为我们节省的繁琐事务(比如怎么正确计算法线等)。另外,本文所涉及的资源的下载链接如下:Dota2
Shader for Unity。好的,让我们来进入第一部分吧。
Shader需要四张贴图,如下图:
本文主要介绍两张Mask贴图所对应的算法。
图解算法
先用图解的方式来体会效果,主要是为了强调: 1)可以用图层来包含特殊意义的信息;2)要调好效果,光照模型很重要;这里的效果是Dota2专有的;比较简单的光照模型还有Phong/Blinn模型、BRDF模型等...
我们以角色武器的材质球作为参考,原始贴图颜色:
a. 漫反射部分( Mask1.g)
float3 albedo = tex2D(_MainTex, i.uv).rgb; float diff = max(0.05, dot(worldN, lightDirection)); float3 diffuseReflection = albedo * diff;
Diffuse wrap texture (相关概念:Half-Lambert):
float3 diffuseReflection = albedo * tex2D(_Diffuse, float2(diff, 0.2));
Diffuse的结果
b. 高光相关(Mask2.a,Mask2.r)
不同的材质,高光点的大小是不一样的,越粗糙的,高光点越大但亮度越小,粗糙到一定程度就是漫反射(相反,就是镜面反射)。这里用两张贴图来表明高观点大小(Mask2.a)和强度(Mask2.r):
我们发现,同一种材质的高光强度也会有所不一样,这个是因为考虑了AO(Ambient Occlusion);
float3 specularReflection = pow(max(0.0, dot(reflect(-lightDirection, worldN), viewDirection)), shininess) * specIntensity;
加上漫反射:
漫反射+高光结果:
c. Rimlighting Mask2.g
Rimlighting用来亮化对象边缘,就像对象后面也有光源一样,使得对象更加凸显。不同物质的边缘光强度也不一样,金属的会弱一点,皮革皮肤等会强一些,这里用Mask2.g来控制边缘光的轻度。Mask如下:
float3 rim = rimIntensity * saturate(1 - saturate(dot(worldN, viewDirection))*1.8) ;
漫反射+高光+Rim结果:
d. 自发光(Mask1.a)
自发光是用来模拟热辐射等的效果,mask如下:
diffuseReflection = albedo * lerp(tex2D(_Diffuse, float2(diff, 0.2)), float3(1, 1, 1) * 2, selfIllu);
漫反射+高光+Rim+自发光结果:
d. 更加真实的金属 (Mask1.b, Mask2.b)
f. Detail Map
略
算法原理
这里Shader的原理和Phong/Blinn中的原理比较相似,Phone/Blinn模型主要是用三个向量(Light, View, Normal Direction)来计算环境光(ambient),漫反射(diffuse)和高光(specular)。而Dota2模型是在此基础上,用Mask的方式来对各种光进行细致地区分;再加上了rimLight, DetailMap等其它一些效果。另外,Blinn模型是Phong模型的优化方案(请参考文章:Shading
Model);当然还有物理渲染模型(待我了解之后,在写文章吧 :)
下面介绍一下Phong/Blinn模型:
漫反射
高光
rimLight
完整代码
Shader "stalendp/dotaShaderSuf" { Properties { _MainTex("Albedo (RGB)", 2D) = "white" {} _Normal("Normal", 2D) = "white" {} _Mask1("Mask1", 2D) = "white" {} _Mask2("Mask2", 2D) = "white" {} _Diffuse("Diffuse", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf DotaSpecular noforwardadd noshadow // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; sampler2D _Normal; sampler2D _Mask1; sampler2D _Mask2; sampler2D _Diffuse; struct Input { float2 uv_MainTex; float2 uv_Normal; float2 uv_Mask1; float2 uv_Mask2; float2 uv_Diffuse; }; struct MySurfaceOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Specular; fixed Gloss; fixed Alpha; float2 myuv; }; half4 LightingDotaSpecular(MySurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { fixed4 tex = tex2D(_MainTex, s.myuv); s.Albedo = tex.rgb; float4 mask1 = tex2D(_Mask1, s.myuv); float4 mask2 = tex2D(_Mask2, s.myuv); float specIntensity = mask2.r; float rimIntensity = mask2.g; float tint = mask2.b; float shininess = mask2.a * 10; float metalness = mask1.b; float selfIllu = mask1.a; metalness = 1 - metalness*0.7; float3 viewDirection = viewDir; // normalize(UnityWorldSpaceViewDir(worldPos.xyz)); float3 lightDirection = lightDir; // normalize(_WorldSpaceLightPos0.xyz); // directional light float dr = max(0.05, dot(s.Normal, lightDirection)); float3 diffuseReflection = s.Albedo * metalness * lerp(tex2D(_Diffuse, float2(dr, 0.2)), float3(1, 1, 1) * 2, selfIllu); float3 rim = rimIntensity * saturate(1 - saturate(dot(s.Normal, viewDirection))*1.8); float3 specularReflection = specIntensity * metalness * 4 * lerp(_LightColor0.rgb, _LightColor0.w * s.Albedo, tint) * pow(max(0.0, dot(reflect(-lightDirection, s.Normal), viewDirection)), shininess*metalness); return half4((diffuseReflection + rim + specularReflection) * atten, 1); } void surf (Input IN, inout MySurfaceOutput o) { o.myuv = IN.uv_MainTex; o.Normal = UnpackNormal(tex2D(_Normal, IN.uv_MainTex)); } ENDCG } FallBack "Diffuse" }
参考
Phone VS Blinn模型:http://tech-artists.org/wiki/Shading_models
本文所涉及的素材(包括Unity文件和pdf,需要用Unity5打开):http://download.csdn.net/detail/stalendp/9469097
==== Specular ======
1) Vertex and Fragment Shader Examples: http://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html
2) Diffuse Reflection: https://en.wikibooks.org/wiki/Cg_Programming/Unity/Diffuse_Reflection
3) Smooth Specular Highlights: https://en.wikibooks.org/wiki/Cg_Programming/Unity/Smooth_Specular_Highlights
4) Specular Highlights: https://en.wikibooks.org/wiki/Cg_Programming/Unity/Specular_Highlights
5) Silhouette Enhancement: https://en.wikibooks.org/wiki/Cg_Programming/Unity/Silhouette_Enhancement
6) Applying Matrix Transformations: https://en.wikibooks.org/wiki/Cg_Programming/Applying_Matrix_Transformations
====== rim ======
7) Rim Light: http://in2gpu.com/2014/07/02/rim-lighting/
8) Bump Spec Rim: http://wiki.unity3d.com/index.php?title=BumpSpecRim
9) OpenGL Rim Shader: http://www.roxlu.com/2014/037/opengl-rim-shader
10) Velvet: http://wiki.unity3d.com/index.php/Velvet
======Normal Mapping======
11) Lighting of Bumpy Surfaces: https://en.wikibooks.org/wiki/Cg_Programming/Unity/Lighting_of_Bumpy_Surfaces
12) LightMode非常重要: http://docs.unity3d.com/432/Documentation/Components/RenderTech-ForwardRendering.html(请详细了解Unity的光照模型!!)
13) 3D Texture技术: http://docs.unity3d.com/Manual/class-Texture3D.html
http://docs.unity3d.com/Manual/script-ColorCorrectionLookup.html
14) Half-Lambert: https://developer.valvesoftware.com/wiki/Half_Lambert
15) 参考Unity Shaders and Effects Cookbook Chapter 2 Packing and blending textures
16) Light Attenuation: https://en.wikibooks.org/wiki/GLSL_Programming/Unity/Light_Attenuation
http://blog.csdn.net/candycat1992/article/details/41605257
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/