Cutscene Editor Slate Tutorial since by 2016.

这部影片是韩国人的声音。
但是您只能通过观看视频来理解它。

This video is a Korean voice.
But you can understand it only by watching the video.

Unified shader works.

Unified shader works. Organization / Netease Pangu Studio Technical Art Team Unit.Author / JP.LeeDirect Contact / hzlizhengbiao@corp.netease.com Overview.Milestone

TASK 任务Achievement goal 成就目标1 Month2 Month3 Month

Shader Layout 着色器布局 Ground , Building , Skin , etc… “尽可能整合各种目的的Shader。把Shader编译容量降为最低。Ground , Building , Skin , etc…” Ground , Building,Skin shaderGlobal Foliage ,Cloth with armor

本文档是关于所有角色和场景中使用的Shader的说明。

还包含对各个Shader光照模型概要和Unity PBR Shader的基本设计的解释。角色根据LOD的不同,单独设置了材质球和Shader,场景大的分为两类,分类如下: 最重要的核心就是分析完个别Shader的性能之后,该怎么定义使用到个别的场景?我们基于大O符号(大致表现出函数上限)技法,可以达成大致的性能测定。虽然,稍微有点抽象,我们把我们大致所知的运行时间定义为被填充的水,我们就需要预测水是否能充满100升的水桶。因为是以大O符号的概念来构成Shader大致的性能的,性能测定的结果无法直接定义是否超出函数上限。结论就是把函数上限设置为120,测定第一版完成的Shader,并记录。等场景制作完成,除了Shader之外,完成第一轮优化之后,把函数上限设置为100以下,对第一版应用进去的Shader再进行一次过滤(删除不需要的功能),对于剩下的Shader我们要准备优化方案。因为函数上限要定义起来很难,把能预测到的函数上限定为90,并进行最终的优化。

场景Shader

1. 地表Shader。

2. 物件Shader。

地表Shader是Ground Shader,物件Shader是Prop Shader。大的部分为两个,按照使用目的,还做了细分。

这边最重要的就是根据使用环境,一定要选用合适得Shader。(写Shader的人需要理解美术的使用需求,反之,美术需要把使用目的告诉写Shader的那位。)

基本概要参考此文档就好。

Prop Shaders.

目录区分:

Shader分为动态物件和静态物件。

理由就是Unity内置Shader的特性,MobileBlinnPhong完全不支持光照贴图效果,性能层面使用了很简单的光照公式,合适用于手游开发。

但是,完全不支持Unity的新功能。(如高光lightmap,Baked GI等等。。。)

理解构成Shader的大概念是很重要的。

1. 根据物件在游戏中的重要程度,稍微使用了不同的光照模型。

2. 原来是按照Uber Shader形态来开发的,但是要频繁确认美术是否有设置错误,选项是否有选错,所以,按照功能别,给Shader做了分类。

Dynamic Shader Group.(动态Shader组)

PropSimpleBumpedSpecular : Custom Blinn-Phong based

PropSimpleBumpedSpecular(ReflectionCube) : Custom Blinn-Phong based

1. 使用传统简单的光照计算公式。

2. 简化了Unity内置的Blinn-Phong。

3. 可以用于很小的物件,玩家获得的道具,破碎的物件等。

4. 用于陷阱等能给玩家造成伤害的物件。

5. 不支持动态批处理。(Shader运算超出了动态批处理的规则。)

6. 动态物件因为有很多效果的功能,所以分离了基本Shader。例如:自发光效果,或者未来加入动态物件的一些效果。

Static Group.

PropSimple : Lambert based

PropSimple(ReflectionCube) : Lambert based

1. 使用很传统简单的光照公式。

2. 无法表现出镜面反射。

3. NormalMap表现无法做。

4. 推荐使用 : 可以部分用于LOD 1和不重要的物件中。

– 但是,需要美术自己判断,用于即使没有NormalMap也可以充分表现出立体感的物件。

PropSimpleSpecular : Blinn Phong based

PropSimpleSpecular(ReflectionCube) : Blinn Phong based

1. 使用Unity内置Blinn-Phong光照模型。

2. 虽然,能表现出简单的镜面反射,但是无法表现NormalMap。

3. 推荐使用 : 可以部分用于LOD 1和不重要的物件中。

– 但是,需要美术自己判断,用于即使没有NormalMap也可以充分表现出立体感的物件。

5. 提示.

– 2011年前后在手机环境上使用NormalMap比现在来说要恶略得很多,提前把光照信息烘培到Albedo上是一种比较流行的方式。(一些特殊的游戏类型除外,如:无尽之剑)

PropSimpleBumped : Lambert based

PropSimpleBumped(ReflectionCube) : Lambert based

1. 使用传统简单的光照公式。

2. 无法表现镜面反射。

3. 用CubeMap来模拟镜面反射。

4. 推荐使用:几户没有镜面反射的自然物件,未经雕磨的石头,用木头做的物件等。用CubeMap可以同时表现物件铁制品部分得表现。

PropSimpleBumpedSpecular :: Phong based

PropSimpleBumpedSpecular(ReflectionCube) :: Phong based

PropSimpleBumpedSpecular :: Phong based

PropSimpleBumpedSpecular(ReflectionCube) :: Phong based

1. 使用传统简单的光照公式。

2. 比BlinnPhong效率要好。大部分的精度是half,或者以下。以GLSL精度来看就是mediump,或者以下。

– 众所周知,在opengl es中,half和fixed没有大的差异。

3. 推荐·使用:用于一般的物件。

– 其它Shader和其它特性,达成了Specular参数和CubeMap间的直接关系。

4. Shader目录:

Tianyu Shaders/Scene/Prop/Static/PropBumpedSpecular

Tianyu Shaders/Scene/Prop/Static/PropBumpedSpecular(ReflectionCube)

PropBumpedSpecular : BlinnPhong based

PropBumpedSpecular(ReflectionCube) : BlinnPhong based

PropBumpedSpecular(Reflection Probe) : BlinnPhong based

1. 使用Unity内置Blinn Phong。

2. 和PropSimpleBumpedSpecular几乎类似,美术根据视觉效果评测是否使用。

3. 因为用于静态物件,所以不受LightProbe影响。

4. 特性上,还有一个使用ReflectionProbe进行发射处理的Shader。美术判断,在无法使用CubeMap的地方可以使用它。

CubeMap的一般设置:

ttps://en.wikipedia.org/wiki/Cube_mapping

反射贴图运算,在手游环境中考虑到性能是不太推荐的。

但是,近几年,带宽使低分辨率的 CubeMap程度使用起来变容易了。

实际,根据带宽,对于测定像素Shading还是有大的差异的。 (结果当然是这样的。)

1. 性能改善对策

– 传递CubeMap因子的时候,不个别设置Shader,用SetGlobalTexture函数来实现场景manager,这样来管理的话,对性能测定会有帮助的。

https://docs.unity3d.com/ScriptReference/Shader.SetGlobalTexture.html

– Paraboloid mapping技法虽然能看到更快的运行技法,但是不是Unity内置的。

– 在View space中,使用了用旋转矩阵来实现的简单的反射mapping投影的Matcap reflection技法。

– 理论上要快3倍以上。

– 适合低配机型,关键取决于美术对物理的认可程度。

– 完全是反射模型的情况,视觉对于方向可能觉得有突兀。反之,粗糙反射模型的情况,效率还是很高的。

2. 设置

PropPBRSimple shader

1. 这个Shader稍微修改了Unity的基本PBR Shader模型。

2. 比Standard shader稍微效率好点。使用 halfview 。 (站在开发者立场上来看,能感受到高光方向差异,但是在玩家立场上很难区分出来。)

3. 强制使用brdf2 와 brdf3 。

这个用LOD来分类。

LOD 0使用brdf2,LOD 1使用brdf3 。

LOD 0为了表现出更加精巧的表现,使用paramtex(质感表现控制用),LOD1使用常量(Constant value),虽然LOD 1使用mipmap,即使已经比LOD 0处理得要快了,但是为了能更快处理,请使用0至1的常数。

美术自己判断,质感表现跟距离无关,对于那些要保留质感的重要事物,不设置LOD,或者即使设置Mesh LOD,在Mesh LOD各物件上设置同样的LOD 0 Shader就可以了。

Unity PBR BRDF的基础知识。

所谓BRDF?

05_Reflectance_Handout.pdf에 액세스하려면 클릭하세요.

双向反射率分布函数 ( Bidirection reflection distribution function )

简单的说,就是关于 ωi (光入射方向向量的逆向量),作为入射光的能量,辐射照度(irradiance)和ωr(View vector)对反射光能量的辐射率的影响的函数。

https://en.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function

PBR定义BRDF,通过Shader实现来获取真实的基于物理的正确的光的反射。还有BRDF就是我们昨天所知道的 Lambertian reflectance model和Blinn Phong reflectance model的一般化函数。

BRDF满足两大物理法则。

1. 能量守恒 : 入射光等于反射光。

2. 相互作用 : 入射和反射可以互换。 ( Helmholtz reciprocity )

Unity提供三种BRDF计算式。

用UNITY_BRDF_PBS来定义,总共分为三个运算式。

1. BRDF1_Unity_PBS : Disney的Torrance-Sparrow反射模型。

http://www.cs.columbia.edu/~belhumeur/courses/appearance/4-Torrance-Sparrow%20%20+%20Oren-Nayar%20Models.ppt

WLT04a.pdf에 액세스하려면 클릭하세요.

2. BRDF2_Unity_PBS : 简化的Cook-Torrance反射模型。主要用于有代表性的游戏杀戮地带和UE4引擎中。

http://ruh.li/GraphicsCookTorrance.html

3. BRDF3_Unity_PBS : 参考正规化 Blinn-Phong 反射浓度函数(reflection density function,RDF)和预计算结果贴图(Lookup texture)。

我们使用它的时候,要按照我们游戏的性能和效果来修改定义。

参考如下:

在PanguPBSLighting.cginc中定义了。

因为是如上定义的,我们写Shader的时候,根据把Shader model定义为几,可以使用BRDF模型

但是,因为我们是手游,原则上是不使用 BRDF1_Unity_PBS的。

使用方式如下:

#pragma target 3.0 ,根据 SHADER_TARGET<31,使用BRDF3_Unity_PBS。

#pragma target 3.5 ,根据SHADER_TARGET>34,使用BRDF2_Unity_PBS。

使用如上定义,把场景中使用的PBR的运算精度分了类,进行了LOD处理和优化。

在PC画面上比较。

在手机实际画面上,LOD1的Specular要更亮点。

(根据画面大小和内部运算精度,高光一般都要比PC画面来得亮,其它Shader也是同样,特别是,不进行 MSAA等抗锯齿处理的时候,更加明显。)

导体和非导体表现测试。

