引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES & tools

之前骨骼动画的IK暂时放一放, 最近在搞GLES的实现. 之前除了GLES没有实现, Android的代码移植已经完毕:

[原]跨平台编程注意事项(三): window 到 android 的 移植

总的来说上次移植的改动不是很大, 主要是DLL与.so之间的调整和适配, 还有些C++标准相关的编译错误. 数据包的加载/初始化/配置文件和插件的加载测试可用了, 但GLES没有实现, 所以上次的移植只能在真机上空跑.

最近想在业余时间抽空把GLES的空白填上, 目前接口调整差不多了, GLES runtime正在填实现.

1.先简单说下Tile Based Rendering GPU的原理和注意事项

  • TBR方式会将屏幕空间划分为若干个Tile, 每个tile比屏幕小, 比如32x32.
  • TBR会把几何数据在屏幕空间划分到每个tile, 然后对每个Tile进行渲染, 几何数据可能是跨很多tile的, 所以需要一直保存, 而且drawcall的几何数据越多, 耗费的内存越大.
  • TBR的架构, GPU内部有针对Tile的快速内存(fast memory, 暂时先叫tile cache吧), 访问速度很快. 但是video memory一般不是卡载的物理显存, 而是使用系统主存,  video memory 到cache的传输相对比较慢.
  • 由于Tile Cache的存在, 去读写depth 和color 都很快.这和现代PC的GPU不同. TBR的blending, depth write/test, multisample都不慢. 对于深度不同的像素, 即使重复着色, 也只是在Tile Cache上进行, 最终一次写入到video memory.
  • 由于Tile Cache到video memory很慢, 所以GLES提供了InvalidateFrameBuffer的hint, 对于这种架构, 可以避免cache和memory之间的额外传输.
  • 如果GPU有hidden surface removal 特性(PVR GPU), GPU会去排序这个几何数据, pixel负载小很多. 所以app在绘制的时候, solid物体不需要按距离排序, 但是discard/texkill 会导致其特性失效. 对于失效的情况, 或者没有该特性的GPU, 仍然可以利用early z: 使用传统方式的pre-z pass先写深度.
  • Tile Based GPU的几何负载(三角形数量)相对要比现代PC的GPU要低很多. 现代PC几百万的三角形是小意思, 但是Tile based需要保存这些几何数据, 用于各个tile的渲染, 内存和运行开销都比较大.

2.GLES和D3D接口统一

渲染接口基本类似, 有等价的实现, 主要在shader接口:

GL/ES是运行时link program, 他的shader是中间对象.D3D一般是离线编译然后运行时直接载入.

GLES3有glGetProgramBinary和glProgramBinary, 可以保存和加载编译后的shader.但是编译和保存仍然要在target device上做.

Blade之前的接口是IShader => D3D9VertexShader : has a IDirect3DVertexShader9

=> D3D9FragementShader : has a IDirect3DPixelShader9

现在的接口把shader类型合并, 不再有不同类型的的shader 对象, 而是一个shader包含了vs和fs等对象

IShader => D3D9Shader : has a (IDirect3DVertexShader9 & IDirect3DPixelShader9 )

=> GLESShader : has a gl program

同时IRenderDevice:: setShader( EShaderType, HSHADER&  ) 改为setShader(HSAHDER&)

GLSL/ES的shader, 所有的uniform和vertex input stream(vertex attribute) 都没有semantic. 需要用户自己根据名字来绑定和设置.

对于uniform, 因为Blade的shader resouce 会额外的保存一个semantic map, 用于更新引擎内置的变量, 比如WORLD_MATRIX, EYE_POS等等, 所以uniform的绑定和更新没有问题.

而对于vertex atribute, 现在的做法是, 把这些变量使用固定的名字替换. 比如 HLSL中的POSITION0, 对应的GLSL, 其变量名字叫做blade_position0.

这样就可以在运行时glBindAttributeLocation, 绑定到VBO上.

3.工具

打包工具BPK已经有了,runtime也在android上测试可用. 目前需要的工具有: shader compiler, texture compressor.

shader compiler使用的是HLSL2GLSL:

先说下windows下现有的shader compiler:

offline:

HLSL ==(TexShaderSerializer::load) ==>  D3DSoftwareShader : compiled binary == (BinarySerializer::save) ==> binary shader : with semantic map

runtime:

binary shader ==(BinarySerializer::load)==> D3DShader

GLES下已经做的shader compiler:

offline:

HLSL ==(TexShaderSerializer::load) ==> D3DSoftwareShader : compiled binary with HLSL text ==(replace with GLSL)==>

binary with GLSL text ==(HybridShaderSerializer::save)==> hybird shader : text with binary semnatic map

runtime:

hybird shader ==(HybridShaderSerializer::load)==> GLESShader

对于GLES3.0, 可以在启动时将shader(program)保存为binary(只保存一次), 这样shader以后不用再编译, 加载速度会快很多.这个以后也会做.

(https://software.intel.com/en-us/articles/opengl-es-30-precompiled-shaders)

GLES2的扩展有glShaderBinary, 不过是保存链接前的shader,  而不是链接后的program.

starting up precompile: once and for all

hybird shader ==(HybridShaderSerializer::load)==> GLESShader ==(BinaryShaderSerializer::save) ==> binary shader

runtime:

binary shader ==(BinaryShaderSerilizer::load) ==> GLESShader

需要记录的是IShader是渲染设备/API相关的接口, 其接口抽象位于foundation library, 实现在另一个DLL/so. 而ShaderResource和所有的ShaderSerializer是可复用的,平台无关的. 整个Graphics Subsystem是平台无关的, 具体平台相关的优化(比如Tile Based)需要用渲染配置文件(这个文件的范例.xml以前记录过)来做, 还有Blade::IRenderDevice内部的implementation来做针对的处理.

shader compiler因为用了三方库, 所以目前做完了, 可以转换为GLSL ES 3.0,  等runtime填充玩, 有了压缩纹理格式就可以测试了.

texture compressor是把纹理压缩成目标平台使用的格式, 这里Blade准备用的是ETC2/EAC. 之前blade在windows上是实时压缩, 因为看到国外有的引擎这么做, 主要优点是用png保存在磁盘节约磁盘空间,png的压缩比要比S3TC高. 但是使用中发现,对于大贴图, 加载稍微有点慢, 而且对于移动端, 在线压缩也不是好方法,这个之前提到过. 以后的方案改为先离线压缩好贴图, 所有平台统一使用这种预压缩方式.

texture compresstor的话, 最近工作太忙, 没有太多业余时间. 可能也没时间去手写, 会用三方库来做压缩. 目前还没做, 后面会做. 还要做的是, 梳理目标平台数据生成/打包流程. 即综合shader compiler, texture compressor, BPK packager, 一次性生成最终数据的build/project script.

其他的游戏数据, 已经设计成跨平台的, 理论上也应该是跨平台, 不需要做任何额外处理. blade现有的x86和x64用的都是相同的数据或者BPK数据包. 但是android上面可能需要调试.

最后, HLSL之前的uniform semantic解析, 是放在文件的注释里面的:

 1 //!BladeShaderHeader
 2  2 //![VertexShader]
 3  3 //!Entry=TerrainVSMain
 4  4 //!Profile=vs_3_0
 5  5 //![FragmentShader]
 6  6 //!Entry=TerrainPSMain
 7  7 //!Profile=ps_3_0
 8  8
 9 #include "inc/light.hlsl"
10 #include "inc/common.hlsl"
11 #include "inc/terrain_common.hlsl"
12
13 //![Semantics]
14 //!wvp_matrix = WORLD_VIEWPROJ_MATRIX
15 //!world_translate = WORLD_POSITION
16
17
18 void TerrainVSMain(
19     float2 hpos        : POSITION0,
20     float2 vpos        : POSITION1,
21     float4 normal    : NORMAL0,        //ubyte4-n normal
22
23     uniform float4x4 wvp_matrix,
24     uniform float4 world_translate,
25     uniform float4 scaleFactor,        //scale
26     uniform float4 UVInfo,            //uv information
27
28     out    float4 outPos : POSITION,
29     out    float4 outUV  : TEXCOORD0,
30     out float4 outBlendUV : TEXCOORD1,
31     out float3 outWorldPos : TEXCOORD2,
32     out float3 outWorldNormal : TEXCOORD3
33     )
34 {
35     float4 pos = float4(hpos.x, getMorphHeight(vpos, hpos+world_translate.xz, eye_position.xz), hpos.y, 1);
36     pos = pos*scaleFactor;
37
38     float blendOffset = UVInfo[0];
39     float tileSize = UVInfo[1];
40     float blockSize = UVInfo[2];
41     float blockUVMultiple = UVInfo[3];
42
43     //normalUV
44     outUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + 0.5/tileSize;
45     //block repeat UV
46     outUV.zw = pos.xz*blockUVMultiple/blockSize;
47     //blendUV
48     outBlendUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + blendOffset/tileSize;
49     outBlendUV.zw = pos.xz/tileSize;
50
51     //use local normal as world normal, because our terrain has no scale/rotations
52     outWorldNormal = expand_vector(normal).xyz;    //ubytes4 normal ranges 0-1, need convert to [-1,1]
53
54     //don‘t use full transform because our terrain has no scale/rotation
55     outWorldPos = pos.xyz+world_translate.xyz;
56
57     outPos = mul(pos, wvp_matrix);
58 }

现在去掉了注释中的声明, 改成了HLSL的格式. 之前因为D3D的Effect才支持解析uniform的semantic, 所以误以为, 这种格式只有.FX才支持, 如果直接用D3DCompile会报错.

但是前几天试了一下, D3DCompile不会对unform的semantic报错, 只是直接忽略掉它了. 所以全部改成这种格式.

需要稍微加点代码手动解析semantic, 用tokenizer就可以了.

 1 //!BladeShaderHeader
 2 //![Shader]
 3 //!VSEntry=TerrainVSMain
 4 //!VSProfile=vs_3_0
 5 //!FSEntry=TerrainPSMain
 6 //!FSProfile=ps_3_0
 7
 8 #include "inc/light.hlsl"
 9 #include "inc/common.hlsl"
10 #include "inc/terrain_common.hlsl"
11
12
13 void TerrainVSMain(
14     float2 hpos        : POSITION0,
15     float2 vpos        : POSITION1,
16     float4 normal    : NORMAL0,        //ubyte4-n normal
17
18     uniform float4x4 wvp_matrix : WORLD_VIEWPROJ_MATRIX,
19     uniform float4 world_translate : WORLD_POSITION,
20     uniform float4 scaleFactor : _SHADER_,        //per shader custom variable: scale
21     uniform float4 UVInfo : _SHADER_,            //per shader custom variable: uv information
22
23     out    float4 outPos : POSITION,
24     out    float4 outUV  : TEXCOORD0,
25     out float4 outBlendUV : TEXCOORD1,
26     out float3 outWorldPos : TEXCOORD2,
27     out float3 outWorldNormal : TEXCOORD3
28     )
29 {
30     float4 pos = float4(hpos.x, getMorphHeight(vpos, hpos+world_translate.xz, eye_position.xz), hpos.y, 1);
31     pos = pos*scaleFactor;
32
33     float blendOffset = UVInfo[0];
34     float tileSize = UVInfo[1];
35     float blockSize = UVInfo[2];
36     float blockUVMultiple = UVInfo[3];
37
38     //normalUV
39     outUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + 0.5/tileSize;
40     //block repeat UV
41     outUV.zw = pos.xz*blockUVMultiple/blockSize;
42     //blendUV
43     outBlendUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + blendOffset/tileSize;
44     outBlendUV.zw = pos.xz/tileSize;
45
46     //use local normal as world normal, because our terrain has no rotations
47     outWorldNormal = expand_vector(normal).xyz;    //ubytes4 normal ranges 0-1, need convert to [-1,1]
48
49     //don‘t use full transform because our terrain has no scale/rotation
50     outWorldPos = pos.xyz+world_translate.xyz;
51
52     outPos = mul(pos, wvp_matrix);
53 }

关于shader变量, WORLD_VIEWPORJ_MATRIX是blade的FX framework内置的变量, 而"_SHADER_"这个semantic, 仅仅是表示这个变量是模块自定义的shader变量, framework没有内置, 用户模块(如例子中的地形模块)需要根据变量名字, 直接设置/更新该变量. 至少需要设置一次, 如果没有变化, 就不需要再更新它的值. 这个变量的CPU数据是由material/FX framework 自动根据变量类型分配的内存, 保留在shader/instance/global shader constant table里面.

后面有空了做ETC2/EAC的纹理压缩. 目前移植相对来说工作量不大, 可能适配和优化会花时间. 主要还是平台无关的core feature都不完善, 以后会集中做这些, 否则移植了意义也不是很大. 只要core feature和游戏代码有了, 即使出了新平台应该也能很快适配. 当然游戏的工程量跟引擎不是一个数量级, 希望以后有机会可以跟人合作.

时间: 2024-10-10 16:52:16

引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES & tools的相关文章

引擎设计跟踪(九.14) 更新记录和骨骼动画导出

骨骼动画是去年打算写的部分, 但是中间因为工作太忙, 已经拖了一年了. 期间也加了其他东西, 比如对UI做了部分完善.UI对toolbar button添加了drop down 支持, 一种是dropdown menu, 一种是dropdown property sheet 实现这些控件不难, 但是要做抽象和复用, 接口设计稍微有点复杂. 现在可以把一个IConfig对象绑定到toolbar的button里了. 这样保存这些配置的时候,直接使用IConfig接口就可以了.贴一个编辑器的配置文件,

引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复

由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪(九) 3DS MAX 导出插件 引擎设计跟踪(九.10) Max插件更新,地形问题备忘 这里说明一下修复方法, 并且做一个总结. 之前的做法都不算错, 但是不完善. 这里有缝, 主要是因为那个战争机器3的模型本身已经复制了顶点( 左半部分和右半部分是不同的mesh, 有重合的顶点), 接缝处的顶点虽

引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014

Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写过一篇shader跨平台的文章, 开始提到了并有链接. 1.手写或者宏替换 使用宏定义将 HLSL & GLSL 的不同之处封装, 并让每个开发人员了解他们的不同之处. 例子: Valve的Source 2引擎 优点: 简单,容易实现缺点: 每个开发者都必须熟悉使用宏定义库, 还有其他语法上的不同.

引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏

之前milestone2已经做完的工作, 现在趁有时间记下笔记. 1.设计 这里是指兼容3ds max导出/fbx格式转换等等一系列工作的设计. 最开始, Blade的3dsmax导出插件, 全部代码都是写在导出的DLL里面的, 后来考虑到FBX等等其他格式, 现在把模块分成两部分: Model/Anim Collector: 预定义的接口, 用于收集其他模型的相关数据. 用户负责扩展实现, 比如FBXCollector, MaxCollector, 或者其他格式. Model/Anim Bui

引擎设计跟踪(九.14.2 final) Inverse Kinematics: CCD 在Blade中的应用

因为工作忙, 好久没有记笔记了, 但是有时候发现还得翻以前的笔记去看, 所以还是尽量记下来备忘. 关于IK, 读了一些paper, 觉得之前翻译的那篇, welman的paper (http://graphics.ucsd.edu/courses/cse169_w04/welman.pdf  摘译:http://www.cnblogs.com/crazii/p/4662199.html) 非常有用, 入门必读. 入门了以后就可以结合工程来拓展了. 先贴一下CCD里面一个关节的分析: 当Pic的方

引擎设计跟踪(九.14.2b) 骨骼动画基本完成

首先贴一个介绍max的sdk和骨骼动画的文章, 虽然很早的文章, 但是很有用, 感谢前辈们的贡献: 3Ds MAX骨骼动画导出插件编写 1.Dual Quaternion 关于Dual Quaternion, 这里不做太详细的介绍了,贴出来几个链接吧: http://en.wikipedia.org/wiki/Dual_quaternion http://www.seas.upenn.edu/~ladislav/kavan08geometric/kavan08geometric.pdf http

引擎设计跟踪(九.14.2i) Android GLES 3.0 完善

最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVertexBuffer.都是体力活, 根据文档(https://www.khronos.org/opengles/sdk/docs/man3/)填上对应的API就可了.遇到的问题纪录在下面: Stick to the standard C++standard并没有要求char必须是unsignedtype

引擎设计跟踪(九.14.2g) 将GNUMake集成到Visual Studio

最近在做纹理压缩工具, 以及数据包的生成. shader编译已经在vs工程里面了, 使用custom build tool, build命令是调用BladeShaderComplier, 并且每个文件对应一个输出, vs会自动检查工程里面文件的依赖, 这样很方便. 纹理压缩如果也要放在visual studio里面, 可以用build event或者custom build step来做, 但是build dependency很难处理, 比如每个原始贴图对应一张目标贴图, 如果像编译shader

引擎设计跟踪(九.14.3.2) Deferred shading的后续实现和优化

最近完成了deferred shading和spot light的支持, 并作了一部分优化. 之前forward shading也只支持方向光, 现在也支持了点光源和探照光. 对于forward shading, 可以在渲染每个对象之前, 用对象的包围盒, 查询空间内的光源, 然后填入shader cosntant里. 因为空间一般是基于四叉树或者八叉树的划分, 所以查询不会慢.现在透明物体也能通过forward shading 正常光照了. Deferred shading optimizat