ShadowGun 图形技术分析

https://zhuanlan.zhihu.com/p/27966138

ShadowGun虽然是2011年的移动平台的游戏demo,但是里面的很多优化技巧到现在来看都是很值得学习的,毕竟是上过西瓜大会的。

网上现存的两份代码一个是shadow gun sample level,一个游戏场景,没法玩,只有一个摄像机动画,asset store上已经找不到了,另外一个是Shadowgun: Deadzone GM‘s Kit,带服务器,可以玩,asset store上还可以下载到。

下面就通过阅读demo中的代码来一起学习下。

飘动的旗帜

用的就是GPUGems里面的技术Vegetation Procedural Animation and Shading in Crysis,基本原理就是在mesh的顶点色中刷入权重,利用GPU顶点动画来模拟布料被风吹的效果。

在maya里看下mash的顶点色

Shader里面

输入的参数

Properties {
	_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
	//刮风的方向(世界坐标系下)
	_Wind("Wind params",Vector) = (1,1,1,1)
	//风的频率
	_WindEdgeFlutter("Wind edge fultter factor", float) = 0.5
	//风的频率的缩放
	_WindEdgeFlutterFreqScale("Wind edge fultter freq scale",float) = 0.5
}

_Time是Unity的一个内置 float4变量(t/20, t,t*2, t*3),专门用来做shader动画的,

看下vert里面的关键代码

//计算风的一些参数

//计算风的一些参数
float4	windParams	= float4(0,_WindEdgeFlutter,bendingFact.xx);
float2	windTime = _Time.y * float2(_WindEdgeFlutterFreqScale,1);
float4	mdlPos	= AnimateVertex2(v.vertex,v.normal,windParams,wind,windTime);
//mvp矩阵变换
o.pos 	= mul(UNITY_MATRIX_MVP,mdlPos);

所以最核心的函数就是AnimateVertex2,看下它是怎么将模型里面的位置v.vertex转换到被风吹动的mdlPos。

inline float4 AnimateVertex2(float4 pos, float3 normal, float4 animParams,float4 wind,float2 time)
{
	// animParams stored in color
	// animParams.x = branch phase
	// animParams.y = edge flutter factor
	// animParams.z = primary factor
	// animParams.w = secondary factor

	float fDetailAmp = 0.1f;
	float fBranchAmp = 0.3f;

	// Phases (object, vertex, branch)
	float fObjPhase = dot(_Object2World[3].xyz, 1);
	float fBranchPhase = fObjPhase + animParams.x;

	float fVtxPhase = dot(pos.xyz, animParams.y + fBranchPhase);

	// x is used for edges; y is used for branches
	float2 vWavesIn = time.yy  + float2(fVtxPhase, fBranchPhase );

	// 1.975, 0.793, 0.375, 0.193 are good frequencies
	float4 vWaves = (frac( vWavesIn.xxyy * float4(1.975, 0.793, 0.375, 0.193) ) * 2.0 - 1.0);

	vWaves = SmoothTriangleWave( vWaves );
	float2 vWavesSum = vWaves.xz + vWaves.yw;

	// Edge (xz) and branch bending (y)
	float3 bend = animParams.y * fDetailAmp * normal.xyz;
	bend.y = animParams.w * fBranchAmp;
	pos.xyz += ((vWavesSum.xyx * bend) + (wind.xyz * vWavesSum.y * animParams.w)) * wind.w; 

	// Primary bending
	// Displace position
	pos.xyz += animParams.z * wind.xyz;

	return pos;
}

关键思想是分层blend,首先计算了由主体到枝干再到顶点的震动系数,edge指旗子的边缘和自身xz方向的震动,branch指的是旗子整体的y方向的上下移动,接下来用了一些很trick的方法算出了一个float2的位移值,这个值就是顶点的位置,然后是将顶点的位移blend到主干上去,接着是主干上的位移blend到代码有点不讲道理,最后再把结果在风的方向上位移一定系数的距离。

UVAnimation

UVAnimation可以分为三个讲,滚滚浓烟,分层滚动天空盒,水面波纹