Substance WorkFlow for Bumped Specular Shader.

在Substace Painter里面,大部分都是基于PBR来制作的,使用Bumped Specular Shader,要完美得再现PBR感觉是不可能的。

为啥,因为在PBR Shader中,使用了能表现出导体特性的光照模型,但是,Bumped Specular shader只是用了一般的立体表现光照公式。不管怎样,还是要尽可能找到一个能认可的程度。

首先,Substace painter的贴图导出这一过程很重要。

请看下图:

虽然,还不是很完善,先按照上面的设置来吧。

重申一遍,像黄金这样的特殊物质表现,用PBR可以达到很好的结果,用Blinn-Phong是不可能的。

所以,要适当的使用Diffuse Color和Specular Color 来增加导体特性,即最终在材质球设置中去收尾。

Specular 和 Glossiness 部分是很重要的,建议美术可以通过几种方式来树立内部基准。

存在可以原封不动使用PBR texture导出值的情况。

Fast viewspace(Camera Space) dependency

快速查看空间(相机空间)依赖着色器。

上述Shading是基于Lambert光照模型,简略构成了 Fast viewspace(Camera Space) dependency。

2005年应用于 ZBrush的光照模型。

2011年开始用于手游环境,JP加以扩展的Shader model被Netmarble和很多韩国手游公司使用。

预先生成Parabolide reflection map,(1998) View Space坐标系开始计算反射模型的方式。

1. 优点是相比快速渲染处理,能够模拟受很多灯光处理。

2. 缺点是灯光效果是重属于摄像机,即视图的,阴影是不随画面而选旋转的。即,缺点就是无法控制现实的灯光的方向。

虽然是弥补这一点的渲染技法 Dual Parabolic,但是在速度层面比Sphrer Cube Map略快。-有必要测试一下,Dual Parabolic,两张截取的图片接缝部分不能完全缝合,或者因为看起来有缝隙,所以需要在Shader里面再进行处理,美术控制起来确实有不便。

https://www.leegoonz.com/single-post/2013/10/31/HOW-TO-MAKE-PARABOLIC-AMBIENT-TEXTURE-SPHERE-MESH

虽然,PC可以实时截取做弥补处理,但是在手游环境中运算还是有负担的。

3. 如上情况,光照计算以Lambert方式处理,完全可以从光照计算中去除。

4. 完全从光照计算中去除的情况,无法应用光照探针和光照贴图。

如果要应用光照探针和光照贴图的话,可以直接移植Unity内部代码。

LightProbe可以直接用下面的代码来访问。

Inline DecordLightProbe(float3 NormalDir)

{

return ShadeSH9(float4(NormalDir,1))

}

以此为基础,可以用多种方式来扩展Shader。

Lambert光照模型已经连入光照探针,包括光照贴图。

如果希望在反射探针里面获取到环境反射部分,可以添加代码来获取。

如果需要直接连接ReflectionProbe信息的话,需要如下代码。

Inline DecodeReflectionProbe(float3 viewReflection , float mipmap)

{

float4 skydata = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, viewReflection, mipmap);

return DecodeHDR (skydata , unity_SpecCube0_HDR);

}

但是,如果这样连接所有Unity的GI功能的话, Lambert光照模型相比于BlinnPhong,高光部分计算是有优势的。

以上Shader的缺点是无法表现出Lambert光照模型高光,可以在viewspace(Camera Space) dependency Lookup texture中表现出类似效果。

如上图所示,使用提前保存的高光,在Lambert光照模型中表现出了高光和略微的反射。

为了再加强现代的感觉,可以添加 ClearCoat效果。

ClearCoat就是在陶瓷表面涂层,或者给汽车抛光,能表现出这种感觉,光泽和切线法线不会发生反应,值只需反应于Albedo上就能表现出类似的感觉。

简单增加下Fresnel效果。

制作了如上所示贴图。

白色部分是根据菲涅耳效果变亮的部分。

简单的Shader构成树。

简单的不需要在Shader里面增加复杂的计算式,就增加了菲涅耳效果。

和Light Direction并无计算,可以按照美术的意图制作菲涅耳效果。

如上所示制作Fresnel Lut。

Creation of the Advanced Light Lookup texture

Using Octane Render.

Those result from as adjusted some node graph at above image

I had wonder about comparing direction light map mode between direction specular light map mode.

Directional Specular Light map mode.

Stylized Material Production / 程序风格化材质制作

Stylized Material Production
程序风格化材质制作

Stylize风格化
[stáilaiz]
程式化
사전적인 정의: 어떤 특정한 양식의 틀에 일치 시키다.
词语定义:符合某种特定一致框架的样式。

一步一步来了解一下。Stylize是究竟是什么意思呢?Stylized纯粹的词典定义是符合某种特定一致框架的样式的意思。

Stylized art
[stáilaizd]
程式化艺术
what is the meaning of stylized art?

那么Stylized art的定义是什么呢?在美术层面什么是Stylized texture ,Stylized game art 到底是什么我们究竟知道吗?我和大家有必要需要一些时间对几个意义进行分析。这样可以进行充分的理解。

程式化艺术’是长久以来使用的技巧。1979 年 ‘The Great Soviet Dictionary’定义说 “包括线与形态的简化和空间和颜色的关系在内,使用多种多样的传统方法,将其视为人物和物体装饰的普通化。”表现的卓越风格。
与这种模式相反的‘现实主义艺术’的样式从接近现实的模式出发。(现在在艺术方面这一词是进化的,反而有微妙的差异。)为了使主题表现出接近现实的想法,艺术家可以使用‘程式化艺术创造主题及样式的形象。把重点放在颜色,线及作品的感人的特性上。
今天要一起了解的游戏美术中的程式化艺术(Chéngshì huà yìshù),为了能从根本上了解,首先要先从一般化的定义开始说。
程式化艺术’是用很长时间使用的技巧。1979 年 ‘The Great Soviet Dictionary’定义说 “包括线与形态的简化和空间和颜色的关系在内,使用多种多样的传统方法,将其视为人物和物体装饰的普通化。”表现的卓越风格。与这种模式相反的‘现实主义艺术’的样式从接近现实的模式出发。(现在在艺术方面这一词是进化的,反而有微妙的差异。)为了使主题表现出接近现实的想法,艺术家可以使用‘程式化艺术创造主题及样式的形象。把重点放在颜色,线及作品的感人的特性上。

程式化艺术’ 可以说从很远也可以看到的古代洞穴壁画的简单的视觉表现中开始的。

虽然长时间艺术的方向在不断进化,但是随着时间的流逝,很多艺术家们都以写实表现了起来,直到文艺复兴时代和19世纪初期,写实的表现得到了更多的探究 。

但是19世纪初,中期的‘印象派艺术’ 以学问衍生出来后 ,比起严格的现实主义表现,在颜色和光上的多种实验和研究,转移到有什么故事情节(Storytelling) ,能识别主题的描写和形状与接近性的方式的视觉艺术上。

程式化艺术’ 可以说从很远也可以看到的古代洞穴壁画的简单的视觉表现中开始的。

虽然长时间艺术的方向在不断进化,但是随着时间的流逝,很多艺术家们都以写实表现了起来,直到文艺复兴时代和19世纪初期,写实的表现得到了更多的探究 。

但是19世纪初,中期的‘印象派艺术 以学问衍生出来后 ,比起严格的现实主义表现,在颜色和光上的多种实验和研究,转移到有什么故事情节(Storytelling) ,能识别主题的描写和形状与接近性的方式的视觉艺术上。

有一个必须要记住的东西。

古代洞穴壁的简单化壁画,在很远的地方也能看到吧?请记住。

Journey可以说是一个具有代表性的游戏。

下一个是最近在韩国制作的游戏介绍视频。以整体的颜色和形态为主,几乎都是设计表现的游戏,现在在韩国游戏开发人员中也受到了艺术性的评价。

好的游戏艺术分析。
[Tracking of a good game arts]

虽然美术效果有限,但要看一看几种这种风格的成功的游戏美术。

叫做Shardbound的游戏角色

虽然整体设计简单化,但是把块状和形态表现得很好。

当然,质感忠实于表现事物的属性,鞋子或衣服等的缝纫线等小描写就果断省略了。

这个怪物的整体形态也很果断,表现不精致,但粗糙的简单化也表现出了特色。

这是Over watch 。
虽然是PC游戏,但也可以看出适当的表现。

表面质感的表现虽然不太精致,但是整体设计要素和节制性的描写很好,整体的完成度也是最上等的。

用Substance designer模仿类似Over Watch风格的例子。

为了做这样的模仿,有必要了解Over Watch美术组所理解的表面质感规格和块状感。

这种石头表现出了很明确的角度,能展现出不同的表现,让人不感到混乱。

因为明确的折断,明亮的一面和黑暗的一面很明显,所以会很清楚地维持块状感。

另外,在游戏背景下主人公和配角的背景主题表现程度一定要不同。所有事物都不能成为主角。

稍微大一点的岩石。也很好地遵守了差不多的制作规格。

手机游戏比现在看的例子来说,表现密度更大一点会更好。

这对美术师来说,这是一件很难的事情。

一整天,美术师都在24英寸或27英寸屏幕的画面上进行模型或贴图的工作,一般没有经过训练的美术师,无法轻易决定在何时停止细节深入。

在手机屏幕上看不到的描写范围太多了。我想在下一个画面中聊一聊。

Shardbound游戏中的物件也可以看出,更重视的颜色和线条还有倒角,裂缝的表现也很简单,但表现却很明确。

这种石堆的大部分场面都是在场面中以帮助场景的角色为中心,所以要忠于表现,做的太详细是浪费的事情。特别是手机游戏的情况更是如此。

如果不是完整的真实风格的图片的话,面的造型特别是与光会有反应。表面的颜色没有精致质感比较好。

只不过是色调要丰富,这是不可忘记的。

参考图片中要注意的部分是在大的体面部分做的折断,大的面完全转折了5 0度以上,有各种各样的折面。

对于事物的固有特性,并不是精致的纹理,而是磨损的特性。

特别是在使用与PBR效果相似的游戏中,根据游戏内的照明和相机的变化,玩家可以更清楚地看出更明显的照明,阴影的变化。

另外,在小画面中能看到的到High-light ,是因为在微小的领域中,视觉上Pixel artifact的展现,也是因为有很多人最后在游戏中,不使用MSAA等的Anti-aliasing。

Specular anti-aliasing虽然也有其它处理技术,但由于需要从性能方面耗费,所以美术师从一开始就要考虑到智能手机的环境,而且曲面也需要明确和果断的表现

像怪物或角色的服装,盔甲等饰品也要大胆一点,在游戏中以照明的方式识别事物的形态,

根据摄像机和光与事物的角度,更丰富的表现出阴影的变化程度。

