【OpenGL】Dota2 Shader分析(1)

转发,请保持地址: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/

时间: 2024-10-02 18:49:54

【OpenGL】Dota2 Shader分析(1)的相关文章

【OpenGL】Shader实例分析(七)- 雪花飘落效果

转发请保持地址:http://blog.csdn.net/stalendp/article/details/40624603 研究了一个雪花飘落效果.感觉挺不错的.分享给大家,效果例如以下: 代码例如以下: Shader "shadertoy/Flakes" { // https://www.shadertoy.com/view/4d2Xzc Properties{ iMouse ("Mouse Pos", Vector) = (100,100,0,0) iChan

【OpenGL】Shader实例分析(六)- 卡牌特效

转发请保持地址:http://blog.csdn.net/stalendp/article/details/30989295 本文将介绍怎么通过alpha通道来隐藏信息,并实现卡牌特效.运行效果如下: 代码如下: Shader "stalendp/imageShine" { Properties { _MainTex ("image", 2D) = "white" {} _NoiseTex("noise", 2D) = &qu

【OpenGL】Shader实例分析(九)- AngryBots中的主角受伤特效

转发请保持地址:http://blog.csdn.net/stalendp/article/details/40859441 AngryBots是Unity官方的一个非常棒的样例.非常有研究价值. 曾经研究的时候.因为其内容丰富,一时间不知道从哪入手写文章分析. 这一段时间研究shader技术比較多一些,就从shader的这一方面開始吧.首先分析当中的一个屏幕特效:当主角受到攻击时会出现的全屏效果(postScreenEffect).效果例如以下: 事实上这是一种的Bloom效果,相关文件有:M

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3)

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(3) 到上一篇为止,拾取一个VBO里的单个图元的问题已经彻底解决了.那么来看下一个问题:一个场景里可能会有多个VBO,此时每个VBO的gl_VertexID都是从0开始的,那么如何区分不同VBO里的图元呢? 指定起始编号 其实办法很简单.举个例子,士兵站成一排进行报数,那么每个士兵所报的数值都不同:这时又来了一排士兵,需要两排都进行报数,且每个士兵所报的数值都不同,怎么办?让第二排士兵从第一排所报的最后一个数值后面接着报就

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2)

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现(2) 上一篇里介绍了Color-Coded Picking的思路和最基本的实现.在处理GL_POINTS时已经没有问题,但是处理GL_LINES.GL_TRIANGLES等时会遇到同一图元的各个顶点颜色不同的问题,这就不能正确拾取了,本问来解决这个问题. 对于GL_LINES,可以用 int objectID = gl_VertexID / 2; 来使得每个线段图元的两个顶点颜色分别相同:对于GL_TRIANGLES,则用

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现

Modern OpenGL用Shader拾取VBO内单一图元的思路和实现 什么意思? 拾取 最简单的理解拾取的方式大概是到(http://www.yakergong.net/nehe/course/tutorial_32.html)玩一下NEHE的拾取游戏.用鼠标点击飞过屏幕的物体就会击中它,这就是拾取的意义. Legacy OpenGL VS Modern OpenGL Legacy OpenGL就是使用glTranslate.glRotate.glScale.gluLookAt.glPers

OpenGL 各个shader的作用和区别

penGL4.0发布了Tessellation shader(Control + Evaluation shader).到OpenGL4.* 为止,现在OpenGL已经支持了5种不同类型的shader. 1.Vertex Shader,简称VS 2.TESS  Control  Shader (D3D11 叫Hull shader),简称TCS 3.TESS Evaluation Shader (D3D叫Domain shader),简称TES 4.Geometry Shader ,简称GS 5

OpenGL中shader使用

引自:http://blog.csdn.net/wl_soft50/article/details/7916720 http://blog.sina.com.cn/s/blog_923fdd9b0102vbe0.html 与OpenGL ES1.x渲染管线相比,OpenGL ES 2.0渲染管线中“顶点着色器”取代了OpenGL ES 1.x渲染管线中的“变换和光照”:“片元着色器”取代了OpenGL ES 1.x渲染管线中的“纹理环境和颜色求和”.“雾”以及“Alpha测试”. 这使得开发人员

【Modern OpenGL】Shader【转】

转自:https://blog.csdn.net/SUKHOI27SMK/article/details/81040161 Shaders 正如在上一篇教程中提到的,shader是在GPU中运行的小程序.如上一个教程中实现的最简单的vertex shader和fragment shader,一个shader基本上负责图形渲染流水线中的一个阶段的功能.从根本上来说,shader就是将输入转化成输出的操作.而且,它们之间是独立的,除了以输入和输出方式外,他们之间不允许进行通信. 上一篇教程中我们仅仅