先说最简单的天空盒,就是两套UV速度,以不同的速率变化

o.uv = TRANSFORM_TEX(v.texcoord.xy,_MainTex) + frac(float2(_ScrollX, _ScrollY) * _Time);
o.uv2 = TRANSFORM_TEX(v.texcoord.xy,_DetailTex) + frac(float2(_Scroll2X, _Scroll2Y) * _Time);

最后又叠了一个颜色用来调节明暗关系。

fixed4 frag (v2f i) : COLOR
{
	fixed4 o;
	fixed4 tex = tex2D (_MainTex, i.uv);
	fixed4 tex2 = tex2D (_DetailTex, i.uv2);

	o = (tex * tex2) * i.color;

	return o;
}

晚上竟然又月亮。

滚滚浓烟

还是用了顶点色

看下Mesh

地下的红色,和烟的颜色叠起来,表现火焰的感觉。

贴图是两张不同的烟,用来表现层次感。

Shader和天空盒的基本一致。

不要觉得上面两个shader比较简单就没人用了,可以自习对比下cfm的运输船

“Volumetric” effects

所谓的体效果包括了Glow,Light Shafts,Fog Plane,Emissive BillBoards

为了模拟光从窗户投射进来,用了一个透明的片来表现

但不是单纯地半透明片,它是View distance based fade out,有下面两个特点

1) 随着视角的接近,透明的程度变大,离得特别远得时候,透明度也会变大

2) Mesh的位置会随着摄像机的位置变化,接近的时候有一种推开的感觉(减少overdraw)

减少overdraw的同时,规避了透明片插在摄像机里的问题。

都是vertex shader 干的

核心的代码

float3	viewPos	= mul(UNITY_MATRIX_MV,v.vertex);
float		dist		= length(viewPos);
float		nfadeout	= saturate(dist / _FadeOutDistNear);
float		ffadeout	= 1 - saturate(max(dist - _FadeOutDistFar,0) * 0.2);

关于saturate函数:camps the specified value within the range of 0 to 1.

简单的说就是跟据面片到摄像机的距离计算出淡入淡出的系数。具体计算可以参考这里

面片涂了顶点色

在计算位置的时候会根据alpha值来计算推开的距离

float4 vpos = v.vertex;
vpos.xyz -=   v.normal * saturate(1 - nfadeout) * v.color.a * _ContractionAmount;

官方的说法是这样

Vertex color alpha determines which vertices are moveable and which are not (in our case, vertices with black alpha stays, those with white alpha moves).

Vertex normal determines the direction of movement.

The shader then evaluates distance to the viewer and handles surface fade in/out appropriately.

为了实现这些效果,渲染了一大推的半透明物体,在移动平台上,会引起严重的overdraw。为了解决overdraw的问题,做了下面几点

1. 使用最简单的fragmentshader,基本上就只采样一张贴图。如果插值的结果不太好就用密一些的网格。

2. 减少半透明的面积,这个在shader里面已经体现了

还有几个用来模拟灯的地方

特点是会随机闪动。

Mesh方面还是刷了顶点色

插在面片上的两个长条三角形目测是为了防止在摄像机靠近的时候被culling掉。

闪动的原理是在vertexshader中利用sin函数计算出一个随机系数乘以o.color.

具体的计算代码如下

float	fracTime	= fmod(time,_TimeOnDuration + _TimeOffDuration);
float	wave		= smoothstep(0,_TimeOnDuration * 0.25,fracTime)  * (1 - smoothstep(_TimeOnDuration * 0.75,_TimeOnDuration,fracTime));
float	noiseTime	= time *  (6.2831853f / _TimeOnDuration);
float	noise		= sin(noiseTime) * (0.5f * cos(noiseTime * 0.6366f + 56.7272f) + 0.5f);
float	noiseWave	= _NoiseAmount * noise + (1 - _NoiseAmount);

wave = _NoiseAmount < 0.01f ? wave : noiseWave;
o.color	= nfadeout * _Color * _Multiplier * wave;

具体的原理可以参考这一篇的分析

Billboarding

用来表现Glow的感觉,用了两个片来模拟