这个给大家展示了一般水平的质感。这是以简单为主题制作的。

增加表面质感的表现与大胆省略后的版本的质感表现。

比起用小图片看,在智能手机上看起来会更小。

我在几年间做手机游戏的期间,在网易上也参与过细节比较高的次世代游戏,最近推出的游戏就从开始就有看到。

但是最早的测试版本的图像,大部分开发公司都无法维持。

大部分都是因为贴图量大而发生的内存,框架现象和发热等多种多样的问题,实际上还没有得到解决。

Heterogeneous Computing即使在智能手机上都适用,但短期,贴图尺寸也要减少初期版本的一半或以上。

这将把大家投入大量精力所做出来的Normal Map和细密质感都抹掉,甚至表现光泽的辅助贴图的一部分会看起来像杂点。

我简单地比较了两个版本。

也比较了制作时间。

比较除了模型之外的所需时间。

虽然不能把所有的事情都简约地进行制作,但在NPC持有的物件画面上,根据画面大小的大小,可以决定这种表现手法。

这种单纯表现的话,Albedo 可以放 Vertex RGB上 ,Metallic 可以放 Vertex Alpha 中保存。

大部分表现都在法线贴图上完成。

举个例子,这个得表现的再大一些。

原画在设计的时候也需考虑,在手机游戏画面中,如何表现出我的设计。

是的…电脑显示器和智能手机的比例差不多有6倍的差异,在面积上会有很大的差异。

不仅如此,事实上人从物理上认识到的画面物理分辨率,只是一个小小的画面。再加上大家制作的物品也不是充满在整个画面。

所以真的要好好想一想才行。

韩国的代表性高质量手机游戏中的Blade 2也是事物的形态都被夸大,表面细节也能在智能手机上保持着适当表现上思索了很多。

从电脑屏幕上,到智能手机画面显示的时候,什么程度的细节可以从转折显示照明效果。这样的观察是非常重要的。

式化艺术不能只说是计算机图像中光的概念。

一次说的时间化的变迁,到现代的时候,程式化艺术要装入在里面闪耀的感情和故事。

不能只看Global illumination或Spherical Harmonic技术层上的一面。

Stylized Texturing Substance Designer tutorial since by 2013 in years.

This post is an example of the process of combining Substance designer and Unity shading while learning the Substance designer for the first time in 2013.

During the month of March, I would like to share code, node sharing, etc. while writing a basic manual for each node, previewing characters using a light capture shader, and a tutorial (intermediate and advanced) that integrates a lightcapture shader with dynamic texture and parameter nodes.

Let’s try posting as much as possible rather than holding it too big.

Node-specific manuals will be written at the level of the example below.

Filter = node = library?

Nodes are, in simple terms, subgraphs of each subsegment, each functioning in their own segment.

You can find them all in the Library view, which is placed by default in the bottom left of the Substance Designer screen.

It is easily written as a node, but it is a library of substances used for that node.

It would be better not to write top-down, but to talk about the most accessible library and to reinforce it by discussing opinions about simple application approaches.

Substance Online Manual stipulates that each function in the library is a filter.
However, in the library, individual functions, meshes, sbs, and sbsr are all included in addition to filters.

Substances (graphs from .sbs package and .sbsar) Bitmap images Vector images Functions Meshes

The above list is a detailed category that can be accessed within the library.
It is good to organize the filters for each filter while making an easy-to-understand Case by sample graph.
The easiest way to understand substance is that the graph concept is almost the same as non-linear editing software such as kanata, fusion, etc., used in movie shorts.

3월 작성 해 볼 라이트켑와 서브스턴스 디자이너 연동의 예상 결과물입니다. 도타2 의 영웅 모드 데이터를 이용 하여 설명 합니다.
연구목표는 아래와 같습니다.

Color Adjust function target list.
HSV 변조 매개변수(Expose a parameter)
Contrast 매개변수

Dynamic texture generating for ligthCap(Matcap bake texture based) shader.

GLSL shader code writing for Substance designer 3D View. 최종 통합 되어 구현 될 유니티에서 사용 할 ” Lightcap shader ” 와 거의 같은 쉐이딩을 서브스턴스에서도 직접 구현 해 봅니다.

간략한 이해를 돕기 위해 서브스턴스의 ” 내부 쉐이더 라이브러리의 구조를 참조 ” 합니다.

위 모든 서브스턴스의 필터를 통한 매개변수는 쉐이더 안에서도 작성 할 수 있습니다. 연구의 목표는 연속적인 프로세싱을 최소화 하는 Look up texture table 을 미리 작성 된 서브스턴스의 매개변수로 부터 다이나믹 텍스처 과정을 거처 static computing 화 하는 것에 있다고 볼 수 있습니다.

Substance power user 에게는 불필요 한 내용 일 수 있지만 앞으로 Substance power user 가 되실 분들을 위해 손풀기로 노드를 살짝? 다루어 보면서…

아~~~ 대략 이런 것이구나 하는 내용 잠깐 보고 갈께요?! 느낌 아니까~~~

보통 프로그래밍을 하다 보면 API 를 사용 하는 경우가 많습니다. 다만 그 API 를 어떤 식으로 어디에 사용 하는지에 따라 다른 경우 또는 같은 경우 이지만 확장성이 편리 하거나 유지보수를 더 쉽게 할 수 있거나 하는 식으로 프로그래밍 구조가 짜여 지게 되겠죠. 역시 이것은 사람의 성향에 따라 달라지게 되는 것입니다.

Substance 의 normal output node 를 보죠.

아래 그림처럼 회색 이미지를 통해 내부적으로 작성 되어 있는 Normal mapper 필터를 경유 함으로서 자동 변환 됩니다.

하지만 무언가 우리가 생각 하고 있는 것과는 매치가 잘 되지 않는군요?! 단순히 Normal mapper 내부적으로 갖고 있는 파라메터에는 한계가 있어 보이는 군요.!

이제 한단계 더 나아가 볼까요?

좌측 하단의 라이브러리 창에서 검색어 입력 라인에 normal 이라고 검색어를 넣습니다. 아직 숙련 되지 않았다면 필더를 찾는것은 꽤나 어렵게 느껴 질 겁니다. 하지만 Substance designer 의 개발자들이 이름을 잘 지어 놨군요! ^^

검색 된 리스트 에서 Normal Color 를 선태 하고 Graph 창에 드레그 해서 가져 옵니다.

위에서 사용 된 입력 페턴의 가장 검정색 부분에 대해서 생각 해 보죠.

가장 깊은 곳입니다.

이것을 Height 맵으로 이해 한다면 좋겠네요.

노말텍스처는 세개의 컬러 체널을 사용 하여 좌표를 기록 하고 있습니다. R = 좌우 G = 상하 B = 깊이

대략 이렇게 이해 했다면…. Normal mapper 로 생성 된 노말맵의 Z 값을 변조 해야 합니다. Normal Color 내부 파라메터인 Slope 을 이용 하여 Blue 값에 가깝도록 변형 해 보세요.

결과를 위해 Blender 를 사용 하여 합쳐줍니다.

주의 할 것은 여기서는 Normal Combine 을 사용 하지 않습니다.

위의 예제에서 처럼 프로시주얼 페턴의 블랙 부분은 알파 영역으로 취급 되어 Normal Mapper 노드를 경유 하면서 완전히 오퍼시티 영역으로 규정이 되는 것이고 내부적으로 투명 영역은 화이트 처럼 인식 해 버릴지도 모릅니다. 그렇기 때문에 Blender 를 통해서 빈 공간을 채워 줄 노말 칼라를 만들어 주어야 하는 것이고 결과를 위해서 두 소스를 그대로 합쳐 주기만 할 것이기 때문이죠.

이제 어느정도 예상 했던 결과물이 되었죠? Slope 각도에 따라 적당히 제어를 하면서 원하는 결과를 만들어 낼 수 있겠습니다.

결론. 서브스턴스는 하나의 길을 제공 하는 것이 아니라 노드의 결합을 이용 한 무한한 확장이 가능 한 차세대 비선형 동적 텍스처 생성 프로그램 이라고 이해 하시면 좋겠습니다. 수많은 노드 연결 학습으로 여러가지 결과물을 만들어 보는 것이 가장 좋은 학습이겠죠!

서브스턴스 디자이너에서는 몇가지 노말맵 생성기능을 제공 하고 있습니다.

PART-2 에서는 보편적인 Normal Mapper 필터를 잠깐 살펴 봤는데요. PART-3 에서는 Height Normal Blender 필터를 경유 하여 노말맵을 생성 하였습니다.

PART-3 의 목표는 서브스턴스에서 기본적으로 제공 하는 렌더링 엔진의 노말맵 연산에 사용 되는 Binormal 과 UV Space 간의 계산을 위한 노말맵의 녹색 체널에 대한 이야기를 살짝 다루면서 서브스턴스의 맥락과 이해에 좀 더 접근 해 보는 것에 두었습니다.

쉽게 말하면 원래 서브스턴스에서 내부적으로 제공 하는 DirectX 모드 와 OpenGL 모드에 따른 Y 체널 Invert Function 을 사용자 노드로 다시 구현 해 보는 것이죠.

이번 구현에서는 쉬운 이해를 위한 방법을 선택 하고 있고 좀 더 어려운 매개변수 개방을 위한 내부 함수(Internal built-in Function)로 변수 개방을 하지 않습니다.

이해를 위해서 간단한 노드를 구성 했어요.

그냥 딱 봐도 정상적으로 출력 되고 있어 보입니다.

테스트를 위해서 일단 렌더링 엔진을 확인 해 봐야겠습니다.

예문 작성을 맥에서 하고 있기 때문에 OpenGL GLSL 로 설정을 하였습니다.

SSE2 는 인텔 CPU 에서 제공 하는 가속 렌더링 아zl텍처 입니다. 서브스턴스는 내부적으로 Bool 타입을 green channel inverting function 을 제공 하고 있습니다.

Normal Format 을 보면 다이렉트 엑스와 오픈지엘을 선택 해 줄 수 있어요.

하지만 어차피 개념적인 이해를 위한 예제 이기 때문에 이것은 생각 하지 않겠습니다.

우리는 노멀맵이 세개의 체널로 부터 작성 되고 있다는 것을 알고 있습니다. 즉 녹색 체널을 반전 시켜야 하죠.

노드를 좀 더 활용 해 보기 위해서 channel split 으로 RGBA 체널을 분리 시키고 다른 필터를 경유 시킨 후에 Channel merge 하여 병합 후 Output 노드로 연결 하겠습니다. 무엇을 해야 할까요? 녹색 체널을 반전 하기 위해서 분리 노드와 병합 노드 사이의 G체널에 Invert 와 관련 된 필터를 연결 해 보는 것이죠.

