【OpenGL】Dota2 Shader分析(1)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/stalendp/article/details/50953869

转发,请保持地址: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://ws.cis.sojo-u.ac.jp/~izumi/Unity_Documentation_jp/Documentation/Components/SL-Attenuation.html
http://blog.csdn.net/candycat1992/article/details/41605257
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/


猜你喜欢

转载自blog.csdn.net/stalendp/article/details/50953869