Shader方面,除了前面的View distance based fade out和闪动特性之外,有加了billboarding。

float3	centerOffs		= float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy;
float3	centerLocal	= v.vertex.xyz + centerOffs.xyz;
float3	viewerLocal	= mul(_World2Object,float4(_WorldSpaceCameraPos,1));
float3	localDir			= viewerLocal - centerLocal;

localDir[1] = lerp(0,localDir[1],_VerticalBillboarding);

float		localDirLength=length(localDir);
float3	rightLocal;
float3	upLocal;

CalcOrthonormalBasis(localDir / localDirLength,rightLocal,upLocal);

float		distScale		= CalcDistScale(localDirLength) * v.color.a;
float3	BBNormal		= rightLocal * v.normal.x + upLocal * v.normal.y;
float3	BBLocalPos	= centerLocal - (rightLocal * centerOffs.x + upLocal * centerOffs.y) + BBNormal * distScale;

BBLocalPos += _ViewerOffset * localDir;

在Mesh里面的顶点色是这样的

大概的思路是通过顶点色构建一个坐标系,然后算顶点的偏移。具体的实现可以参考这里

角色阴影

实现方法是在脚下放一个面片,render queue是 transparent – 15,基本是再所有透明物体的之前渲染。然后在面片的vertex shader中算人的AO。

在计算AO的时候,将人近似成球体

Shader里面的代码也很简单

#if 1
		// quite suprisinly this looks better (probably there is some error in AO calculation)
		ao = 1 - saturate(SphereAO(_Sphere0,wrldPos,wrldNormal) + SphereAO(_Sphere1,wrldPos,wrldNormal) + SphereAO(_Sphere2,wrldPos,wrldNormal));
#else
		ao = 1 - max(max(SphereAO(_Sphere0,wrldPos,wrldNormal),SphereAO(_Sphere1,wrldPos,wrldNormal)),SphereAO(_Sphere2,wrldPos,wrldNormal));
#endif

		ao = max(ao,1 - _Intensity) + (1 - v.color.r);
		o.color = fixed4(ao,ao,ao,ao);

#endif

_Sphere0;_Sphere1;_Sphere2;是由外面传进来的三个近似球体的位置,关键看下SphereAO函数

float SphereAO(float4 sphere,float3 pos,float3 normal)
{
	float3	dir = sphere.xyz - pos;
	float	d	= length(dir);
	float	v;

	dir /= d;

	v = (sphere.w / d);

	return dot(normal,dir) * v * v;
}

就是跟据顶点的位置,法线以及球体的中心计算出一个ao值,具体原理参见大神的文章sphere ambient occlusion

参考

Rendering techniques and optimization challenges\

Fast Mobile Shaders\

ShadowGun: Optimizing for Mobile Sample Level\

sphere ambient occlusion\

【Unity Shaders】ShadowGun系列\

ShadowGun 的学习笔记 - GodRays

原文地址:https://www.cnblogs.com/alps/p/8367291.html

时间: 2024-08-14 18:58:06

ShadowGun 图形技术分析的相关文章

负载均衡技术分析与测试报告

负载均衡技术分析与测试报告                 目录 负载均衡测试报告... 1 负载均衡技术概述:... 2 服务器负载均衡... 2 链路负载均衡... 3 Outbound链路负载均衡... 3 Inbound链路负载均衡... 4 常见负载均衡调度算法... 5 测试目的... 6 测试环境搭建... 7 1:原始网络环境... 7 2:测试网络环境... 7 测试设备介绍... 8 1:产品介绍... 8 2:产品操作界面... 8 出现问题... 9 最终解决方案...

智能语音人机交互产业链及关键技术分析

人机交互是一门计算机科学,主要研究关于设计.评价和实现供人们使用的交互计算系统以及相关现象的科学.人机交互的发展经历了以下几个阶段:手工作业阶段.作业控制语言与交互命令语言阶段.图形用户界面(GUI)阶段.网络用户界面,目前已经发展到多通道.多媒体的智能人机交互阶段.其中,语音人机交互是当前多通道.多媒体智能人机交互的主要方式.特别是苹果Siri.科大讯飞语点的出现,让智能语音人机交互技术实现了新的跨越,得到了社会各界的广泛关注. 一.智能语音人机交互产业发展现状 什么是智能语音人机交互技术?简