역시 좌측하든의 라이브러리 창에서 invert 로 검색 해 보는 겁니다.

우리는 회색 체널을 반전 해야 하기 때문에 Invert Grayscale 필터를 사용 할 것입니다.

Invert Grayscale 필터를 연결 해 보니 노말맵의 위아래가 바뀐 결과를 볼 수 있어요.

왜 이런 체널 인버트를 해야 하는지는 Binormal 연산이 탄젠트 공간과 UV 공간 좌표계의 계산에 의해 이루어 진다는 겁니다.

다이렉트 엑스는 좌하단이 0.0 이고 오픈지엘은 좌상단이 0.0 의 UV 공간 좌표계를 사용 합니다.

쉽게 생각 하면 위아래가 뒤집혀 있다는 거죠.

렌더링 아키텍처에 따른 올바른 법선맵핑 연산을 위해서 가볍게 … 뭐 아.. 그렇군. 정도로 이해 하시면 될거 같습니다.

보통 쉐이더 작성시에는 Y 값에 대해서 마이너스 값으로 치환 대입 하여 처리 하고는 하죠.

하지만 쉐이더에서는 지속적인 계산을 수행 해야 하기 때문에 중간 작업용 UberShader 등에서는 이런 Bool 타입 파라메터가 있고 if 문 등으로 처리 할 수 있지만 연산은 그다지 달갑지 않은 탓에 다이나믹 텍스처를 통해 초기 한번만 처리 하고 결과를 리턴 할 수 있도록 하는 것이 좋을 듯 합니다.

가볍게 노드란 이런 것이구나 살펴 봤다면 이제 substance designer 의 filter 와 custom shader 를 추가 하면서 간단한 mobile shading 을 해 볼 것입니다.

first step 으로 저는 Lookup texture based Rimlighting texture 를 만들어 볼겁니다.

이것은 포토샵에서 간단하게 만들 수도 있지만 좀 더 substance designer 의 filter 와 Dynamic texture 의 강력한 기능 그리고 매개변수를 노출 하면서 Unity 에서 어떻게 B2M 과 같이 사용 할 수 있는 것인가를 이해 하기 위해 다양한 방법으로 접근 해 볼 것입니다.

먼저 제작 목표가 될 Lookup texture based Rim lighting texture 입니다

위에서 보여지는 텍스처는 링크에서 보시는 것과 같은 Rim Lighting 의 Pre calculated lookup texture 로 사용 됩니다.

lookup texture 를 사용 하는 이유는 모바일 그래픽의 경우 Pow. function ( 거듭제곱 ) 을 자주 사용 하는 것은 섬세하지만 연산에 있어서 Cost 가 무척 높기 때문이죠.

참고로 sqrt 는 제곱근 입니다.사실 Pow function 의 내부를 들여다보면 꽤나 복잡한? 연산을 수행 하고 있습니다.

Pow function 에서의 옵티마이징에는 여러 방법이 있습니다. Pow function 알고리즘을 직접 구현 할 때 int 타입으로 만들고 거듭제곱의 테이블을 미리 정의 하는 것이죠. 다만 우리가 사용하는 쉐이더 컴파일러에서 제공 하는 Pow function 은 none int 타입입니다. 보통 Specular exponent 등을 구현 할 때 사용 하죠. 이것은 여러 방법으로 최적화 할 수 있지만 대략 그런 것이 있구나 정도만 언급 하도록 하죠.

아무튼…

위와 같은 이미지를 얻어 내기 위해서 substance designer 의 어떤 기능을 활용 할 것인지… 그것이 중요 하겠죠.

하지만 여기서 가장 중요 한 부분은 SVG 가 아닙니다.

어떻게 하얀색 튜브를 만들어 낼 것이냐 이죠.

이제 라이브러리를 좀 살펴 볼께요.

흐음…

검색 하다 보니 맘에 드는 것이 나타 납니다.

바로 이것 입니다.

해당 필터에는 여러가지 옵션이 붙어 있습니다.

substance designer 의 개발자들이 이 필터를 그냥 만들진 않았을 것이라는 희망을 갖고 시작 하게 되었죠.
사실 이런 방식으로 제작 하지 않아도 됩니다. 기초 예제 라서 다른 여러가지 노드를 사용 하는 방식으로 제작 해 봤습니다.

Splatter Circular 를 Graph 에 Drag 해서 하나 만들어 보면 Number 라는 파라메터가 있습니다.

기타 Radius 에 대한 옵션도 있죠.

포인트는 Number 에 있습니다.

시각적으로 크게 어색하지 않는 수 만큼을Type 입력 하면 위와 같이 Circle tube 가 만들어 집니다.

좋습니다.

이제 가운데 부분을 부드럽게 Blur 링 파라메터를 추가 하고 싶으실 겁니다.

그리고 이것은 Dynamic Texture 형태로 유지 되어야 하고요.

단순히 Bitmap 으로 뽑아서 사용 하려고 이렇게 어렵게 substance designer 를 사용 하는 것은 아닐겁니다. 🙂

SVG node 를 하나 만들고 Shape tool 을 사용 하여 그림처럼 Black circle 을 만들어 줍니다.

이건 뭐에 쓸까요?

네… 저는 첨에 만들었던 Splatter Circle 의 중심에 SVG 로 만든 노드를 Blending 할 생각 입니다.

Rim light texture 를 만들때 안쪽은 Blur 를 줘서 부드럽게 사라지는 느낌을 주기 위해서 Blur HQ 노드를 중간에 연결 했습니다.

그리고 이것을 Blending node 에서 알맞게 사용 할 수 있도록 Grayscale 로 바꾸어 줬죠.

중요한 포인트 인데… SVG 를 바로 Blur HQ 노드로 연결 후 Blending 의 Foreground Layer 로 사용 하면 원하는 결과를 얻을 수 없습니다.

SVG 의 투명 속성에 대해서 Flatten Alpha 를 True 로 변경 해 줘야 하기 때문이죠. 이것은 Blending node 의 특성과도 관계가 있는 것이겠군요.

결과를 위해서 연결 된 Node 입니다.

Blend 노드의 확대 이미지 입니다.

Multiply 옵션을 사용 하여 Splatter circle 과 SVG 가 합성 된 것을 알 수 있을 겁니다.

위 그림은 이번 장에서 작성 한 Graph 를 Instance 화 하여 다른 Graph 에서 제활용 하는 구조입니다. 서브스턴스를 잘 활용 하려면 instance 에 대한 개념을 잘 알아 두는 것이 좋습니다. 다음 파트 에서는 Blur HQ 값 , 또는 Blending 값을 활용 하여 매개변수로 노출 시키고 Rim light texture 를 Instance 를 사용하고 변수노출을 하여 유니티 에서 Dynamic 하게 활용 해 보는 것을 살펴 보겠습니다.

작업을 위해 신규 도큐먼트를 만들어 보세요. 이때 Template 에서 세 번째 있는 Physically Based(metallic/roughness) 를 선택 해 봅니다. 아직 우리는 커스텀 쉐이더를 만들어 추가 한 상태가 아닙니다.

When you are creating new substance project and you would be selecting 3th category of template. So, currently We did not added to custom shader for this project.

Dota2 mode 에서 추출 한 Bounty Hunter 3d model 을 drag 하여 unsaved package 에 올려 놓으면 위 그림처럼 자동으로 Resources folder 가 만들어 지고 mesh data 가 등록 됩니다.

Dragging extracted Bounty Hunter 3d modeling data and then while you hovering mouse cursor to your unsaved package or your custom sbs project. You will be see automatic creation resource folder like above images.

등록 된 상태에서 모델 아이콘을 더블클릭 하면 3D View 창에 자동으로 해당 모델이 나타나게 되죠. 이때 외부에서 모델을 수정 하고 저장 하면 substance designer 에서는 자동으로 업데이트 시켜 줍니다.

If your 3d modeling data to finished registration and then next you can just double clicking your 3d modeling that just done. ta da~! While you guys if edit your 3d modeling via external DCC tool and directly step you will see automatic updating substance designer.

미리 만들어져 있는 기본 텍스처 페스들을 리소스 폴더에 드레그 해서 등록 합니다.

만약 여러분이 특정 폴더 안의 리소스 경로를 유지 하고 싶다면 link , 서브스턴스 디자이너 전용 프로젝트 폴더를 따로 관리 하신다면 import 로 설정 하면 되겠습니다. 사실 임베디드 데이터가 되는 것은 아니고 복사가 자동으로 되는 것일 뿐입니다. 위 그림에는 도타2 에서 추출 한 여러 텍스처 페스 소스가 있군요. 이번엔 학습을 위해 모두 사용하지는 않을 겁니다.

참고사항으로 이번 학습예제는 Valve soft 에서 제공하는 도타2 아트웍 매뉴얼과는 상관 없는 비주얼 프로세스를 다루고 있습니다.이제 기본적인 준비는 끝이 되었습니다. 다음으로 요구 되는 텍스처 아웃풋을 만들어 주고 목표 한 작업을 위해 Graph 에서의 노드 작업을 해 보죠. 디퓨즈 , 노말 , AO TEX , MATCAP TEX , RIM TEX. 이렇게 5개의 텍스처 소스가 필요 합니다. 5개의 텍스처 소스 중에서 이미 확보 되어 있는 것은 디퓨즈 , 노말 , MATCAP TEX 이렇게 3개 이고 substance designer 에서 직접 만들어야 하는 것이 나머지 두 개 입니다.

Doing just dragging your pre made pass textures into your current .sbs project root. If you want to preserve your other directory path , have to choose just link mode or other case you want to preserve include currently sbs own project directory you have to choose import mode. So, import mode is just copy into sbs project location that is not embedded data. Include many other kind of pass texture see above images. This chapter is not implementation about Valve softs’ dota2 graphics manuel.So.Now we ready to basically assets for next work. And then We will have to making texture for each working in require output graph node. This element tex is diffuse , normal , AO TEX , MATCAP TEX , RIM TEX that is done. Diffuse , Normal and MATCAP TEX is already that made pre calculating external tool and You will have to making just another texture only.

먼저 AO TEX. 를 만들어 볼텐데요 여기서 이제 substance designer 의 인스턴스 라이브러리를 사용 하게 됩니다.

First time We will making AO TEX that now you will utilize instance library in substance designer.

이렇게 노말맵과 substance designer 의 Normal to Height 과 Ambient Occlusion 이라는 Instance filter 를 활용 하면 간단하게 AO 맵을 만들 수 있는 공정이 됩니다.

So… Normal to Height filter and Ambient Occlusion filter on substance designer by normal map textures at like this technique to simply you make for your AO TEX that is too some easy process I am sure if you has to understanding precess about many other filter to substance designer.

하이폴리곤 디테일 모델링이 없기 때문에 노말맵과 내부 필터를 사용하여 AO TEX 를 만들게 되지만 Zbrush 등에서 하이폴리곤 작업을 했다면 Bake model information 에서 AO TEX 를 만들 수도 있습니다.

In this case just utilized normal map and built in instance filter for making AO TEX because we does’t has original high polygon model source but if we have that source we can utilizing Bake model information on substance designer.

적당한 값을 설정 하여 원하는 AO TEX 를 간편 하게 얻어 낼 수 있습니다.

You would be get some fitting AO TEX texture result for your work.

그림은 기본 노드를 사용 한 AO TEX. 다만 목표 쉐이딩을 위해 한단계 가공을 해 보겠습니다. 중간톤 부분의 디테일이 좀 더 필요 한 이유로 Highpass 를 사용 한 별도의 노드와 블렌딩을 거쳐 사용자화 텍스처로 변환 해 봅시다.

Detail AO 프레임을 보면 Height map 을 사용 하여 Hipass 로 필터링 한 후 블렌딩을 위해 그레이스케일 컨터빙을 한 출력값과 AO TEX 프레임에 있는 Ambient Occulusion 의 출력값을 혼합 , Level 을 통해 적정한 값을 유추 하였습니다.

약간의 값 변화를 주어 1차 결과물을 추출했습니다.

다만 마지막 쉐이딩 에서의 결과물을 확인 하는 시점에서 사용자 AO TEX 의 값은 달라 질 수 있습니다. (AO 의 원론적인 출력 결과로 볼때는 올바르지 않을 수 있습니다 , level 등과 이 전의 노드의 속성을 달리 해 보면서 유추 되어 지는 결과를 자주 보는 것이 substance designer 의 노드 그래프 속성에 대해서 빨리 이해 하는 길입니다.)

Substance designer 에서 node 를 사용하여 작성 된 AO TEX <-(Detailed AO) 를 모델의 Shader node 의 Ambient Occlusion 에 연결 하였습니다.

Normal texture 도 조금 보강을 합니다. 다만 이것은 학습을 위한 것일 뿐입니다.

미리 작성 되어 있는 Normal Texture 와 Diffuse texture 를 이용하여 변환 된 detail normal texture pass 을 Normal Combine node 를 활용 하여 섞어 줍니다.

Detail normal texture는 contrast/luminosity_grayscale 을 경유 하도록 하였는데요 그레이톤으로 변경 된 diffuse texture 를 적당한 normal texture 로 변경 한 뒤에도 handling 할 수 있도록 한 것입니다.

contrast/luminosity_grayscale 역시 Instance 인데요…여기서 매개변수를 노출 시켜 게임상에서 디테일 노말을 제어 할 수도 있겠습니다.

참고사항. Physically Based(metallic/roughness) 를 사용 할 때 metallic 값을 변경 하는 체널은 R 체널 입니다. 위 그림은 이해를 돕기 위한 것인데요. uniform color 의 RGB slider 를 움직여 보면 알 수 있습니다.

즉 metallic power 등을 R 체널에 비트맵으로 삽입 해 주어도 되겠지요?

유니폼 컬러로 일괄적으로 metallic power 가 반영 된 예.

살짝 삼천포로 빠진 것 같긴 하지만 살짝 이해를 돕기 위해서 비트맵을 이용해서 시각적으로 노드를 구성 해 봤습니다. ( 그림을 클릭 하면 엄청 크게 나옵니다. -_-;)

중간에 level node 를 두고 조절 해 보면 Metallic power 가 어떤 식으로 반영 되는지 더 시각적으로 확인 해 볼 수 있죠. 역시 이런 식으로 매개변수를 노출 시켜서 게임에서 활용 하는 것입니다. 대략 이런식으로 PBS 에서는 작업 한다는 정도 인데요. 3월 연구 목표의 범주가 커스텀 쉐이더의 적용과 룩업텍스처의 제작 이니까…

슬쩍 이렇게 넘어가고 다음 쳅터에서는 GLSL 부분도 조금 씩 살펴 보도록 하겠습니다. 회사에서는 자체엔진을 사용 하기 때문에 PC 에서는 cg 와 모바일에서는 gles . 이렇게 코드 짤 때 두개를 다 함께 짜야 해서 정말 피곤이 절정에 이르는데요…

일단 여기까지 꼭지만 일단 ….

이번 쳅터는 오우거 엔진을 좀 보셨던 분은 아…. 대충 비슷 하구나. 란 느낌이 들거 같네요.

파싱 콘테이너는 xml 로 되어 있고 이것은 오우거 쉐이더 파싱 구조와 유사 합니다.

오우거 엔진에서 사용 되는 .pragram 과 거의 비슷 합니다.

오우거 엔진 에서는 .pragram 에서 matrix semantic definition 이나 uniform variable value definition 등을 define 해 줘야 하거든요.

타입은 xml 인데 쉽게 이해 되실 듯 합니다.

xml 문법은 여기를 참조.

일단 matcap 쉐이더 에서는 라이트에 관한 구현부가 전혀 없기 때문에 주석처리.;

주말 출근 시즌이라 이런 저런 일을 처리 하고 좀 더 추가 해 봅니다.

정말 쥐 오줌처럼 조금씩 추가 하게 되네요. 🙁

요즘 회사에서 FumeFX 와 Particle Flow 와 씨름 중이라 머리가 폭팔 할 지경 입니다.

사족이 길었군요… ㅋ; 왠지 내용이 길어 보이고 좋군요. -_-;

좀 귀찮기도 하고 해서 커스텀 쉐이딩 쪽은 구글링을 하지 않고 이리 저리 컴파일 해 보면서 테스트 중이랍니다. 일단은 서브스턴스 메뉴얼에 커스텀 쉐이더 추가 규칙이나 내부 API 문서가 있는 거 같진 않은데 안찾아 봐서 모르겠습니다. 누가 좀 찾으시면 링크 좀…. 찾았어요.. -_-;

서브스턴스 디자이너 내부에서 이미 정해 져 있는 Output 타입을 살펴 보면 ambient 가 있습니다. 결국 matcap 은 반구형의 2D 암비언트 텍스처 맵이라고 볼 수 있기 때문에 ambient 아웃풋 노드를 만들도록 합니다.

그리고 서브스턴스 디자이너를 위한 glslfx 안에서 선언 해야 하는 usage 키워드와 서브스턴스 디자이너 코어 라이브러리와 에디터 라이브러리 내부에서 구현 된 대리자(아마도…)넘겨 주는 지시자 역시 미리 정의 되어 있는 범주 안에서만 커스터마이징이 가능 한 것으로 보입니다.(일단 여기까지는 제가 대략 추정 한 내용 일 뿐입니다.)

좀 더 진행을 해 보기 전에 몇 가지 확인 하고 넘어 갈 생각인데요. 쉐이더나 glslfx 파일을 수정 후 다시 컴파일 하려면 3D View 를 클릭 한 상태에서 Ctrl + R 을 누르면 제컴파일이 됩니다.

만약 잘못 된 라인이 있다면 그림 처럼 로그를 보여 줍니다.

이렇게 말이죠. 암튼… 전체적인 구성 자체가 왠지 저는 오우거 엔진 기반 같다는 생각이… 떠나질 않네요. ^^

glslfx 에서의 usage 는 확실히 미리 코어 라이브러리에서 정의 되어 있는 키워드 같습니다. 만약 아래와 같이..

<sampler name=”ambientHemisphericalMap” usage=”ambient”/>

가 아닌

<sampler name=”ambientHemisphericalMap” usage=”ambientMap”/>

디파인 된 키워드를 사용 하면 인터페이스에는 sampler name 을 표시 하도록 되어 있네요.

솔직히 코드소스 보면 금방 알텐데… ㅜㅜ; 5분도 안걸리는 건데…. 이틀을 삽질을.;;; 암튼…

http://support.allegorithmic.com/documentation/display/SD41/GLSLFX+Shaders?src=contextnavpagetreemode ( GLSLFX 레퍼런스 )

정의 되지 않는 usage 키워드를 사용 하면 sampler 이름을 문자열로 리턴 해서 사용 하도록 되어 있습니다. 마단 실제 usage 키워드가 정해진 대로만 사용 해야 하는 지는 좀 더 실제 쉐이더 코드를 만들어 보면서 살펴 볼게요.

솔직히 이거 쓰면서…. ㅜㅜ; 쉐이더는 아무것도 아닌데…

XML 데이터의 정확한 규칙을 모르겠음. 이건 뭐 … 코어 인테페이스 로직에서 뭔 짓을 해 놨는지 알아야..;;; -_-;

암튼… 몇일동안 계속 삽질 중입니다.

여차 저차 하여 기본적인 디퓨즈 컬러와 암비언트 맵이 적용 되도록 쉐이더 코드를 수정 하였습니다.

차 후에 matcap + 2 lighting 을 해보려고… 3dsmax 2015 의 shaderFX 에서 matcap 을 간단히 구현 해 봤습니다

일단 코드 테스트를 위해서 그림처럼 RGB 텍스처를 만들도록 합니다.

위와 같은 결과를 얻을 수 있도록 쉐이더 코드를 작성 할 것입니다. 카메라를 돌려도 계속 색의 패턴이 프로젝션 맵핑 되는 것 처럼 보인다면 대략 완성 단계에 가깝다고 보시면 됩니다.

matcap.glslfx 코드.

<?xml version="1.0" encoding="UTF-8"?>
<glslfx version="1.0.0" author="allegorithmic.com">


<!-- TECHNIQUES -->
<technique name="Matcap">
<!-- PROPERTIES -->
<property name="blend_enabled" value="true"/>
<property name="blend_func" value="src_alpha,one_minus_src_alpha"/>
<property name="cull_face_enabled" value="true"/>
<property name="cull_face_mode" value="back"/>


<!-- SHADERS -->
<shader type="vertex" filename="matcap/vs.glsl"/>
<shader type="fragment" filename="matcap/fs.glsl"/>


</technique> 


<!-- INPUT VERTEX FORMAT -->
<vertexformat name="iVS_Position" semantic="position"/>
<vertexformat name="iVS_Normal" semantic="normal"/>
<vertexformat name="iVS_UV" semantic="texcoord0"/>
<vertexformat name="iVS_Tangent" semantic="tangent0"/>
<vertexformat name="iVS_Binormal" semantic="binormal0"/>


<!-- SAMPLERS -->
<sampler name="diffuseMap" usage="diffuse"/>
<sampler name="normalMap" usage="normal"/>
<sampler name="matcapMap" usage="emissive"/>
<sampler name="AOMap" usage="ambientocclusion"/>
<sampler name="rimMap" usage="mask"/>