蓝牙协议分析(7)_BLE连接有关的技术分析

转自:http://www.wowotech.net/bluetooth/ble_connection.html#comments 1. 前言 了解蓝牙的人都知道,在经典蓝牙中,保持连接(Connection)是一个相当消耗资源(power和带宽)的过程.特别是当没有数据传输的时候,所消耗的资源完全被浪费了.因而,对很多蓝牙设备来说(特别是功耗敏感的设备),希望在无数可传的时候,能够断开连接.但是,由于跳频(hopping)以及物理通道(Physical Channel)划分的缘故,经典蓝牙连接

3DSMAX建模技术分析

在游戏美术中,模型的建立是很重要的,今天卡拉小编与大家分享的是3DSMAX建模技术分析1 旋转建模 用于具有中心对称的物体的造型比较简单,只需用工 具画出对称截面,加入(旋转)修改器就可以得到三维实体,有些还可以加入一些特殊变形,以增强表现的效果.2 Loft放样建模 这种建模的含义是将一个二维的交错物件转换为完整的三维物件的一个处理过程,也就是将一个二维的造型物件沿着第三轴向构建出复杂曲面的模型.也可以这样理解这个过程:将许多2D(shape)造型物件紧密且整齐地排列于一条路经(path)上,

蓝牙4.0技术分析1-广播者角色

第1章  BlueTooth Roles-Broadcaster 1.1    广播类型 广播可设置以下几种类型: 1)   Connectable Undirected Event Type(可连接无定向广播) 2)   Connectable Directed Event Type(可连接定向广播) 3)   Scannable Undirected Event Type(可扫描无定向广播) 4)   Non-connectable Undirected Event Type(不可连接无定向

PLSQL_R12 MOAC多组织底层技术实现技术分析(Oracle VPD)

  2014-05-30 BaoXinjian In Capgemini  一. 介绍 之前一直存在对Oracle R12 多组织访问的一些疑惑,所以查询了一些相关资料,并介绍实现R12 MOAC的Oracle VPD技术 Oracle VPD全称Virtual Private Database, Oracle的一种控制数据访问的其中安全策略之一 Oracle数据安全策略访问实现方法 Role-based security create role cust_role; grant select

Java三大主流开源工作流引擎技术分析

Java三大主流开源工作流引擎技术分析 首先,这个评论是我从网上,书中,搜索和整理出来的,也许有技术点上的错误点,也许理解没那么深入.但是我是秉着学习的态度加以评论,学习,希望对大家有用,进入正题! 三大主流工作流引擎:Shark,osworkflow,jbpm! Shark的靠山是Enhydra.Enhydra做过什么呢?多了!从j2ee应用服务器,到o/r mapping工具,到这个工作流引擎等等.为什么Shark的持久层采用DODS来实现?就是因为他们是一家人. Jbpm的靠山是jboss

Wireshark(三):应用Wireshark IO图形工具分析数据流

原文出处: EMC中文支持论坛 基本IO Graphs: IO graphs是一个非常好用的工具.基本的Wireshark IO graph会显示抓包文件中的整体流量情况,通常是以每秒为单位(报文数或字节数).默认X轴时间间隔是1秒,Y轴是每一时间间隔的报文数.如果想要查看 每秒bit数或byte数,点击“Unit”,在“Y Axis”下拉列表中选择想要查看的内容.这是一种基本的应用,对于查看流量中的波峰/波谷很有帮助.要进一步查看,点击图形中的任意点就会看到报文的细 节. 为了讲解方便,点击示

技术分析之OGNL表达式概述

1. OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写 * 所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象 * 通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能.它使用相同的表达式去存取对象的属性 2. Struts2框架使用OGNL作为默认的表达式语言 * OGNL是一种比EL强大很多倍的语言 * xwork提供 OGNL表达式 * ognl-3