<!-- MATRICES -->
<uniform name="modelViewMatrix" semantic="modelview"/>
<uniform name="worldMatrix" semantic="world"/>
<uniform name="worldViewMatrix" semantic="worldview"/>
<uniform name="projectionMatrix" semantic="projection"/>

<uniform name="worldViewProjMatrix" semantic="worldviewprojection"/>
<uniform name="worldInverseTransposeMatrix" semantic="worldinversetranspose"/>
<uniform name="viewInverseMatrix" semantic="viewinverse"/>


<!-- SCENE PARAMETERS -->
<uniform name="ambientColor" semantic="ambient"/>



<!-- UNIFORMS -->
<uniform name="tiling" guiName="Tiling" default="1" min="1" guiWidget="slider" guiMax="10"/>
<uniform name="heightMapScale" guiGroup="Normal" guiName="Normal Strength" default="1" guiWidget="slider" guiMin="0" guiMax="2" />
<uniform name="matcapPowr" guiGroup="MatCap" guiName="Matcap Strength" default="1" guiWidget="slider" guiMin="0" guiMax="5" />

<uniform name="flipY" guiGroup="Normal" guiName="DirectX Normal" default="true" guiWidget="checkbox" />

</glslfx>






vs.glsl 코드
/////////////////////////////// Vertex shader
#version 120

attribute vec4 iVS_Position;
attribute vec4 iVS_Normal;
attribute vec2 iVS_UV;
attribute vec4 iVS_Tangent;
attribute vec4 iVS_Binormal;

varying vec3 iFS_Normal;
varying vec2 iFS_UV;

varying vec3 TtoV0;
varying vec3 TtoV1;
varying vec3 TtoV2;


varying vec4 iFS_Tangent;
varying vec4 iFS_Binormal;
varying vec4 iFS_PointWS;

uniform mat4 modelViewMatrix;
uniform mat4 worldMatrix;
uniform mat4 worldViewMatrix;
uniform mat4 worldViewProjMatrix;
uniform mat4 worldInverseTransposeMatrix;
uniform mat4 viewInverseMatrix;
uniform mat4 projectionMatrix;


void main()
{
gl_Position = worldViewProjMatrix * iVS_Position;

iFS_UV = iVS_UV;

iFS_Normal = (worldInverseTransposeMatrix * iVS_Normal).xyz;


iFS_Tangent.xyz = (worldInverseTransposeMatrix * iVS_Tangent).xyz;


iFS_Binormal.xyz = (worldInverseTransposeMatrix * iVS_Binormal).xyz;


vec3 biNorm = cross( iFS_Tangent.xyz, iFS_Normal ) * iFS_Tangent.w;

iFS_PointWS.xyz = (worldMatrix * iVS_Position).xyz;

mat3 rotationMat = mat3(iFS_Tangent.xyz , biNorm , iFS_Normal);

// use like unity3d macro based matrix function name
mat3 UNITY_MATRIX_IT_MV = mat3(worldViewMatrix * worldInverseTransposeMatrix ); // inverse transpose matrix for Inverse transpose model view matrix

mat3 TtoV_Mat = UNITY_MATRIX_IT_MV * (rotationMat);

TtoV0 = TtoV_Mat[0];
TtoV1 = TtoV_Mat[1];
TtoV2 = TtoV_Mat[2];


}
//vs shader end

-------------------------------------------------------------------------------
버택스 쉐이더에서 유심히 볼 것은 ...

제가 잘 몰라서 그런지 ... 암튼...
서브스턴스 디자이너의 빌트인 메티릭스 에
Inverse Transpose modelview matix 가 없던 건데요...
보통 어지간한 엔진들은 내부에서 역전치 행렬 공간변환에 대한 것도 정의가 다 되어있는데 말이죠....
아무튼...
mat3 UNITY_MATRIX_IT_MV = mat3(worldViewMatrix * worldInverseTransposeMatrix );
식으로 공간변환을 추가 해 줬습니다.
UNITY_MATRIX_IT_MV 는 유니티 유저이기 때문에 머리에 빨리 들어 오도록 이름을 같게 했습니다.

rotationMat 는 Tangent rotate Transpose 입니다.
유니티 쉐이더랩 에서는 메크로 화 되어 있는데 풀어서 쓰면
mat3 rotationMat = mat3(iFS_Tangent.xyz , biNorm , iFS_Normal);
가 됩니다.

하지만 iFS_Binormal 을 사용 하지 않고 따로 외각을 구하는 cross 함수를 사용 해서
따로 biNorm 을 만들어 주어야 합니다.

vec3 biNorm = cross( iFS_Tangent.xyz, iFS_Normal ) * iFS_Tangent.w;
이렇게 말이죠.

유니티 쉐이더랩에서 메크로화 되어 있는
Transformations 참조.
https://docs.unity3d.com/Documentation/Components/SL-BuiltinValues.html


-------------------------------------------------------------------------------
fs.glsl 코드


//////////////////////////////// Fragment shader
#version 120


varying vec3 TtoV0;
varying vec3 TtoV1;
varying vec3 TtoV2;

varying vec2 iFS_UV;
varying vec3 iFS_Normal;
varying vec3 iFS_Tangent;
varying vec3 iFS_Binormal;
varying vec3 iFS_PointWS;

uniform vec3 Lamp0Pos = vec3(0.0,0.0,70.0);
uniform vec3 Lamp0Color = vec3(1.0,1.0,1.0);
uniform float Lamp0Intensity = 1.0;
uniform vec3 Lamp1Pos = vec3(70.0,0.0,0.0);
uniform vec3 Lamp1Color = vec3(0.198,0.198,0.198);
uniform float Lamp1Intensity = 1.0;

uniform float Ka = 1;
uniform float heightMapScale = 1;
uniform vec3 ambientColor = vec3(0.07,0.07,0.07);
uniform float tiling = 1.0;
uniform bool flipY = true;
uniform float matcapPowr = 1.0;
uniform float RimPowr = 1.0;

uniform sampler2D normalMap;
uniform sampler2D diffuseMap;
uniform sampler2D matcapMap;
uniform sampler2D AOMap;
uniform sampler2D rimMap;

uniform mat4 viewInverseMatrix;


vec3 fixNormalSample(vec3 v)
{
vec3 result = v - vec3(0.5,0.5,0.5);
result.y = flipY ? -result.y : result.y;
return result;
}


vec3 srgb_to_linear(vec3 c)
{
return pow(c,vec3(2.2,2.2,2.2));
}

vec3 linear_to_srgb(vec3 c)
{
return pow(c,vec3(0.4545,0.4545,0.4545));
}

void main()
{

vec3 cameraPosWS = viewInverseMatrix[3].xyz;
vec3 pointToCameraDirWS = normalize(cameraPosWS - iFS_PointWS);
vec3 pointToLight0DirWS = normalize(Lamp0Pos - iFS_PointWS);
vec3 pointToLight1DirWS = normalize(Lamp1Pos - iFS_PointWS);
vec3 normalWS = iFS_Normal;
vec3 tangentWS = iFS_Tangent;
vec3 binormalWS = iFS_Binormal;

// ------------------------------------------
// Update UV
vec2 uv = iFS_UV * tiling;

// ------------------------------------------
// Add Normal from normalMap
vec3 normalTS = texture2D(normalMap,uv).xyz;

vec3 cumulatedNormalWS = normalWS;

vec3 T = normalize(TtoV0.xyz);
vec3 B = normalize(TtoV1.xyz);
vec3 N = normalize(TtoV2.xyz);

if (length(normalTS)>0.0001)
{
normalTS = fixNormalSample(normalTS);
normalTS *= heightMapScale;
//vec3 normalMapWS = normalTS.x * tangentWS + normalTS.y * binormalWS;
vec3 normalMapWS = normalize(N + normalTS.y * T + normalTS.x * B);
cumulatedNormalWS = cumulatedNormalWS + normalMapWS;
cumulatedNormalWS = normalize(cumulatedNormalWS);
}


// Diffuse
vec3 diffuseColor = srgb_to_linear(texture2D(diffuseMap,uv).rgb);

// Matcap
vec2 abmUV;
abmUV.x = (T.x * normalTS.x + B.x * normalTS.y + N.x * normalTS.z) * 0.5 + 0.5;
abmUV.y = (T.y * normalTS.x + B.y * normalTS.y + N.y * normalTS.z) * -0.5 + 0.5;

//ambientOcclusionColor
vec3 AO_TEX = srgb_to_linear(texture2D(AOMap,uv).rgb);

vec3 Rim_TEX = srgb_to_linear(texture2D(rimMap,abmUV.xy).rgb);
Rim_TEX *=RimPowr;

vec3 ambientMapColor = srgb_to_linear(texture2D( matcapMap , abmUV.xy).rgb) * matcapPowr;




// ------------------------------------------
vec3 finalcolor = (diffuseColor.rgb * AO_TEX.rgb) * (ambientMapColor.rgb * ambientColor) + Rim_TEX.rgb;

// Final Color
gl_FragColor = vec4(linear_to_srgb(finalcolor), 1.0);
}

코드 중 아직 좀 미완성 부분도 있고 (AO_TEX 를 활용 한 감마콘트롤은 빠져있어요.) 대충 pseudo code 는 아래와 같긴 합니다.

한번 넣어 보세요.

그리고 전체 코드 중에… 앞으로 버전 2.0 에서 작업 할 예정인 다이나믹 라이트와의 혼용. 라이트를 나중에 또 추가 할 것이기 때문에 기존의 Lamp0 과 Lamp1 에 대한 변수등은 남겨 둔 상태 입니다.

위 코드를 보시면 RimTex 에 대한 부분이 있습니다. vec3 finalcolor = (diffuseColor.rgb * AO_TEX.rgb) * (ambientMapColor.rgb * ambientColor) + Rim_TEX.rgb; 로 만들었는데요. 가장 마지막에 Rim_TEX.rgb 를 더해 주도록 합니다.

Rim_TEX 는 따로 텍스처를 만들지 않았고 이 전 강좌에서 배웠던 필터를 사용 했고 Transform2D 를 중간에 추가 하여 림라이트의 주방향을 제어 할 수 있도록 하였습니다. 이것은 차 후 유니티 등에서 활용 될 수 있는 연결 변수 일 수 있습니다.

glslfx.xml 의 내용. Matcap 강도와 림텍스처의 강도에 대한 변수가 추가 되었습니다.

림라이트 텍스처의 패턴 컬러에 기본 색조를 만들고 HSL 값을 변경 하여 그림처럼 다양한 컬러가 들어 간 림텍스처를 만들어 보는 것도 좋은 방법입니다.

이런 식이 될 수 있겠죠.

1차로 나온 결과물 입니다. 여러 값을 수정 해 가면서 적당한 쉐이딩을 찾아 가면 될 듯 합니다.

전체 노드 맵.

모바일용 으로 제작 해 본 Matcap 확장 커스텀 쉐이더 적용 이미지.

서브스턴스 디자이너의 두개의 라이팅은 적용이 되지 않습니다.

사전 정의 된 2D 암비언트 텍스처로 베이스 라이팅 느낌을 주었고 림텍스처를 활용 해서

여러개의 간접광이 비추는 것 처럼 느낌을 내 봤습니다. 최대한 가벼운 상태로 적당한 느낌을 낼 수 있는 모바일 쉐이더 기반이라고 생각 하고 만들어 봤는데요… 어떻신가요?

해당 쉐이더는 유니티 엔진에서도 똑같이 적용 되며 대부분의 GLSL 기반에서 차이 없이 적용이 가능 합니다.

한달여 동안 차곡 차곡 서브스턴스 디자이너를 알아 봤습니다.

인터페이스 부터 시작 해서 필터와 활용… 합성 활용. 그리고 커스텀 쉐이더를 제작해서 서브스턴스에 적용 하고 실제 작업 해 보는 실전까지 살펴 봤습니다.

저는 모바일 게임개발을 하고 있기 때문에 물리기반 쉐이더나 그런 것에 관심이 많지만 실제 작업에서는 어떻게 하면 가볍게.. 그럴사 하게 돌아 가게 해 볼까 라는 것에 더 생각이 맞춰져 있습니다.

여기에 서브스턴스 디자이너의 강력한 기능을 접목 해 보는 것이야 말로 중요 하다고 생각 합니다.

HELLO GPU?

Authored by JP。LEE
leegoonz@163.com
TECHNICAL ART DIRECTOR OF THE GIANT INTERACTIVE
07/12/2018

Purpose of this lecture
给90后新人美术简单介绍电脑图形历史,让他们认识到过去和现在,从而培养可以预估未来的洞察力。
这次分享的内容包含对 GPU 的介绍,对近期有变化的游戏图形优化因素的基本原理的介绍,使美术可以根据这些信息主动树立优化思路。

HISTORY OF MODERN GPU 1999
GPU(图形处理器) ARCHITECTURE的变迁
1999年发布了 NVIDIA 的 Ge-force 256 显卡。 这是第3代 gforce
HARDWARE T&L(transform & lighting),行列演算和向量内积, 乘法的累积演算 (Multiply accumulate operation)用 ‘fixed function hardware’ 实现。
 ! 3D PC 游戏的坐标变换开始在 GPU上执行。!
DIRECT X 7
但是很遗憾,这个时代比起 3D 游戏, 2D 游戏的开发才是主流。

HISTORY OF MODERN GPU 2000
CPU的 Emotion Engine 的向量演算在单位上做几何处理。
这是基于 MIPS-III R5900 开发的,由 CPU 核,  FPU, 向量处理单元 2个(VPU0, VPU1), 图形界面 (GIF), 10 通道 DMA, 内存控制器, 图形处理单位 (IPU), I/O 界面组成,各个单位连接到 128 Bit 内部数据总线。

2000年微软发布了用于Windows的图形 API DirectX 8 。
DirectX 8 不是固定功能,是可以定制玩家制作的微软管线的硬件, 是以Programmable shader 结构的 GPU为前提制作的。
2001年发布了支持 DiectX 8的 OS 微软Windows XP 和 GPU 的 Nvidia GPU 的 Geforce 3。
同一年开售的微软主机游戏机 ‘XBOX’ 内含定制 Geforce 3,支持,Programmable shader 。

HISTORY OF MODERN GPU 2001 ~ 2003
4代 GeForce 登场。
GeForce 3 和 GeForce 4 发行。
这是改造 3代 GPU ARCHITECTURE 的模型,所以有着只支持 DIRECTX 7.0 的缺点,是过度用的 GPU。
5代 GeForce 登场。
GeForce FX 发行。
支持 DirectX 9.0a 和 OpenGL 2.1 ,天堂 2 或 孤岛惊魂FARCRY1 或 毁灭战士DOOM3 可以跑起来了。

HISTORY OF MODERN GPU 2004 ~ 2008
2004年 8月开始 5代~6代  GeForce 登场。
发布了 GeForce  FX 和 GeForce 6 ~7 。
GeForce 6 是完整支持 DirectX 9.0c的第一个系列。
开始支持 DirectX 9.0c 后,游戏的品质急剧上升。

2005年发布了 XBOX 360 /  ATI GPU 的 Xenos 是用的 Unified shader 。
Unified shader 是同一个硬件的 Shader process 里处理 vertex shader 和 Pixel shader 两面,根据处理阶段和 game title 的特性,换成符合各个处理负荷的资源分配 ,做 Shader process 的负荷平衡。
2006年发布 PS3 。
用的是 NVIDIA 的 Reality Synthesizer GPU 。

HISTORY OF MODERN GPU 2008 ~ 2017
GeForce 7 代到 12 代。Core 芯片集开始全面发展起来。NVIDIA 以 Kepler Architecture 为开端,步入了高性能手机 GPU 领域。
NINTENDO SWITCH 的 GPU 是 MAXELL CORE 的 NVIDIA TEGRA X1 (T210) 。
以游戏为主运营体系的任天堂SWITCH 在游戏的驱动上可以期待比 A9X 或 A10 Fusion 更好的性能,实际性能比期待还高。 用多平台游戏堡垒之夜做了性能对比后发现超过了 iPhone X 的 A11 Bionic。

HISTORY OF MODERN GPU 2018
HDR 和 4K ,在图形处理有绝对优势的 PLAY-TATION 4 PRO。
影视视觉效果为主流的PC和主机游戏图形模范出现了。
这个时代对光源的精细处理和大气扩散效果, 3D空间的深度和高度的处理的要求很多。

NVIDIA 8th generation TURING GPU
加速Ray tracing (光线追踪) 的新 RT CORE(Ray-tracing core)。
在每秒最大 10Giga Ray 的 3D环境里,可以更快计算光和声音移动的方式。
支持 Real-time ray tracing的 AI(人工智能) 推理使用的 Tensor Core 每秒最多演算 500兆次 Tensor 。
HYBRID RENDERING
在复杂的模型上通过神经网和流体的相互作用实现影视水准的交互。

PROJECT PICA PICA FROM SEED, ELECTRONIC ARTS / SIGGRAPH 2018

MOBILE GRAPHICS PARADIGM

GRAPHICS PROGRAM PARADIGM SHIFT [旧时代]
2009年 11月 28日 发布了可以跑 3D 游戏的 IPHONE 3GS 。
主导手游图像革新的 Epic games 的 Infinity Blade(无尽之剑)
发布后,全世界多数游戏开发公司都开始发布精致的 3D 游戏了。

2011年 Infinity blade(无尽之剑) 的下一个作品 Infinity blade 2 上线了。
IPhone 4S 的 Flag ship game 也可以跑起来了 。

2016年到 2018年现在为止,大部分是过渡期。
天堂2 revolution 和 剑灵 revolution 等游戏开始在整个场景投入实时照明效果。


FIRST NEW GENERATION

GRAPHICS PROGRAM PARADIGM SHIFT [新时代]
2014年在 GOOGLE I/O 发布的 EPIC GAMES.
NVIDIA SHIELD DEMO ( 和 NINTENDO SWITCH 相似的性能 )

2018年唯一一个决定只支持 OpenGL-es 3.1 以上的游戏。

2018年第一个支持 VULKAN 的手游上线了。 BLADE 2

NEXT GENERATION MOBILE HARDWARE
SPECIFICATION

GRAPHICS PROGRAM PARADIGM SHIFT
硬件上先出的合并Shader, Driver 和 API是之后完成的,这形成了一个新时代。 

列表可以看出,不一样的特点是 GPU可以直接更新缓冲。 
现在所有图形相关负荷最大的瓶颈就是 CPU > GPU 的指令。  
这些指令可以分为三个种类。

我们看下…

Draw Call : 中国有时会叫做 DP(DrawPoint??) 之前就在沟通时弄混过。
– CPU要求 GPU 画出某种物体。

Batches

– 很多人跟 Draw call 混用,但其实这是包含Draw call 的更广泛的概念 
– Draw Call + Set VB/IB + Set Transform + Set Pass Call (Set Shader + Set Texture 0~7 + Set Blending + Set Z enable。。)  

Set Pass Call
– 指 Material 和 Shader 相关的 Batch。
不单独进行Batching时, Set Pass Call和 Material的关系
案例1 : 共享 Material的物体有 10个
– Batch是 10个, Set Pass Call用 1个, Batch总共是 11个。 
案例2 : Material或 Shader不同的物体是 10个
– Batch是 10个, Set Pass Call是 10个, Batch总共是 20个。

 Batching : 不要跟Batche弄混了。
– 是把多个Draw call 捆成一个Draw call 处理的工作

所有指令都有庞大的内存/演算 负荷。最终图形优化的核心就是从 cpu到 gpu不做指令,或合起来一次性做好减少次数。
这种优化方法里,有执行同一个程序(Shader)时为了不反复修改程序,根据材质整理好合起来 ( SRP Batcher, 只支持SRP, 2019.1开始 ) 的方法。
为了不进行多次 Draw,同一个材质的顶点和索引收集起来一起画
( Dynamic/Static Batch ), 在同样的状态下只修改参数的话一次性收集数据传过去减少负荷的方法。( GPU Instancing) 到这里为止是旧时优化方法。 
在现代用更简单的方法解决这个问题。 方法 :  因为 GPU可以进行以 CPU 为准的计算了,所以要在 GPU更新。   这样就可以利用演算力强大的 GPU,不仅演算速度变快,持续从 cpu更新到 GPU的缓冲和参数的过程也会变少或变得不需要,可以大量减少 call 开销。
其中一个就是 Indirect Draw。
看成 Instancing Draw 的高效率版本就可以了。
Instancing Draw API 指令基本上是在 CPU 更新数据,只是把 Draw 指令合并起来了,相比之下 indirect draw 是画在 GPU 缓冲时需要的数据,所以是把这个作为参考做 Draw 的指令。  
用 compute Shader 做 curling 做更新和 indirect draw, 用几个 Dis-patch 指令和一个 Draw 指令就能画出数百万个物体。 
( 当然只能在新一代 API里才有的指令。)
https://www。khronos。org/registry/OpenGL-Refpages/es3。1/html/glDrawArraysIndirect。xhtml
用相同模式做出来的就是 VFX-Graph ( 现在是Beta, SRP专用, 预计2019年末上线? ) 。 
更新的程序用 compute Shader做出来,在 GPU完成更新和画。 
基本上所有 演算都放到 GPU里做,Shader的 80%是 Compute Shader 的 SRP HD。资源商城陆续出现了利用这个模式做成的插件。


UNDERSTANDING HARDWARE LIMITATIONS
手机硬件的变化和 bandwidth(带宽)。
Reference by SIGGRAPH 2015 Xroads of Discovery。

我们已经了解了GPU的模式,还有个重要的点。
现代的 GPU已经成长为比 Play-station 3 还高的性能。但为什么还是很难做出跟 Play-station 3 一样的图形处理,我们有必要想一下。这里的差距就是内存的带宽。在手机环境有很多要求是跟desktop一样的,但是内存带宽或电池,发热是不一样的。如果带宽有瓶颈,最终会让渲染速度变慢,电池的消耗也会上升。
我们祈祷 HBM 技术可以适用到手机环境里吧。

手游内存芯片的带宽已经接近 PC 主内存的带宽了。但是手机用的总线宽度通常是比 PC的 128 bit 小的 64 bit。
手机上是没有高性能 PC 图形系统为了渲染保持整体帧缓冲器的 ‘VRAM’ 缓冲器。

而且电池的消耗也是一个很大的拖后腿因素。
DDR 带宽消耗在游戏运行的电池使用量里占很大分量。
想提高手机图形的关键是减少 DDR 带宽消耗。

GPU里有一个叫 GMEM 的专用高速内存缓冲器。 渲染表面用 “Bin (bins)”分开。 Bin的大小是形式 (包含Z) 和渲染对象的分辨率用总 GMEM 除去的值来决定的。 储存器的数量根据硬件版本,对象分辨率的大小和对象显示的形式都不同。
每一个 Bin 生成三角形 “可视流”。Rendering Pass 会利用可视流把该 Bin 上可看的像素拿过来 (GPU 周期和电池的节约)。
各个 Bin 的所有像素都画出来了, 和 GPU功能一致的很高带宽的 GMEM里 GPU有效率地进行 Bust ,所有混合的像素在 GMEM 用单一的 Layer用于系统内存的帧缓冲器 (叫 “解决”)。 避开解决方法是重点。

次时代游戏的节约内存带宽的方法有很多种。
通常会有贴图的压缩, OpenGL 3.1 AEP 的重要功能 Tessellation 技法也是其中一个。RTC 这个是用于 ‘under-the-covers’的压缩技法。 纯粹的带宽 (不是内存空间) 压缩。让显示器理解这个格式,带宽会储存在帧缓冲器的写和显示器里。

跟美术有直接关联的是贴图的压缩,这也是最重要的部分。随着游戏的图形提高,一个Shader里也会用好几张贴图。以前连 PC 游戏也是手绘风贴图为主,但是现在的手游图形不仅是 PBR ,视觉表现上也需要大量的贴图。

结果就是,比起 GPU 的发展速度,手机的内存带宽还没有跟上 PC 或主机游戏硬件的发展速度。所以在手机的硬件环境下,美术要完全掌握节约内存带宽的方法,亲自修改贴图的密度和压缩格式,执行即在视觉上追求稳定,也能减少内存的方案。压缩贴图必然会发生画质的下降。
所以美术要尽量利用 UV 空间,多想和多测试怎么制作图形数据。

MASSIVLY FITTING UNPACKED UV。
利用 HOUDINI 的 MULTI COVERAGE UVLAYOUT WORKS。

MASSIVLY FITTING UNPACKED UV。
概念上的 MULTI COVERAGE UVLAYOUT WORKS。
设计造型时,原画最好树立重复图案(Tiling) 结构。

MIP-MAP ARTIST DRIVEN CONTROL
手机里的所有 Shader map 都需要 Mip-map 吗? 我们有必要像这种问题。
Mip-map 的有或无容量上有 1.35 倍的差距。
关掉 Mip-map 可以节省百分之 35 内存。

美术要对所有贴图进行视觉判断,进行把控。
下图有相当于 5.2 倍的容量差距。
但在手机上显示的话基本没有差距。

原理上, MIP-MAP 是把原贴图进行 Scale Down ,预先生成的技术。
视觉上在小画面上,以游戏的标准镜头距离为准使用跟 MIP-MAP 等级类似的 Scale Down 阶段的尺寸也不会有很大问题。
但是 MIP-MAP 阶段的 Pixel sampling count 减少的好处是得不到了。
但是先稳定带宽跟 ‘Modern GPU’ 的 pixel sampling 高速处理能力一起想就可以找到共识。

conclusion

即将到来的 2019年为起点会成为 OPENGL-ES 3.1 AEP 的时代,都使用高性能硬件和高配渲染 API的情况下每个公司的项目图像品质还不一样或者性能上有差距的话,是因为在不了解 GPU 的情况下进行开发的结果。

通过这次的分享,我们美术要多思考,开发次时代手游时为了做出更高的品质,更快的渲染性能要检查什么部分,进行怎样的优化,最终要获得什么结果。

如果自己做出的数据有压缩成低画质或强制被最小化的话,那都是因为自己的无知引起的,不能怪谁。

并且程序员要尽量减少 CPU BOUND ,集中想方案怎样才能把独立的 GPU 利用到极致。

HOW TO SETTING UP TO MATERIALS LOD.

MATERIALS LOD FOR INGAME PLAY.
Organization / Netease Pangu Studio Technical Art Team.
Author / JP.Lee

Go Github

Commit schedule : 2016-9-26.

To be honest, this is already four years ago.
Most recently I do not use this method.
Because many easier methods have already been discovered.

老实说,这已经是四年前了。
最近我不使用这种方法。
因为已经发现了许多更简单的方法。

Overview
What is this have purpose of requirement.
In the game, according to the change of the quality template set by the user, the calculation quality of the Shader used in the game, the type of Shader, etc. will be automatically changed. The change materials has decided to use Shader LOD and Material Quality Phase Set (based on the quality setting, it will be replaced with a predefined Material).
The advantages are relatively simple to achieve overall.
Disadvantages are fine-thinking about the management of Material.

游戏中,根据用户设置的品质模板的改变,游戏中使用的Shader的运算品质,Shader的种类等,都会自动变更。变更要素已经决定使用Shader LOD和Material Quality Phase Set(根据品质设定,会更换成预先定义的Material)。
优点整体实现较为简单。
缺点美术得对Matrial的管理费点心思。


Shader structure of QUALITY PHASE.

Depending on the distance, you can use shaders optimized for each quality. # Performance issues # Metal roughness values have a performance difference of about 25% When using variables instead of textures. You need to divide the quality level and the LOD level, set the rules and use the variable parameters.
随着距离的不同,可以使用根据各品质别设置的灯光模型的优化Shader。#性能问题#Metallic Roughness值上面使用贴图和使用变量参数,性能上有25%左右的差别。我们要划分好品质等级和LOD等级,定好规则,使用变量参数。

美术材质球品质设置脚本

This major feature is artist they only when you open the Edit Mode, you can preview the changes in the edit state without clicking gameplay.
只有打开Edit Mode的时候,即使不点击gameplay,也能在编辑状态上预览到变更结果。

using UnityEngine;
using System.Collections;
[System.Serializable]
[ExecuteInEditMode]
public enum EditableMaterialsList
{
    LOD0,
    LOD1,
    LOD2
};

[ExecuteInEditMode]
public class ArtistMaterialSetLOD : MonoBehaviour
{
    public bool editMode = true;
    public Material mat1;
    public Material mat2;
    public Material mat3;
    public EditableMaterialsList editableMaterialsList = EditableMaterialsList.LOD0;
    
    Renderer rend;
    void Start()
    {
     
    }
#if UNITY_EDITOR
    void UpdateMatLOD()
    {
        rend = GetComponent<Renderer>();
        if (UnityEditor.EditorApplication.isPlaying)
        {
            editMode = false;
        }
        else editMode = true;

        if (!editMode)
        {
            return;
        }
        else
        {
            switch (editableMaterialsList)
            {
                case EditableMaterialsList.LOD0:
                    rend.material = mat1;
                    break;
                case EditableMaterialsList.LOD1:
                    rend.material = mat2;
                    break;
                case EditableMaterialsList.LOD2:
                    rend.material = mat3;
                    break;
                default:
                    break;
            }
        }
     
    }


    
    void Update()
    {
        UpdateMatLOD();
    }
#endif

}

测试用Material LOD script.

The distance is divided into 3 levels in total, using 3 LOD materials. The shader for the sample code has to be set manually.
距离总共分为3档,使用3个LOD material。样本代码的材质球得手动设置。

using UnityEngine;
using System.Collections;

[System.Serializable]
[ExecuteInEditMode]
public class SwitchMaterialSwapLOD : MonoBehaviour
{


    public bool shadowLOD = true;
    public float[] matLodDistance = { 60f, 20f, 0f };
    public Material[] lodMat;
    Renderer rend;
    public float updataTime = 0.25f;


    void Start()
    {
        rend = GetComponent<Renderer>();
        StartCoroutine("UpdateMatLOD");
    }



    IEnumerator UpdateMatLOD()
    {
        var period = updataTime;
        var cam = Camera.main.transform;
        rend.sharedMaterial = lodMat[0];
        while (true)
        {
            var distance = (transform.position - cam.position).sqrMagnitude;
            Debug.Log(distance);

            if (distance > matLodDistance[0])
            {
                rend.sharedMaterial = lodMat[2];
            }
            else if (distance > matLodDistance[1])
            {
                rend.sharedMaterial = lodMat[1];
            }
            else if (distance > matLodDistance[2])
            {
                rend.sharedMaterial = lodMat[0];
            }
            else rend.sharedMaterial = lodMat[0];


            if (shadowLOD)
            {
                if (distance > matLodDistance[1])
                {
                    this.GetComponent<SkinnedMeshRenderer>().receiveShadows = false;
                }
                else this.GetComponent<SkinnedMeshRenderer>().receiveShadows = true;
            }

            // make LOD checking non deterministic. avoids stampeding LOD calls?
            yield return new WaitForSeconds(period);
        }
    }
}


细部构成概要
3 shaders-shader name _LOD_0.
-Shader name _LOD_1.
-Shader name_LOD_2.
Shader of each file.

3档材质球-材质球名_LOD_0
-材质球名_LOD_1
-材质球名_LOD_2
各个档的Shader.
-shaderName LOD 0 ( LOD 300 , LOD 200 , LOD 100 )
:Full shader.
-shaderName LOD 1 ( LOD 300 , LOD 200 , LOD 100 )
: Down data precision ( float to half ).- roughness texture fetch to constant value.
-shaderName LOD 2 ( LOD 300 , LOD 200 , LOD 100 )
:Down data precision ( half to fixed )- roughness with metallic texture fetch to constant value.- Optional normal map enabler.

Only Test Phase of Images below

LOD 0

LOD 1

LOD 2

Mesh LOD with Material LOD.

#Mesh LOD的话,可以以Mech renderer单位来登录LOD。

%d 블로거가 이것을 좋아합니다: